spring AOP
本節要點:
- 掌握AOP概念
- 掌握AOP的有關術語
- 掌握spring AOP框架的實現方式
在文章“spring靜態代理和動態代理”中演示了如何使用jdk動態代理功能實現一個最簡單的AOP。使用代理對象將日誌記錄與業務邏輯無關的動作或任務提取出來,設計為一個服務類,這樣的類可以稱之為aspect(切面).
1 AOP定義
- u AOP把軟件系統分成兩部分:核心關註點和橫切關註點。所謂核心關註點,是業務處理的主要流程,也就是說這個解決方案要做的事。所謂橫切關註點,是與核心關註點無關的部分,常常發生在核心關註點的多處,而各處基本相似,如日誌 、權限等。
- u 將Cross-cutting concern設計為通用,不介入特定業務對象的一個職責清楚的Aspect對象,這就是Aspect-oriented programming,縮寫即AOP.
AOP並不會取代OOP,而是作為OOP的補充。
- u AOP主要實現的目的是針對業務處理過程中的切面進行提取,它所面對的是處理過程中的某個步驟或階段,以獲得邏輯過程中各部分之間低耦合性的隔離效果。
2 AOP有關術語
- u Advice (通知).
Aspect的具體實現稱之為Advice(處理).上例中DynaProxyHandler類中invoke方法就是Advice的一個具體實現.
- u Joinpoint(連接點)
Advice在應用程序中被執行的時機稱為Joinpoint(連接點),這個時機可能是某個方法被執行之前或之後(或兩者都有).
- u Pointcut(切入點)
一組連接點的集合,用於指定哪些Aspect在哪些Joinpoint中織入到應用程序之上.
- u Target(目標對象)
一個Advice被應用的目標對象.(被AOP框架進行增強處理的對象)
- u Weave(織入)
Advice被應用至目標對象之上的過程稱這為織入(Weave)
- u AOP代理(AOP Proxy):
由AOP框架創建的目標對象的代理對象。是被插入了advice的Target Object 。
- u 引入:
將方法者字段添加到被處理的類中。
術語圖解
3 spring AOP框架的實現方式
Spring中的AOP代理由Spring的Ioc容器負責生成、管理,其依賴關系也由Ioc容器負責管理。
AOP編程只需要程序員參加3個部分:
- u 定義普通業務組件
- u 定義切入點
- u 定義增強處理 (定義切面)
進行AOP的關鍵就是定義切入點和增強處理。一旦定義了合適的切入點和增強處理,AOP框架將會自動生成AOP代理。
通常采用AspectJ方式來定義切入點和增強處理,在這種方式下,Spring有如下兩種選擇來定義切入點和增強處理:
- 基於註解(Annotation)方式:[email protected]@Pointcut等Annotation來標註切入點和增強處理。
- 基於XML配置文件的管理方式:使用spring配置文件來定義切入點和增強處理
3.1 通過xml配置的方式實現
加入jar包: aopalliance-1.0.jar,aspectjweaver.jar
Log.java前置通知
public class Log implements MethodBeforeAdvice{ /** * @param method 被調用的方法對象,如addUser()方法 * @param args 被調用的方法的參數 * @param target 被調用的方法的目標對象 */ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("執行"+target.getClass().getName()+"的 "+method.getName()+"方法"); } }
afterLog.java後置通知
public class AfterLog implements AfterReturningAdvice{ /** * 目標方法執行後執行的通知 * returnValue 返回值 * method 被調用的方法對象 * args 被調用方法對象的參數 * target 被調用方法對象的目標對象 */ @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("after "+target.getClass().getName()+" 的 "+method.getName()+" 被執行"); } }
UserService
public interface UserService { public void getUser(); public void addUser(); public void delUser(String ss); public void updUser(); }
UserServiceImpl
public class UserServiceImpl implements UserService{ @Override public void getUser() { System.out.println("get user"); } @Override public void addUser() { System.out.println("add user"); } @Override public void delUser( String ss) { System.out.println("delete user="+ss); } @Override public void updUser() { System.out.println("update user"); } }
Beans.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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean name="userService" class="com.silvan.service.UserServiceImpl"></bean> <!-- 定義切面 --> <bean name="log" class="com.silvan.log.Log"></bean> <bean name="afterLog" class="com.silvan.log.AfterLog"/> <aop:config> <!-- aop:pointcut指定哪些對象的哪些方法訂閱切面, expression指定該切入點關聯的切入點表達式 id指定該切入點的標識符--> <aop:pointcut expression="execution(* com.silvan.service.*.*(..))" id="pointcut"/> <!--aop:advisor指定切面關聯哪個切入點 advice-ref指定切面 pointcut-ref指定切入點 --> <aop:advisor advice-ref="log" pointcut-ref="pointcut" /> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
Test
public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); UserService us = (UserService) ac.getBean("userService"); // us.delUser("zhaoliyin"); us.updUser(); } }
使用log4j輸出日誌:
加入jar包:log4j-1.2.14.jar
配置文件:log4j.properties
public class Log implements MethodBeforeAdvice{ private Logger logger = Logger.getLogger(Log.class) ; @Override public void before(Method method, Object[] args, Object target) throws Throwable { logger.info("執行"+target.getClass().getName()+"的 "+method.getName()+"方法"); } }
3.2 通過註釋的方式實現
修改配置文件
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:aspectj-autoproxy/> <bean name="userService" class="com.silvan.service.UserServiceImpl"></bean> <bean name="log" class="com.silvan.log.Log"></bean> </beans>
修改切面內容
import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class Log{ @Pointcut("execution(* com.silvan.service.*.*(..))") private void anyMethod(){}//定義一個切入點 @Before("anyMethod() && args(name)") public void before(String name){ System.out.println("前置通知 "+name); } @AfterReturning("anyMethod()") public void doAfter(){ System.out.println("後置通知"); } }
spring AOP