聊聊注解@Aspect的AOP实现操作
程序员文章站
2022-06-18 09:14:07
spring只支持xml方式而没有实现注解的方式(也叫aspectj方式)的aop,所以要使用@aspect注解,只能引入aspectj相关的 jar 包 aopalliance-1.0.jar 和...
spring只支持xml方式而没有实现注解的方式(也叫aspectj方式)的aop,所以要使用@aspect注解,只能引入aspectj相关的 jar 包 aopalliance-1.0.jar 和 aspectjweaver.jar,这个坑把我给坑惨了。
使用步骤如下:
1、引入相关jar包
2、spring的配置文件 applicationcontext.xml 中引入context、aop对应的命名空间;配置自动扫描的包,同时使切面类中相关方法中的注解生效,需自动地为匹配到的方法所在的类生成代理对象。
<?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、创建简单计算器的接口arithmeticcalculator.java及实现类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; //将实现类加入spring的ioc容器进行管理 @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、现在想在实现类中的每个方法执行前、后、以及是否发生异常等信息打印出来,需要把日志信息抽取出来,写到对应的切面的类中 loggingaspect.java 中
要想把一个类变成切面类,需要两步,
① 在类上使用 @component 注解 把切面类加入到ioc容器中
② 在类上使用 @aspect 注解 使之成为切面类
下面直接上完整代码,用@aspect注解方式来实现前置通知、返回通知、后置通知、异常通知、环绕通知。
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 { /** * 前置通知:目标方法执行之前执行以下方法体的内容 * @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())); } /** * 返回通知:目标方法正常执行完毕时执行以下代码 * @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 + "】"); } /** * 后置通知:目标方法执行之后执行以下方法体的内容,不管是否发生异常。 * @param jp */ @after("execution(* com.qcc.beans.aop.*.*(..))") public void aftermethod(joinpoint jp){ system.out.println("【后置通知】this is a aftermethod advice..."); } /** * 异常通知:目标方法发生异常的时候执行以下代码 */ @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); } // /** // * 环绕通知:目标方法执行前后分别执行一些代码,发生异常的时候执行另外一些代码 // * @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("【环绕通知中的--->前置通知】:the method 【" + methodname + "】 begins with " + arrays.aslist(jp.getargs())); // //执行目标方法 // result = jp.proceed(); // system.out.println("【环绕通知中的--->返回通知】:the method 【" + methodname + "】 ends with " + result); // } catch (throwable e) { // system.out.println("【环绕通知中的--->异常通知】:the method 【" + methodname + "】 occurs exception " + e); // } // // system.out.println("【环绕通知中的--->后置通知】:-----------------end.----------------------"); // return result; // } }
5、编写main方法进行测试
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); } }
运行结果:
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)
把其它代码都注释掉,把环绕通知的方法释放出来,测试结果如下:
【环绕通知中的--->前置通知】:the method 【add】 begins with [3, 5] 【环绕通知中的--->返回通知】:the method 【add】 ends with 8 【环绕通知中的--->后置通知】:-----------------end.---------------------- result: 8 【环绕通知中的--->前置通知】:the method 【div】 begins with [5, 0] 【环绕通知中的--->异常通知】:the method 【div】 occurs exception java.lang.arithmeticexception: / by zero 【环绕通知中的--->后置通知】:-----------------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)
从以上发现,返回通知和异常通知不会同时出现;不管是否发生异常,后置通知都会正常打印。
补充:基于@aspect实现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) //什么时候运行和结束 @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) //什么时候运行和结束 @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()); } }
项目结构:
测试:
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(); } }
所需要的包:
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。