版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zz210891470/article/details/54174169
- AspectJ:Java 社区里最完整最流行的 AOP 框架. spring aop 配合使用aspectj(AOP框架)实现我们所需的aop功能
-
在 Spring 中启用 AspectJ 注解支持 必须在 classpath 下包含 AspectJ 类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar
-
AspectJ 支持 5 种类型的通知注解:
- @Before: 前置通知, 在方法执行之前执行
- @After: 后置通知, 在方法执行之后执行
- @AfterRunning: 返回通知, 在方法成功执行返回结果之后执行
- @AfterThrowing: 异常通知, 在方法抛出异常之后
- @Around: 环绕通知, 围绕着方法执行
- 关于Spring AOP的AspectJ切点,最重要的一点就是Spring仅支持AspectJ切点指示器(pointcut designator)的一个子集。让我们回顾下,Spring是基于代理的,而某些切点表达式是与基于代理的AOP无关的。表4.1列出了Spring AOP所支持的AspectJ切点指示器。
-
基于JavaConfig配置实例 如下:
-
目录:
-
JavaConfig配置类: -
-
package aopbean;
-
import org.springframework.context.annotation.ComponentScan;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.context.annotation.EnableAspectJAutoProxy;
-
@Configuration
-
//开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false,
-
//即使用jdk默认代理模式,AspectJ代理模式是CGLIB代理模式
-
//如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
-
//如果目标对象实现了接口,可以强制使用CGLIB实现AOP (此例子我们就是强制使用cglib实现aop)
-
//如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
-
@EnableAspectJAutoProxy(proxyTargetClass=true)
-
@ComponentScan
-
public class JavaConfig {
-
}
-
-
-
package aopbean;
-
//目标接口
-
public interface Performance {
-
public void perform();
-
}
dance实现类: -
-
-
package aopbean;
-
import org.springframework.stereotype.Component;
-
@Component
-
public class Dance implements Performance {
-
public void perform() {
-
System.out.println("开始看跳舞");
-
}
-
}
切面类Audience: -
-
-
package aopbean;
-
import org.aspectj.lang.annotation.After;
-
import org.aspectj.lang.annotation.Aspect;
-
import org.aspectj.lang.annotation.Before;
-
import org.aspectj.lang.annotation.Pointcut;
-
import org.springframework.stereotype.Component;
-
@Aspect//声明切面,没有声明的话不会起作用
-
@Component
-
public class Audience {
-
public Audience(){
-
}
-
//声明切入点
-
//第一个*表示 方法 返回值(例如public int)
-
//第二个* 表示方法的全限定名(即包名+类名)
-
//perform表示目标方法参数括号两个.表示任意类型参数
-
//方法表达式以“*”号开始,表明了我们不关心方法返回值的类型。然后,我们指定了全限定类名和方法名。对于方法参数列表,
-
//我们使用两个点号(..)表明切点要选择任意的perform()方法,无论该方法的入参是什么
-
//execution表示执行的时候触发
-
@Pointcut("execution(* *.perform(..))")
-
public void dancepoint(){
-
//该方法就是一个标识方法,为pointcut提供一个依附的地方
-
}
-
@Before("dancepoint()")
-
public void beforeDance(){
-
System.out.println("找座位。。。。");
-
}
-
@After("dancepoint()")
-
public void afterDance(){
-
System.out.println("看完回家");
-
}
-
}
-
-
-
package aopbean;
-
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
-
public class Test {
-
public static void main(String[] args) {
-
AnnotationConfigApplicationContext ac =
-
new AnnotationConfigApplicationContext(JavaConfig.class);
-
Dance d = ac.getBean("dance",Dance.class);
-
d.perform();
-
}
-
}
输出结果:
找座位。。。。
开始看跳舞
看完回家 -
- 环绕通知 round
-
-
@Around("dancepoint()")
-
public void roudDance(ProceedingJoinPoint jp) throws Throwable{
-
System.out.println("找座位");
-
jp.proceed();
-
System.out.println("回家");
-
}
开始看跳舞
回家 -
-
可以看到,这个通知所达到的效果与之前的前置通知和后置通知是一样的。但是,现在它们位于同一个方法中,不像之前那样分散在四个不同的通知方法里面。
关于这个新的通知方法,你首先注意到的可能是它接受
ProceedingJoinPoint
作为参数。这个对象是必须要有的,因为你要在通知中通过它来调用被通知的方法。通知方法中可以做任何的事情,当要将控制权交给被通知的方法时,它需要调用ProceedingJoinPoint
的proceed()
方法。需要注意的是,别忘记调用
proceed()
方法。如果不调这个方法的话,那么你的通知实际上会阻塞对被通知方法的调用。有可能这就是你想要的效果,但更多的情况是你希望在某个点上执行被通知的方法。有意思的是,你可以不调用
proceed()
方法,从而阻塞对被通知方法的访问,与之类似,你也可以在通知中对它进行多次调用。要这样做的一个场景就是实现重试逻辑,也就是在被通知方法失败后,进行重复尝试。