Spring中使用AspectJ 注解的AOP
在Spring 2.0之后,可以使用基于AspectJ 注解 或XMl 配置的AOP,这篇文章主要记录一下AspectJ 注解的AOP的实现
1、前提
- 使用 AspectJ 注解 需要在Spring项目中添加 aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar 三个jar包。
- 要在 Spring IOC 容器中启用 AspectJ 注解支持, 只要在 Bean 配置文件中将 aop Schema 添加到 <beans> 根元素中,定义一个空的 XML 元素 <aop:aspectj-autoproxy />。
- 当 Spring IOC 容器侦测到 Bean 配置文件中的 <aop:aspectj-autoproxy/> 元素时, 会自动为与 AspectJ 切面匹配的 Bean 创建代理。
2、用AspectJ注解声明切面
要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例. 当在 Spring IOC 容器中初始化 AspectJ 切面之后, Spring IOC 容器就会为那些与 AspectJ 切面相匹配的 Bean 创建代理.
在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类.
AspectJ 支持 5 种类型的通知注解:
- @Before: 前置通知, 在方法执行之前执行
- @After: 后置通知, 在方法执行之后执行
- @AfterRunning: 返回通知, 在方法返回结果之后执行
- @AfterThrowing: 异常通知, 在方法抛出异常之后
- @Around: 环绕通知, 围绕着方法执行
3、代码实现
首先,创建一个接口 并 创建一个类实现这个接口
interface Calculator
public interface Calculator { int add(int j, int i); int sub(int j, int i); int mul(int j, int i); int div(int j, int i); }
实现类 CalculatorImpl
@Service("calculator") public class CalculatorImpl implements Calculator { @Override public int add(int j, int i) { int result = j + i; return result; } @Override public int sub(int j, int i) { int result = j - i; return result; } @Override public int mul(int j, int i) { int result = j * i; return result; } @Override public int div(int j, int i) { int result = j / i; return result; } }
Bean的配置文件
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="com.spring.demo2"/> <aop:aspectj-autoproxy/> </beans>
切面类 AopTest
// 添加切面的两步,1、将切面类加入Spring Ioc容器中;2、添加 @Aspect注解 @Aspect // 标志这个类是一个切面 @Component public class AopTest { // 定义一个切入点表达式,一般的不需要添加其他代码,其他通知方法使用时,只需要引用这个方法的 ‘名字()’ @Pointcut("execution(public int com.spring.demo2.Calculator.*(int, int))") public void aopExpression(){} // 前置通知 @Before("execution(public int com.spring.demo2.Calculator.*(int, int))") //标识这个方法是个前置通知, 切点表达式表示执行 Calculator接口的所有方法 . * 代表匹配任意修饰符及任意返回值,任意方法, 参数列表中的 .. 匹配任意数量的参数 public void beforePrint(JoinPoint joinPoint){ String methodName = joinPoint.getSignature().getName(); List<Object> args = Arrays.asList(joinPoint.getArgs()); System.out.println("The Method " + methodName + " is start with " + args); } // 后置通知,方法结束后,不管是否发生异常都通知 // 切入点表达式可以通过操作符 &&, ||, ! 结合起来. @After("execution(public int com.spring.demo2.Calculator.add(int, int)) || execution(public int com.spring.demo2.Calculator.sub(int, int))") public void afterMethod(JoinPoint joinPoint) { String name = joinPoint.getSignature().getName(); System.out.println("The Method " + name + " END "); } // 返回通知,可以返回方法的返回值,方法正常结束才执行的代码 @AfterReturning(value="aopExpression()", returning="result") public void afterReturning(JoinPoint joinPoint, Object result){ String name = joinPoint.getSignature().getName(); System.out.println("The method " + name + " ends with " + result ); } // 异常通知 @AfterThrowing(value="aopExpression()", throwing = "ex") public void afterThrow(JoinPoint joinPoint, Exception ex){ String name = joinPoint.getSignature().getName(); System.out.println("The Method " + name + " occurs exception " + ex ); } // 环绕通知需要携带 ProceedingJoinPoint 类型的参数 // 环绕通知类似于动态代理的全过程:ProceedingJoint 类型的参数可以决定是否执行目标方法 // 且环绕通知必须有返回值,返回值即为目标方法的返回值。 // Order 指定切面的优先级,值越小优先级越大 @Order(1) @Around("aopExpression()") public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable { Object result = null; String methodName = joinPoint.getSignature().getName(); // 执行目标方法 try { // 前置通知 System.out.println("The method " + methodName + " begins with " + Arrays.asList(joinPoint.getArgs())); result = joinPoint.proceed(); // 返回通知 System.out.println("The method " + methodName + " end with " + result); } catch (Throwable throwable) { System.out.println("The method " + methodName + "throw " + throwable); throw new Throwable("出错了"); } System.out.println("The method " + methodName + " end with " + result ); return result; } }
5大通知的细节
-
前置通知
前置通知:在方法执行之前执行的通知
前置通知使用 @Before 注解, 并将切入点表达式的值作为注解值.
-
后置通知
后置通知是在连接点完成之后执行的, 即连接点返回结果或者抛出异常的时候, 下面的后置通知记录了方法的终止。一个切面可以包括一个或者多个通知.无论连接点是正常返回还是抛出异常, 后置通知都会执行.
-
返回通知
在返回通知中, 只要将 returning 属性添加到 @AfterReturning 注解中, 就可以访问连接点的返回值. 该属性的值即为用来传入返回值的参数名称.
必须在通知方法的签名中添加一个同名参数. 在运行时, Spring AOP 会通过这个参数传递返回值.
-
异常通知
只在连接点抛出异常时才执行异常通知将 throwing 属性添加到 @AfterThrowing 注解中, 也可以访问连接点抛出的异常. Throwable 是所有错误和异常类的超类. 所以在异常通知方法可以捕获到任何错误和异常.
如果只对某种特殊的异常类型感兴趣, 可以将参数声明为其他异常的参数类型. 然后通知就只在抛出这个类型及其子类的异常时才被执行.
-
环绕通知
环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是否执行连接点.
对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点.
在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.
注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常
@Order 注解指定.
实现 Ordered 接口, getOrder() 方法的返回值越小, 优先级越高.
若使用 @Order 注解, 序号出现在注解中
- spring 4.0 AOP (使用AspectJ的注解方式 的aop实现)简单实例
- Spring使用AspectJ进行AOP的开发:注解方式
- Spring系列之 (十):AOP实现方式(二):使用AspectJ的注解方式
- Spring5.0.4 AOP 使用AspectJ方式自动代理注解(@EnableAspectJAutoProxy)
- Spring使用AspectJ注解和XML配置实现AOP
- Spring AOP的使用 基于全注解AspectJ
- spring 4.0 AOP (使用AspectJ的注解方式 的aop实现)简单实例
- Spring使用Aspectj注解的方式来配置AOP
- spring之基于aspectj注解aop使用
- (转)Spring使用AspectJ进行AOP的开发:注解方式
- Spring AOP使用配置介绍(四):基于@AspectJ注解的aop
- 让spring3.0可以使用aop注解的方法是导入两个jar包aspectjrt.jar aspectjweaver.jar
- Spring AOP之AspectJ的注解方式使用
- Spring AOP使用配置介绍(四):基于@AspectJ注解的aop
- 小白第七篇 Spring AOP编程 使用@AspectJ注解的方式
- 请教:使用spring2.0 @AspectJ注解风格进行AOP编程时遇到问题:传递参数到Advice失败。...
- spring 4.0 AOP (使用AspectJ的注解方式 的aop实现)简单实例
- spring框架的学习(五)——Spring使用AspectJ进行AOP的开发:注解方式
- 基于注解的Spring AOP的配置和使用
- 使用Spring的注解方式实现AOP