聊聊注解@Aspect的AOP實(shí)現(xiàn)操作
Spring只支持XML方式而沒有實(shí)現(xiàn)注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相關(guān)的 jar 包 aopalliance-1.0.jar 和 aspectjweaver.jar,這個(gè)坑把我給坑慘了。
使用步驟如下:
1、引入相關(guān)jar包
2、Spring的配置文件 applicationContext.xml 中引入context、aop對應(yīng)的命名空間;配置自動掃描的包,同時(shí)使切面類中相關(guān)方法中的注解生效,需自動地為匹配到的方法所在的類生成代理對象。
<?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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 配置自動掃描的包 --> <context:component-scan base-package="com.qcc.beans.aop"></context:component-scan> <!-- 自動為切面方法中匹配的方法所在的類生成代理對象。 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
3、創(chuàng)建簡單計(jì)算器的接口ArithmeticCalculator.java及實(shí)現(xiàn)類ArithmeticCalculatorImpl.java
package com.qcc.beans.aop; public interface ArithmeticCalculator { int add(int i, int j); int sub(int i, int j); int mul(int i, int j); int div(int i, int j); }
package com.qcc.beans.aop; import org.springframework.stereotype.Component; //將實(shí)現(xiàn)類加入Spring的IOC容器進(jìn)行管理 @Component("arithmeticCalculator") public class ArithmeticCalculatorImpl implements ArithmeticCalculator { @Override public int add(int i, int j) { int result = i + j; return result; } @Override public int sub(int i, int j) { int result = i - j; return result; } @Override public int mul(int i, int j) { int result = i * j; return result; } @Override public int div(int i, int j) { int result = i / j; return result; } }
4、現(xiàn)在想在實(shí)現(xiàn)類中的每個(gè)方法執(zhí)行前、后、以及是否發(fā)生異常等信息打印出來,需要把日志信息抽取出來,寫到對應(yīng)的切面的類中 LoggingAspect.java 中
要想把一個(gè)類變成切面類,需要兩步,
① 在類上使用 @Component 注解 把切面類加入到IOC容器中
② 在類上使用 @Aspect 注解 使之成為切面類
下面直接上完整代碼,用@Aspect注解方式來實(shí)現(xiàn)前置通知、返回通知、后置通知、異常通知、環(huán)繞通知。
package com.qcc.beans.aop; import java.util.Arrays; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; 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.springframework.stereotype.Component; /** * 日志切面 * * @author QianChaoChen 00002336<br> * @date 2017年3月3日 下午3:03:29 */ @Component @Aspect public class LoggingAspect { /** * 前置通知:目標(biāo)方法執(zhí)行之前執(zhí)行以下方法體的內(nèi)容 * @param jp */ @Before("execution(* com.qcc.beans.aop.*.*(..))") public void beforeMethod(JoinPoint jp){ String methodName = jp.getSignature().getName(); System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs())); } /** * 返回通知:目標(biāo)方法正常執(zhí)行完畢時(shí)執(zhí)行以下代碼 * @param jp * @param result */ @AfterReturning(value="execution(* com.qcc.beans.aop.*.*(..))",returning="result") public void afterReturningMethod(JoinPoint jp, Object result){ String methodName = jp.getSignature().getName(); System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】"); } /** * 后置通知:目標(biāo)方法執(zhí)行之后執(zhí)行以下方法體的內(nèi)容,不管是否發(fā)生異常。 * @param jp */ @After("execution(* com.qcc.beans.aop.*.*(..))") public void afterMethod(JoinPoint jp){ System.out.println("【后置通知】this is a afterMethod advice..."); } /** * 異常通知:目標(biāo)方法發(fā)生異常的時(shí)候執(zhí)行以下代碼 */ @AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e") public void afterThorwingMethod(JoinPoint jp, NullPointerException e){ String methodName = jp.getSignature().getName(); System.out.println("【異常通知】the method 【" + methodName + "】 occurs exception: " + e); } // /** // * 環(huán)繞通知:目標(biāo)方法執(zhí)行前后分別執(zhí)行一些代碼,發(fā)生異常的時(shí)候執(zhí)行另外一些代碼 // * @return // */ // @Around(value="execution(* com.qcc.beans.aop.*.*(..))") // public Object aroundMethod(ProceedingJoinPoint jp){ // String methodName = jp.getSignature().getName(); // Object result = null; // try { // System.out.println("【環(huán)繞通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs())); // //執(zhí)行目標(biāo)方法 // result = jp.proceed(); // System.out.println("【環(huán)繞通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result); // } catch (Throwable e) { // System.out.println("【環(huán)繞通知中的--->異常通知】:the method 【" + methodName + "】 occurs exception " + e); // } // // System.out.println("【環(huán)繞通知中的--->后置通知】:-----------------end.----------------------"); // return result; // } }
5、編寫Main方法進(jìn)行測試
package com.qcc.beans.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); ArithmeticCalculator arithmeticCalculator = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator"); System.out.println(arithmeticCalculator.getClass()); int result = arithmeticCalculator.add(3, 5); System.out.println("result: " + result); result = arithmeticCalculator.div(5, 0); System.out.println("result: " + result); } }
運(yùn)行結(jié)果:
class com.sun.proxy.$Proxy10 【前置通知】the method 【add】 begins with [3, 5] 【后置通知】this is a afterMethod advice... 【返回通知】the method 【add】 ends with 【8】 result: 8 【前置通知】the method 【div】 begins with [5, 0] 【后置通知】this is a afterMethod advice... Exception in thread "main" java.lang.ArithmeticException: / by zero at com.qcc.beans.aop.ArithmeticCalculatorImpl.div(ArithmeticCalculatorImpl.java:28) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy10.div(Unknown Source) at com.qcc.beans.aop.Main.main(Main.java:15)
把其它代碼都注釋掉,把環(huán)繞通知的方法釋放出來,測試結(jié)果如下:
【環(huán)繞通知中的--->前置通知】:the method 【add】 begins with [3, 5] 【環(huán)繞通知中的--->返回通知】:the method 【add】 ends with 8 【環(huán)繞通知中的--->后置通知】:-----------------end.---------------------- result: 8 【環(huán)繞通知中的--->前置通知】:the method 【div】 begins with [5, 0] 【環(huán)繞通知中的--->異常通知】:the method 【div】 occurs exception java.lang.ArithmeticException: / by zero 【環(huán)繞通知中的--->后置通知】:-----------------end.---------------------- Exception in thread "main" org.springframework.aop.AopInvocationException: Null return value from advice does not match primitive return type for: public abstract int com.qcc.beans.aop.ArithmeticCalculator.div(int,int) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:219) at com.sun.proxy.$Proxy7.div(Unknown Source) at com.qcc.beans.aop.Main.main(Main.java:15)
從以上發(fā)現(xiàn),返回通知和異常通知不會同時(shí)出現(xiàn);不管是否發(fā)生異常,后置通知都會正常打印。
補(bǔ)充:基于@Aspect實(shí)現(xiàn)AOP的兩種方式
第一種:
package com.anno; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) //什么時(shí)候運(yùn)行和結(jié)束 @Target( {ElementType.METHOD , ElementType.TYPE}) //能放在哪里. public @interface AnnotationTwo { } package com.anno; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; @Aspect @Component @EnableAspectJAutoProxy public class AspectAnnotationTOW { @Pointcut("@annotation(AnnotationTwo)") public void aspectTow(){ } @Around("aspectTow()") public void aspectservice(ProceedingJoinPoint joinPoint){ System.out.println(111); try { Object proceed = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } } }
第二種:
package com.testAnnotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) //什么時(shí)候運(yùn)行和結(jié)束 @Target( {ElementType.METHOD , ElementType.TYPE}) //能放在哪里. public @interface AnnotationOfTest { public String say(); public long howLong() default Long.MAX_VALUE; } package com.testAnnotation; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component; import com.rabbitmq.tools.jsonrpc.ProcedureDescription; @Component @EnableAspectJAutoProxy @Aspect public class AspectAnnotation { @Around("@annotation(annotationOfTest)") public Object interceptor(ProceedingJoinPoint pjp, AnnotationOfTest annotationOfTest ) { String say = annotationOfTest.say(); long howlong = annotationOfTest.howLong(); try { System.out.println(say+"\t"+howlong); Object object = pjp.proceed(); return object; } catch (Throwable e) { e.printStackTrace(); } return null; } }
配置文件:
<?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:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd"> <context:component-scan base-package="com.testAnnotation"></context:component-scan> <context:component-scan base-package="com.service"></context:component-scan> <context:component-scan base-package="com.anno"></context:component-scan> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> </beans> service: package com.service; import java.util.Date; import org.springframework.stereotype.Service; import com.anno.AnnotationTwo; import com.testAnnotation.AnnotationOfTest; @Service public class service { @AnnotationTwo public void go() { System.out.println(new Date().toLocaleString()); } }
項(xiàng)目結(jié)構(gòu):
測試:
package com.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.service.service; import com.testAnnotation.AnnotationOfTest; public class Test { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); service bean = applicationContext.getBean(service.class); bean.go(); } }
所需要的包:
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
springboot實(shí)現(xiàn)token驗(yàn)證登陸狀態(tài)的示例代碼
本文主要介紹了spring?boot?實(shí)現(xiàn)token驗(yàn)證登陸狀態(tài),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07java文件刪除不了File類的delete方法刪不掉文件的原因以及分析
這篇文章主要介紹了java文件刪除不了File類的delete方法刪不掉文件的原因以及分析,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06mybatisplus?@Select注解中拼寫動態(tài)sql異常問題的解決
這篇文章主要介紹了mybatisplus?@Select注解中拼寫動態(tài)sql異常問題的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12SpringBoot工程打包后執(zhí)行Java?-Jar就能啟動的步驟原理
這篇文章主要介紹了SpringBoot工程打包后為何執(zhí)行Java?-Jar就能啟動,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05Java如何通過反射獲取私有構(gòu)造、私有對象、私有字段、私有方法
這篇文章主要介紹了Java如何通過反射獲取私有構(gòu)造、私有對象、私有字段、私有方法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12MybatisPlus如何自定義TypeHandler映射JSON類型為List
這篇文章主要介紹了MybatisPlus如何自定義TypeHandler映射JSON類型為List,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01MySQL查詢字段實(shí)現(xiàn)字符串分割split功能的示例代碼
本文主要介紹了MySQL查詢字段實(shí)現(xiàn)字符串分割split功能的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01利用Java中Calendar計(jì)算兩個(gè)日期之間的天數(shù)和周數(shù)
Java 語言的Calendar(日歷),Date(日期),和DateFormat(日期格式)組成了Java標(biāo)準(zhǔn)的一個(gè)基本但是非常重要的部分。日期是商業(yè)邏輯計(jì)算一個(gè)關(guān)鍵的部分。下面這篇文章就給大家介紹了如何利用Java中Calendar計(jì)算兩個(gè)日期之間的天數(shù)和周數(shù),下面來一起看看吧。2016-12-12