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

网友投稿 220 2022-11-06


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

Spring只支持XML方式而没有实现注解的方式(也叫AspectJ方式)的AOP,所以要使用@Aspect注解,只能引入AspectJ相关的 jar 包 aopalliance-1.0.jar 和 aspectjweaver.jar,这个坑把我给坑惨了。

使用步骤如下:

1、引入相关jar包

2、Spring的配置文件 applicationContext.xml 中引入context、aop对应的命名空间;配置自动扫描的包,同时使切面类中相关方法中的注解生效,需自动地为匹配到的方法所在的类生成代理对象。

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:aop="http://springframework.org/schema/aop"

xmlns:context="http://springframework.org/schema/context"

xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans.xsd

http://springframework.org/schema/aop http://springframework.org/schema/aop/spring-aop-4.0.xsd

http://springframework.org/schema/context http://springframework.org/schema/context/spring-context-4.0.xsd">

xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:aop="http://springframework.org/schema/aop"

xmlns:context="http://springframework.org/schema/context"

xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans.xsd

http://springframework.org/schema/aop http://springframework.org/schema/aop/spring-aop-4.0.xsd

http://springframework.org/schema/context http://springframework.org/schema/context/spring-context-4.0.xsd">

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

* @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 , ElementTyphttp://e.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;

}

}

配置文件:

xmlns:xsi="http://w3.org/2001/XMLSchema-instance" xmlns:util="http://springframework.org/schema/util"

xmlns:p="http://springframework.org/schema/p" xmlns:aop="http://springframework.org/schema/aop"

xmlns:tx="http://springframework.org/schema/tx" xmlns:context="http://springframework.org/schema/context"

xsi:schemaLocation="

http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans-4.3.xsd

http://springframework.org/schema/util http://springframework.org/schema/util/spring-util-4.3.xsd

http://springframework.org/schema/tx http://springframework.org/schema/tx/spring-tx-4.3.xsd

http://springframework.org/schema/aop http://springframework.org/schema/aop/spring-aop-4.3.xsd

http://springframework.org/schema/context

http://springframework.org/schema/context/spring-context-4.3.xsd">

xmlns:xsi="http://w3.org/2001/XMLSchema-instance" xmlns:util="http://springframework.org/schema/util"

xmlns:p="http://springframework.org/schema/p" xmlns:aop="http://springframework.org/schema/aop"

xmlns:tx="http://springframework.org/schema/tx" xmlns:context="http://springframework.org/schema/context"

xsi:schemaLocation="

http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans-4.3.xsd

http://springframework.org/schema/util http://springframework.org/schema/util/spring-util-4.3.xsd

http://springframework.org/schema/tx http://springframework.org/schema/tx/spring-tx-4.3.xsd

http://springframework.org/schema/aop http://springframework.org/schema/aop/spring-aop-4.3.xsd

http://springframework.org/schema/context

http://springframework.org/schema/context/spring-context-4.3.xsd">

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

}

}

所需要的包:


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:话费查询API(话费查询app下载)
下一篇:使用nginx搭建简易的文件共享服务器
相关文章

 发表评论

暂时没有评论,来抢沙发吧~