您的位置:首页 > 编程语言 > ASP

Spring中使用AspectJ 注解的AOP

2019-10-11 15:35 1316 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_44339867/article/details/102502390

在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 注解, 序号出现在注解中

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: