Spring之AOP

112 阅读3分钟

前言

上一篇讲了jdk和cglib动态代理,实现了一个简单的责任链模式,并模拟了aop几种注解的调用顺序,这里就详细讲述aop

AOP

面向切面编程,网上有很多定义,面向对象的补充,只关注一个切点。我只举一个项目中遇到的切面,平台对设备读写定义了两个顶层接口ReadService、WriteService,读写service中的每个方法调用前都需要判断设备是否在线,且WriteService中的接口还需要判断设备是否处于私钥状态(写操作需要加密,设备必须安装了私钥)。以前是将两种判断写成两个方法,在读写service中到处可见,有时可能还会遗漏。使用切面就能很好的解决这个问题。我理解的切面:在业务中出现很多相同的功能,若在使用抽离方法仍显得冗余时可考虑切面,切面在进入目标方法前会加入一层逻辑,或者在目标方法执行后加入一层逻辑,进一步提高类聚。编程思想:抽离变化点,提取公因式

术语

先上代码,后面看看代码与术语的对应关系

@Aspect
@Component
public class F1AspectJ {
    @Pointcut("execution(* com.jm.demo.demo1.User.f1(..))")
    public void f1PointCut(){
    }
    @Before("execution(* com.jm.demo.demo1.User.f1(..))")
    public void before() {
        System.out.println("before...");
    }
    @After("f1PointCut()")
    public void After() {
        System.out.println("After...");
    }
    @AfterReturning("f1PointCut()")
    public void AfterReturning() {
        System.out.println("AfterReturning...");
    }
    @Around("f1PointCut()")
    public void aroud(ProceedingJoinPoint pj){
        try{
            System.out.println("around 之前");
            pj.proceed();
            System.out.println("around 之后");
        }catch (Throwable e){
            e.getMessage();
        }
    }
    @AfterThrowing(pointcut = "f1PointCut()",throwing = "ex")
    public void ex(Throwable ex){
        System.out.println("有异常:"+ex.getMessage());
    }
}
public class UserA implements User, InitializingBean, ApplicationContextAware, DisposableBean {
    private int order = 0;
    @Autowired
    private UserB userB;
    public UserA() {
        System.out.println("constructor order:" + order++);
    }
    @PostConstruct
    public void postConstruct() {
        System.out.println("postContruct order:" + order++);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean order:" + order++);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("ApplicationContextAware order:" + order++);
    }
    public void initMethod() {
        System.out.println("initMethod..." + order++);
    }
    public void destroyMethod() {
        System.out.println("destroyMethod..." + order++);
    }
    @PreDestroy
    public void preDestroy() {
        System.out.println("preDestroy..." + order++);
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("destroy..." + order++);
    }
    @Override
    public void f1() {
        System.out.println("userA f1...");
    }
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        context.registerShutdownHook();
        UserA userA = context.getBean("userA", UserA.class);
        userA.f1();
    }
}

输出结果:

image-20220112145644827.png

Advice 通知

Tag interface for Advice. Implementations can be any type of advice, such as Interceptors

用于通知的标记接口,实现可以是任何类型的通知,比如拦截器。可以理解为上一篇文章责任链模式图中的拦截器,所有类型的通知都会变成一个Advice接口的实现,在userA.f1()处debug

image-20220112114244237.png 可以看到所有的通知变成了Advice的实现,且添加拦截器的顺序为Around、Before、After、AfterReturning、AfterThrowing

  • @Around 目标方法的前后添加逻辑,如统计方法调用调用时长

  • @Before 目标方法调用前执行 image-20220112144151665.png mi.proceed()进入链条逻辑,触发下一个拦截器

  • @After 目标方法执行后最终执行,被放在了finally中

    image-20220112144854598.png

  • @AfterReturning 目标方法返回后调用

    image-20220112144530454.png

  • @AfterThrowing 目标方法异常时被执行

    image-20220112145201305.png 执行流程:

image-20220112153024631.png 通过顶层的链条遍历拦截器达到拦截器层层调用的效果。很多文章说这里是递归,其实不是,递归是自己调用自己,并在一定条件推出,而这里只是拦截器的层层调用,调用链比较长而已

JointPoint 连接点

能够被切入的位置,代码中execution定义的就是连接点。JoinPoint属于org.aspectj包中源码,被spring中的MethodInvocationProceedingJoinPoint实现,用于解析和处理execution中的内容

PointCut 切点

可理解为连接点的别名,若没有PointCut,某个目标方法存在多种通知时,都需要在注解里面定义execution连接点,使用PointCut给连接点定一个别名,通知注解可以直接使用别名,减少冗余代码

Aspect

标注某个类为切面类

思考题

@Service
public class TxService {
    @Autowired
    private TxService txService;
    @PostConstruct
    public void init(){
        //是否相等,为什么
        System.out.println(txService == this);
    }
}
@Service
public class TxService {
    @Autowired
    private TxService txService;
    @PostConstruct
    public void init(){
        //是否相等,为什么
        System.out.println(txService == this);
    }
    @Transactional
    public void f1(int x){
    }
}