spring之AOP(转)
Spring之AOP篇:
AOP框架是Spring的一個重要組成部分.但是Spring IOC 并不依賴于AOP,這就意味著你有權力選擇是否使用AOP,AOP作為Spring IOC容器的一個補充,使它成為一個強大的中間件解決方案。
一、AOP(Aspect-Oriented Programming)
??? AOP的目標:橫切關注點? 這種問題用面向對象很難解決
? AOP是用來解決什么問題的呢?
???? 橫越多個模塊的業務關注點,比如記錄日志,事務管理
?1、AOP的解決方案——攔截
?? 1)由誰來攔截?--代理類Proxy 和本類實現同一個接口
????????? 就像是這樣:action發出命令--代理類(處理一些操作)--本類
?? 2)攔截的過程:
?????? I.將Proxy(如UserServiceProxy)交給調用者(如UserAction),調用者調用需要的方法
?????? II.Proxy先攔截該方法,執行攔截邏輯
?????? III.然后執行調用者真正的實現類(如UserServiceImp)
?? 3)核心概念:
???? a、代理對象Proxy:是核心,負責攔截,由工具自動創建
???? b、攔截器(Interceptor):實現攔截邏輯的對象
???? c、目標對象(Target):是Proxy所代理的對象,是已有的類
? 以上三個類實現了一個“金三角”
???? Proxy--攔截-->Interceptor--實現攔截邏輯--處理目標對象-->Target
?2、代理如何實現?
????? 有兩種方式:
??????? 1)JDK動態代理(使用這個比較多)
??????????? 由JDK自帶的動態代碼生成技術,可以對實現了接口的類進行處理
??????? 2)CGLib
???? 對沒有實現任何接口的類進行處理
?這兩種方式的共同點:(都是在后臺自動生成的)
??在程序運行期間,動態的生成代碼并進行動態編譯和加載
?????
??? 問題:Spring的AOP是如何實現代理的?是自己實現的嗎?
??????? 注:Spring借用了JDK動態代理和CGLib來實現代理對象。
???? Spring進行如下判斷來實現代理:
??i、如果目標類實現了接口,Spring則調用JDK動態代理;
??ii、反之,調用CGLib
?????
????? Proxy是核心:
????? 創建Proxy的方法:ProxyFactoryBean
?<bean id="arithProxy"
???????? class="org.springframework.aop.framework.ProxyFactoryBean">
?????? <property name="target" ref="arith"/>
?????? <property name="interceptorNames">
?????? <list>
??????? <!--<value>logBefore</value>
?????????? <value>logAfter</value>
?????????? <value>logThrows</value> -->
?????? <value>logAround</value>
????? </list>
???? </property>
????? JDK動態代理的實現方式:(工廠模式)
??UserDao userDao = new UserDaoImp();
??UserDao proxy = (UserDao)
??Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
????userDao.getClass().getInterfaces(),
????new LogHandler(userDao));
二、Spring中的AOP方式(在Spring中將攔截稱為通知)
?? 攔截邏輯放在Advice之中,Advice按時間將攔截器分為四類:
????? 1)Before Advice:方法執行之前進行攔截
??? public class LogBeforeAdvice implements MethodBeforeAdvice{
?//Method method攔截的方法
?//Object[] args 方法中的參數
?//target 目標對象
?@Override
?public void before
?(Method method, Object[] args, Object target)
???throws Throwable {
??MyLogger logger = new MyLogger();
??System.out.println("Methods:"+method.getName()+
????"? begins, args:"+Arrays.toString(args));
?}
}
??? 2)After Advice:方法執行之后進行攔截
? public class LogAfterAdvice implements AfterReturningAdvice{
?@Override
?public void afterReturning(Object resultValue, Method method, Object[] args,
???Object target) throws Throwable {
??MyLogger logger = new MyLogger();
??System.out.println("Methods:"+method.getName()+
????"? ends, result:"+resultValue);
?}
}
?? 3)AfterThrowing Advice:方法異常結束之后進行攔截
? public class LogThrowingAdvice implements ThrowsAdvice {
?
?//ThrowsAdvice 沒有方法,但方法名必須是afterThrowing
?public void afterThrowing(IllegalArgumentException e)
?throws Throwable{
??MyLogger logger = new MyLogger();
??logger.log("IllegalArgumentException!");
?}
}
?? 4)Around Advice:環繞通知
? public class LogAroundAdvice implements MethodInterceptor {
?//MethodInvocation相當于Method的包裝
?@Override
?public Object invoke(MethodInvocation methodInvocation)
?throws Throwable {
??MyLogger logger = new MyLogger();
??try {
???//methodInvocation.getMethod()獲得方法
???//methodInvocation.getArguments()獲得方法的參數
???logger.log("before:"+
?????methodInvocation.getMethod().getName()+
?????", args:"+methodInvocation.getArguments());
???
???//在環繞通知里面,必須執行目標方法!!!!!!!!!!!!!!
???Object result = methodInvocation.proceed();
???logger.log("ends:"+
?????methodInvocation.getMethod().getName());
???return result;
??} catch (Exception e) {
???logger.log("Exception:"+e.getMessage());
???throw e;
??}
?}
? 注意:雖然環繞通知包含了另外三種,但還是要依據業務邏輯來選擇,這樣有利于代碼的編程量
?????? 并且環繞通知最好不要和另外三種混用,并且并許執行目標方法proceed().
?也可以大致分成兩組——
?i、BeforeAdvice, AfterReturning, Throwing
?ii、AroundAdvice:需要在內部負責調用目標類的方法。
????? 注意:AroundAdvice應單獨使用
<!-- Target Object -->
<bean id="arith" class="myspring.calculator.ArithmeticCalculatorImp"></bean>
<!-- Advice -->
<bean id="logBefore" class="myspring.aop.LogBeforeAdvice"/>
<bean id="logAfter" class="myspring.aop.LogAfterAdvice"/>
<bean id="logThrows" class="myspring.aop.LogThrowingAdvice"/>
<bean id="logAround" class="myspring.aop.LogAroundAdvice"/>
<!-- Proxy -->
<bean id="arithProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="arith"/>
<property name="interceptorNames">
? <list>
??? <!-- <value>logBefore</value>
??? <value>logAfter</value>
??? <value>logThrows</value> -->
??? <value>logAround</value>
? </list>
</property>
</bean>
在test類中這樣用:ApplicationContext ac =
???new ClassPathXmlApplicationContext("aop-base.xml");
??IArithmeticCalculator proxy =
???(IArithmeticCalculator)ac.getBean("arithProxy");
?????
?面試中可能會問到:
????????? proxyTargetClass屬性
??將其加入到代理bean中去,則Spring將調用CGLib來實現創建Proxy對象。
?????????????????? (無論Target有無實現接口)
????? 形如
???? <bean id="" class="....ProxyFactoryBean">
??? .....
?????? <property name="proxyTargetClass" value="true" />
???? </bean>
?三、Spring AOP基本方式? AspectJ Spring2.5之后用的
Spring對AspectJ的支持
????? 應用于有多個target,不想一個一個的配置時,用AspectJ?????
1)POJO類(Aspect類),使用@Aspect?
??基本信息:Advice和Pointcut??
2)定義Bean:在Bean定義文件中定義這個Aspect類?
3)啟用Spring的自動代理
??<aop:aspectj-autoproxy />??
? ?1、JoinPoint(連接點):被攔截的方法
??????????????? 程序執行過程中的點,就好像方法中的一個調用,
??或者一個特別的被拋出的異常
?????????????? 在Spring AOP中,一個連接點通常是方法調用.
???Spring并不明顯地使用"連接點"作為術語,
??連接點信息可以通過MathodInvocation的參數穿過攔截器訪問,
??相當于實現org.springframework.aop.Pointcut接口.
?2、JoinPoint對象
??獲取連接點信息(獲取目標方法以及目標對象的信息)?
?1)目標方法:
??i、getSignature():方法簽名
????joinPoint.getSignature().getName()
??
??ii、getArgs():方法的實參(方法的實際參數)
????????????????? joinPoint.getArgs()
??打印輸出時:Arrays.toString(joinPoint.getArgs())
?2)目標對象:? getTarget()
????目標對象實際類型:getTarget().getClass()
?3、PointCut(切入點):一組連接點。
??????????????? 作用:定義了一組被攔截的方法?
??PointCut(切入點):當一個通知被激活的時候,
??會指定一些連接點.一個AOP框架必須允許開發者指定切入點。
??一個AOP框架必須允許開發者指定切入點:例如使用正則表達式.
??只有引入了切入點,才能很好的進行攔截
??PointCut的意義:可以一次性定義多個類的
????多個方法作為攔截目標(即JoinPoint)
??PointCut代表了Target 一組Target組成了PointCut
??一組Target可以理解為一個類的多個方法,或者多個類的多個方法
??代理、攔截器、目標
??PointCut
???在AspectJ中,切入點是用PointCut Expression來
???定義的。(切入點表達式可以理解為過濾條件)
???
???PointCut表達式
????格式? execution(表達式)
????表達式? 一個表達式就是一個匹配規則
?????* myspring.calculator.IArithmeticCalculator.add(..)
??*理解為public void myspring.calculator.IArithmeticCalculator.add(int a,int b)
??* 取代public void 通配一切字符???
???Spring自動代理(即Spring自動生成Proxy對象)
????用<aop:aspectj-autoproxy />來啟用
?Pointcut表達式語法
?1)方法級別? —— execution
??execution(* *.*(..)) :所有類的方法
??execution(public * somepackage.SomeClass.someMethod(..))
??總結:execution是對方法簽名進行匹配
?2)對象級別(類級別)—— within
?within(somepackage.*):somepackage包下所有的類
?within(com.dang.service.*Service)
?即 com.dang.service包下所有以Service結尾的類
?within(somepackage..*):somepackage以及所有子包中的類
?within(somepackage.SomeInterface+):SomeInterface接口的所有實現類
?總結:within是對類名(帶包名)進行匹配。?攔截這個類的所有方法?
4、Aspect類的組成? 一般都用注解 重點
??1)Advice(AspectJ中的通知類型)
???i、Before
??//@Before("LogPointcuts.logAdd()") 方法1
??????????? @Before("execution(* *.*(..))") 方法2
???????? public void logBefore(JoinPoint joinPoint){
???? logger.log("{before} the method: "
???? ??+joinPoint.getSignature().getName()+
???? ??"? args:"+Arrays.toString(joinPoint.getArgs()));?
?}
???????????? 注:方法1和方法2都是可以的,方法1的話需要在定義一個方法.
??在同一個類或不同的類都是可以的 直接調用這個方法
????????????????? @Pointcut("execution(* *.*(..))")
????????? public void logOperation(){}
???ii、AfterReturning
??@AfterReturning(pointcut="execution(* *.*(..))",
???returning="result")
??? public void afterReturning(JoinPoint joinPoint,Object result){
?? logger.log("{AfterReturning} the method: "
????? ??+joinPoint.getSignature().getName()
????? ??+" result:"+result);?
?}
? ??注意:方法有兩個參數,在注解中的returning的值必須和Object的值完全一樣
???iii、AfterThrowing
??@AfterThrowing(pointcut="execution(* *.*(..))",
???throwing="e")
??? public void afterThrowing(JoinPoint joinPoint,IllegalArgumentException e){
??logger.log("{AfterThrowing} the method: "
???? ??+joinPoint.getSignature().getName()
???? ??+" e:"+e.getMessage());
?}
???iv、After:相當于finally,它在AfterReturning
?????和AfterThrowing之后執行。
?????它的作用——一般為釋放資源(如關閉Connection)
??@After("execution(* *.*(..))")
?public void after(JoinPoint joinPoint){
??logger.log("{After} method:"+joinPoint.getSignature().getName());
?}
??注意:After是肯定是要在最后執行的,
???v、Around
??@Around("execution(* *.*(..))")
?public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable{
??logger.log("{Arount}Before method:"
????+joinPoint.getSignature().getName());
??try{
?????????????????????? //一定不要忘記調用目標方法
???Object result = joinPoint.proceed();
???
???logger.log("{Arount}After method:"
?????+joinPoint.getSignature().getName()
?????+" result:"+result);
???????? //返回目標方法的返回值
???return result;
??}
??catch(IllegalArgumentException e){
???logger.log("{Around}Exception:Illegal Argument"
?????+Arrays.toString(joinPoint.getArgs()));
???throw e;
??}
??注意:Around最好不要和其他四種同時使用
??把處理日志的代碼放到一起就是切面,模塊化(Aspect)
? 5.Introduction(引入)
?? 1)Introduction的定義
??給正在運行的程序中的對象添加方法。
???其實是一種動態技術
?? 2)問題:如何給ArithmeticCalculatorImp類加入
???求最大值和最小值的方法
?? 假設:最大值和最小值的方法已經存在,且分別屬于不同的類。
?extends MaxCalculatorImp, MinCalculatorImp 不行,java不允許多繼承
?? 3)要點:
?a、實現一個POJO類
??定義@DeclareParents來"繼承"的父類
???????? @DeclareParents(
??value="myspring.calculator.ArithmeticCalculatorImp",
??defaultImpl=MaxCalculatorImp.class
?)
?private MaxCalculator maxCalculator;
?
?@DeclareParents(
???value="myspring.calculator.ArithmeticCalculatorImp",
???defaultImpl=MinCalculatorImp.class
??)
??private MinCalculator minCalculator;???
?b、定義這個Bean:在Bean定義文件中聲明一下。
???????? <bean class="myspring.calculator.CalculatorIntroduction"></bean>
??? 在test類中測試時:ApplicationContext ac =
??new ClassPathXmlApplicationContext("aop.xml");
??
??IArithmeticCalculator cal = (IArithmeticCalculator)
??ac.getBean("arith");
??cal.add(2, 3);
??cal.div(4, 3);
??
??MaxCalculator max = (MaxCalculator)cal;
??max.max(3, 6);
??MinCalculator min = (MinCalculator)cal;
??min.min(3, 6);
?
spring之AOP
總結
以上是生活随笔為你收集整理的spring之AOP(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SpringJunit测试类 BaseT
- 下一篇: 事务相关报错记录