欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  互联网

聊聊注解@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包

聊聊注解@Aspect的AOP实现操作

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());
 } 
}

项目结构:

聊聊注解@Aspect的AOP实现操作

测试:

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();
 }
}

所需要的包:

聊聊注解@Aspect的AOP实现操作

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。