spring-註解---aopjava
package com.zwj.aop; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; /** * 切面類 * @author lfy * * @Aspect: 告訴Spring當前類是一個切面類 * */ @Aspect public class LogAspects { //抽取公共的切入點表達式 //一、本類引用 //二、其餘的切面引用 @Pointcut("execution(public int com.zwj.aop.MathCalculator.*(..))") public void pointCut(){}; //@Before在目標方法以前切入;切入點表達式(指定在哪一個方法切入) @Before("pointCut()") public void logStart(JoinPoint joinPoint){ Object[] args = joinPoint.getArgs(); System.out.println(""+joinPoint.getSignature().getName()+"運行。。。@Before:參數列表是:{"+Arrays.asList(args)+"}"); } @After("com.zwj.aop.LogAspects.pointCut()") public void logEnd(JoinPoint joinPoint){ System.out.println(""+joinPoint.getSignature().getName()+"結束。。。@After"); } //JoinPoint必定要出如今參數表的第一位 @AfterReturning(value="pointCut()",returning="result") public void logReturn(JoinPoint joinPoint,Object result){ System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:運行結果:{"+result+"}"); } @AfterThrowing(value="pointCut()",throwing="exception") public void logException(JoinPoint joinPoint,Exception exception){ System.out.println(""+joinPoint.getSignature().getName()+"異常。。。異常信息:{"+exception+"}"); } }
package com.zwj.aop; public class MathCalculator { public int div(int i,int j){ System.out.println("MathCalculator...div..."); return i/j; } }
package com.zwj.config; import org.aopalliance.aop.Advice; import org.aopalliance.intercept.MethodInterceptor; import org.springframework.aop.Advisor; import org.springframework.aop.Pointcut; import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import com.zwj.aop.LogAspects; import com.zwj.aop.MathCalculator; /** * AOP:【動態代理】 * 指在程序運行期間動態的將某段代碼切入到指定方法指定位置進行運行的編程方式; * * 一、導入aop模塊;Spring AOP:(spring-aspects) * 二、定義一個業務邏輯類(MathCalculator);在業務邏輯運行的時候將日誌進行打印(方法以前、方法運行結束、方法出現異常,xxx) * 三、定義一個日誌切面類(LogAspects):切面類裏面的方法須要動態感知MathCalculator.div運行到哪裏而後執行; * 通知方法: * 前置通知(@Before):logStart:在目標方法(div)運行以前運行 * 後置通知(@After):logEnd:在目標方法(div)運行結束以後運行(不管方法正常結束仍是異常結束) * 返回通知(@AfterReturning):logReturn:在目標方法(div)正常返回以後運行 * 異常通知(@AfterThrowing):logException:在目標方法(div)出現異常之後運行 * 環繞通知(@Around):動態代理,手動推動目標方法運行(joinPoint.procced()) * 四、給切面類的目標方法標註什麼時候何地運行(通知註解); * 五、將切面類和業務邏輯類(目標方法所在類)都加入到容器中; * 六、必須告訴Spring哪一個類是切面類(給切面類上加一個註解:@Aspect) * [7]、給配置類中加 @EnableAspectJAutoProxy 【開啓基於註解的aop模式】 * 在Spring中不少的 @EnableXXX; * * 三步: * 1)、將業務邏輯組件和切面類都加入到容器中;告訴Spring哪一個是切面類(@Aspect) * 2)、在切面類上的每個通知方法上標註通知註解,告訴Spring什麼時候何地運行(切入點表達式) * 3)、開啓基於註解的aop模式;@EnableAspectJAutoProxy * * AOP原理:【看給容器中註冊了什麼組件,這個組件何時工做,這個組件的功能是什麼?】 * @EnableAspectJAutoProxy; * 一、@EnableAspectJAutoProxy是什麼? * @Import(AspectJAutoProxyRegistrar.class):給容器中導入AspectJAutoProxyRegistrar * 利用AspectJAutoProxyRegistrar自定義給容器中註冊bean;BeanDefinetion * internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator * * 給容器中註冊一個AnnotationAwareAspectJAutoProxyCreator; * * 二、 AnnotationAwareAspectJAutoProxyCreator: * AnnotationAwareAspectJAutoProxyCreator * ->AspectJAwareAdvisorAutoProxyCreator * ->AbstractAdvisorAutoProxyCreator * ->AbstractAutoProxyCreator * implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware * 關注後置處理器(在bean初始化完成先後作事情)、自動裝配BeanFactory * * AbstractAutoProxyCreator.setBeanFactory() * AbstractAutoProxyCreator.有後置處理器的邏輯; * * AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory() * * AnnotationAwareAspectJAutoProxyCreator.initBeanFactory() * * * 流程: * 1)、傳入配置類,建立ioc容器 * 2)、註冊配置類,調用refresh()刷新容器; * 3)、registerBeanPostProcessors(beanFactory);註冊bean的後置處理器來方便攔截bean的建立; * 1)、先獲取ioc容器已經定義了的須要建立對象的全部BeanPostProcessor * 2)、給容器中加別的BeanPostProcessor * 3)、優先註冊實現了PriorityOrdered接口的BeanPostProcessor; * 4)、再給容器中註冊實現了Ordered接口的BeanPostProcessor; * 5)、註冊沒實現優先級接口的BeanPostProcessor; * 6)、註冊BeanPostProcessor,實際上就是建立BeanPostProcessor對象,保存在容器中; * 建立internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】 * 1)、建立Bean的實例 * 2)、populateBean;給bean的各類屬性賦值 * 3)、initializeBean:初始化bean; * 1)、invokeAwareMethods():處理Aware接口的方法回調 * 2)、applyBeanPostProcessorsBeforeInitialization():應用後置處理器的postProcessBeforeInitialization() * 3)、invokeInitMethods();執行自定義的初始化方法 * 4)、applyBeanPostProcessorsAfterInitialization();執行後置處理器的postProcessAfterInitialization(); * 4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)建立成功;--》aspectJAdvisorsBuilder * 7)、把BeanPostProcessor註冊到BeanFactory中; * beanFactory.addBeanPostProcessor(postProcessor); * =======以上是建立和註冊AnnotationAwareAspectJAutoProxyCreator的過程======== * * AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor * 4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工做;建立剩下的單實例bean * 1)、遍歷獲取容器中全部的Bean,依次建立對象getBean(beanName); * getBean->doGetBean()->getSingleton()-> * 2)、建立bean * 【AnnotationAwareAspectJAutoProxyCreator在全部bean建立以前會有一個攔截,InstantiationAwareBeanPostProcessor,會調用postProcessBeforeInstantiation()】 * 1)、先從緩存中獲取當前bean,若是能獲取到,說明bean是以前被建立過的,直接使用,不然再建立; * 只要建立好的Bean都會被緩存起來 * 2)、createBean();建立bean; * AnnotationAwareAspectJAutoProxyCreator 會在任何bean建立以前先嚐試返回bean的實例 * 【BeanPostProcessor是在Bean對象建立完成初始化先後調用的】 * 【InstantiationAwareBeanPostProcessor是在建立Bean實例以前先嚐試用後置處理器返回對象的】 * 1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation * 但願後置處理器在此能返回一個代理對象;若是能返回代理對象就使用,若是不能就繼續 * 1)、後置處理器先嚐試返回對象; * bean = applyBeanPostProcessorsBeforeInstantiation(): * 拿到全部後置處理器,若是是InstantiationAwareBeanPostProcessor; * 就執行postProcessBeforeInstantiation * if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } * * 2)、doCreateBean(beanName, mbdToUse, args);真正的去建立一個bean實例;和3.6流程同樣; * 3)、 * * * AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的做用: * 1)、每個bean建立以前,調用postProcessBeforeInstantiation(); * 關心MathCalculator和LogAspect的建立 * 1)、判斷當前bean是否在advisedBeans中(保存了全部須要加強bean) * 2)、判斷當前bean是不是基礎類型的Advice、Pointcut、Advisor、AopInfrastructureBean, * 或者是不是切面(@Aspect) * 3)、是否須要跳過 * 1)、獲取候選的加強器(切面裏面的通知方法)【List<Advisor> candidateAdvisors】 * 每個封裝的通知方法的加強器是 InstantiationModelAwarePointcutAdvisor; * 判斷每個加強器是不是 AspectJPointcutAdvisor 類型的;返回true * 2)、永遠返回false * * 2)、建立對象 * postProcessAfterInitialization; * return wrapIfNecessary(bean, beanName, cacheKey);//包裝若是須要的狀況下 * 1)、獲取當前bean的全部加強器(通知方法) Object[] specificInterceptors * 一、找到候選的全部的加強器(找哪些通知方法是須要切入當前bean方法的) * 二、獲取到能在bean使用的加強器。 * 三、給加強器排序 * 2)、保存當前bean在advisedBeans中; * 3)、若是當前bean須要加強,建立當前bean的代理對象; * 1)、獲取全部加強器(通知方法) * 2)、保存到proxyFactory * 3)、建立代理對象:Spring自動決定 * JdkDynamicAopProxy(config);jdk動態代理; * ObjenesisCglibAopProxy(config);cglib的動態代理; * 4)、給容器中返回當前組件使用cglib加強了的代理對象; * 5)、之後容器中獲取到的就是這個組件的代理對象,執行目標方法的時候,代理對象就會執行通知方法的流程; * * * 3)、目標方法執行 ; * 容器中保存了組件的代理對象(cglib加強後的對象),這個對象裏面保存了詳細信息(好比加強器,目標對象,xxx); * 1)、CglibAopProxy.intercept();攔截目標方法的執行 * 2)、根據ProxyFactory對象獲取將要執行的目標方法攔截器鏈; * List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); * 1)、List<Object> interceptorList保存全部攔截器 5 * 一個默認的ExposeInvocationInterceptor 和 4個加強器; * 2)、遍歷全部的加強器,將其轉爲Interceptor; * registry.getInterceptors(advisor); * 3)、將加強器轉爲List<MethodInterceptor>; * 若是是MethodInterceptor,直接加入到集合中 * 若是不是,使用AdvisorAdapter將加強器轉爲MethodInterceptor; * 轉換完成返回MethodInterceptor數組; * * 3)、若是沒有攔截器鏈,直接執行目標方法; * 攔截器鏈(每個通知方法又被包裝爲方法攔截器,利用MethodInterceptor機制) * 4)、若是有攔截器鏈,把須要執行的目標對象,目標方法, * 攔截器鏈等信息傳入建立一個 CglibMethodInvocation 對象, * 並調用 Object retVal = mi.proceed(); * 5)、攔截器鏈的觸發過程; * 1)、若是沒有攔截器執行執行目標方法,或者攔截器的索引和攔截器數組-1大小同樣(指定到了最後一個攔截器)執行目標方法; * 2)、鏈式獲取每個攔截器,攔截器執行invoke方法,每個攔截器等待下一個攔截器執行完成返回之後再來執行; * 攔截器鏈的機制,保證通知方法與目標方法的執行順序; * * 總結: * 1)、 @EnableAspectJAutoProxy 開啓AOP功能 * 2)、 @EnableAspectJAutoProxy 會給容器中註冊一個組件 AnnotationAwareAspectJAutoProxyCreator * 3)、AnnotationAwareAspectJAutoProxyCreator是一個後置處理器; * 4)、容器的建立流程: * 1)、registerBeanPostProcessors()註冊後置處理器;建立AnnotationAwareAspectJAutoProxyCreator對象 * 2)、finishBeanFactoryInitialization()初始化剩下的單實例bean * 1)、建立業務邏輯組件和切面組件 * 2)、AnnotationAwareAspectJAutoProxyCreator攔截組件的建立過程 * 3)、組件建立完以後,判斷組件是否須要加強 * 是:切面的通知方法,包裝成加強器(Advisor);給業務邏輯組件建立一個代理對象(cglib); * 5)、執行目標方法: * 1)、代理對象執行目標方法 * 2)、CglibAopProxy.intercept(); * 1)、獲得目標方法的攔截器鏈(加強器包裝成攔截器MethodInterceptor) * 2)、利用攔截器的鏈式機制,依次進入每個攔截器進行執行; * 3)、效果: * 正常執行:前置通知-》目標方法-》後置通知-》返回通知 * 出現異常:前置通知-》目標方法-》後置通知-》異常通知 * * * */ @EnableAspectJAutoProxy @Configuration public class MainConfigOfAOP { //業務邏輯類加入容器中 @Bean public MathCalculator calculator(){ return new MathCalculator(); } //切面類加入到容器中 @Bean public LogAspects logAspects(){ return new LogAspects(); } }
package com.zwj.test; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.zwj.aop.MathCalculator; import com.zwj.config.MainConfigOfAOP; public class IOCTest_AOP { @Test public void test01(){ AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class); //一、不要本身建立對象 // MathCalculator mathCalculator = new MathCalculator(); // mathCalculator.div(1, 1); MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class); mathCalculator.div(1, 0); applicationContext.close(); } }
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zwj</groupId> <artifactId>spring-annotation---AOP</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.3.12.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/javax.inject/javax.inject --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> <!-- https://mvnrepository.com/artifact/c3p0/c3p0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.44</version> </dependency> </dependencies> </project>
div運行。。。@Before:參數列表是:{[1, 0]}
MathCalculator...div...
div結束。。。@After
div異常。。。異常信息:{java.lang.ArithmeticException: / by zero}
mysql