AOP的相關概念
這裏面的名詞解釋,可以結合上一章的內容(模擬Hibernate的事務處理和查看工資)來進行理解。
1. 切面
日誌、安全性、權限類以及事務類,總之和業務邏輯沒有關係的都可以做切面
2. 通知
切面中的方法。例如:事務中的beginTransaction和commit方法
3. 切入點
只有符合切入點,才能把通知和目標方法結合起來。例如:代理的判斷語句
if("admin".equals(this.privilege.getAccess())){
//調用目標類的目標方法
method.invoke(this.iSalaryManage, args);
}else{
System.out.println("您沒有權限");
}
4. 連接點
客戶端調用的方法
proxy.showSalary();//代理對象的代理方法
AOP做到了代碼的重用
springAOp切面編程的例子
結合上一節的“模擬Hibernate的事務處理”來進行拓展。因此,這裏只粘貼部分代碼。
Xml文件
使用AOP切面編程
1. 導入命名空間
2. 引入目標類
3. 定義切面類,過濾切入點。代理類的生成交給spring來完成
4. 設置通知方法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!--
使用AOP切面編程
1. 導入命名空間
2. 引入目標類
3. 定義切面類,過濾切入點
4. 設置通知方法
-->
<bean id="personDao" class="com.lzl.test.aop_proxy.PersonDaoImpl"></bean>
<bean id="transaction" class="com.lzl.test.aop_proxy.Transaction"></bean>
<aop:config>
<!-- 切面類
expression(切面表達式)
com.lzl.test.aop_proxy.personDaoImpl下面的所有方法
-->
<aop:pointcut expression="execution(* com.lzl.test.aop_proxy.PersonDaoImpl.*(..))" id="perform"/>
<!--
切面
-->
<aop:aspect ref="transaction">
<!-- 前置通知
* 在目標方法執行之前
-->
<aop:before method="beginTransaction" pointcut-ref="perform"/>
<!-- 後置通知
* 在目標方法執行之後執行
* 通過returning來獲取返回的結果
* 代理方法發生異常時,不執行該方法
-->
<aop:after-returning method="commit" pointcut-ref="perform" returning="val"></aop:after-returning>
<!--
最終通知
* 目標方法執行之後
* 無論是否發生異常,都會執行。類似於finally
* 常用於資源的關閉等操作
-->
<aop:after method="finallyMethod" pointcut-ref="perform"/>
<!--
異常通知
* 目標方法發生異常時,觸發該方法。
* throwing拋異常的參數
-->
<aop:after-throwing method="throwEx" throwing="ex"
pointcut-ref="perform"></aop:after-throwing>
<!-- 環繞通知
* 在目標方法執行之前執行
* 可以控制那些對象執行目標方法
-->
<aop:around pointcut-ref="perform" method="arroundAdvice"></aop:around>
</aop:aspect>
</aop:config>
</beans>
切入點類
public class Transaction {
/**
* 前置通知
* @param joinPoint
*/
public void beginTransaction(JoinPoint joinPoint){
joinPoint.getArgs(); //獲取方法參數
String methodName = joinPoint.getSignature().getName();
System.out.println("=begin transaction...="+methodName);
}
/**
* 後置通知
* @param val
*/
public void commit(Object val){
//List<Person> list = (List<Person>) val;
//System.out.println("=commit...="+list.size());
System.out.println("=commit...=");
}
/**
* 最終通知
*/
public void finallyMethod(){
System.out.println("finally Method...");
}
/**
* 異常通知
* @param ex
*/
public void throwEx(Throwable ex){
System.out.println(ex.getMessage());
}
public void arroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
String methodName = pjp.getSignature().getName();
if("savePerson".equals(methodName)){
pjp.proceed();
}
}
}
測試類
public class PersonTest extends SpringHelper{
static{
path="com/lzl/test/aop_proxy/applicationContext.xml";
}
@Test
public void test(){
PersonDao personDao = (PersonDao) fileResource.getBean("personDao");
personDao.selectPerson();
}
}
說明
如果目標類實現了接口,spring就用JDKproxy代理,如果沒有實現接口,spring就使用cglib代理。
用SpringAOP重構查看工資的代碼
代碼的實現參照上一節。這裏只粘貼修改的代碼
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<bean id="salaryManage" class="com.lzl.test.spring.salary.SalaryManageImpl"></bean>
<bean id="logger" class="com.lzl.test.spring.salary.Logger"></bean>
<bean id="privilege" class="com.lzl.test.spring.salary.Privilege"></bean>
<bean id="security" class="com.lzl.test.spring.salary.Security"></bean>
<!--
一個切入點有多個切面
通知方法執行的順序,按照其配置的先後順序來執行的。
-->
<aop:config>
<!-- 定義的目標類及方法 -->
<aop:pointcut expression="execution(* com.lzl.test.spring.salary.SalaryManageImpl.*(..))" id="perform"/>
<!-- 切面 -->
<aop:aspect ref="logger">
<!-- 前置通知
日誌系統
-->
<aop:before method="startLogger" pointcut-ref="perform"/>
</aop:aspect>
<aop:aspect ref="security">
<!-- 前置通知
安全系統
-->
<aop:before method="startSecurity" pointcut-ref="perform"/>
</aop:aspect>
<aop:aspect ref="privilege">
<!-- 前置通知
權限驗證
-->
<aop:before method="openPrivilege" pointcut-ref="perform"/>
</aop:aspect>
</aop:config>
</beans>
測試類
public class SalaryTest extends SpringHelper{
static{
path="com/lzl/test/spring/salary/applicationContext.xml";
}
@Test
public void test(){
ISalaryManage sm = (ISalaryManage) fileResource.getBean("salaryManage");
sm.showSalary();
}
}
AOP註解
使用註解的方式實現AOP切面編程。(僅供瞭解)
這個例子還是MVC的那個例子。在PersonDaoImpl中添加了事務的處理。這裏只粘貼部分代碼。
XML文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!--
開啓註解解析器
-->
<context:component-scan base-package="com.lzl.test.annotation.mvc.aop"></context:component-scan>
<!-- 開啓AOP的註解 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
事務類(切面類)
@Component("transaction")
@Aspect //定義爲切面類
public class Transaction {
//切入點
@Pointcut("execution(* com.lzl.test.annotation.mvc.aop.PersonDaoImpl.*(..))")
private void aa(){} //方法簽名 返回值必須是void 方法的修飾符最好是private
@Before("aa()")
public void beginTransaction(){
System.out.println("=開啓事務...=");
}
@AfterReturning("aa()")
public void commit(){
System.out.println("=關閉事務..=");
}
}