Spring的AOP原理

Spring的AOP是我们在开发中比较常用的一种思想,比如日志的数据等,那Spring是如何通过配置来创建AOP的呢

本文主要通过注解配置来讲解

解析切面类

我们在使用注解配置AOP的时候通常需要@EnableAspectJAutoProxy这个注解来开启AOP
Spring的AOP原理_第1张图片
那其实这个注解里的@Import注解里有个AspectJAutoProxyRegistrar类来注册对应的BeanDefinition
Spring的AOP原理_第2张图片
这个类里注册了一个AnnotationAwareAspectJAutoProxyCreator这个BeanDefinition
Spring的AOP原理_第3张图片
我们可以看看这个类的继承关系图
Spring的AOP原理_第4张图片
这个类最终是实现了InstantiationAwareBeanPostProcessor、SmarInstantiationAwareBeanPostProceessor、 BeanPostProcessor这三个后置处理器,所以在这个Bean创建的时候会第一次调用InstantiationAwareBeanPostProcessor接口里的postProcessBeforeInstantiation()方法来解析切面,会在初始化后调用BeanPostProcessor后置处理器的postProcessAfterInitialization()方法来创建动态代理,在遇到循环依赖的时候会调用SmarInstantiationAwareBeanPostProceessor后置处理器的getEarlyBeanReference()方法来解决循环引用AOP

那么我们可以按照顺序来寻找对应的代码

首先是在创建Bean的时候,会第一次调用BeanPostProcessor后置处理器

Spring的AOP原理_第5张图片
这个方法里就是判断是否是继承了InstantiationAwareBeanPostProcessor,如果继承了的话则直接调用postProcessBeforeInstantiation()方法
Spring的AOP原理_第6张图片
而这里就会去解析我们配置的切面类
Spring的AOP原理_第7张图片
那我们再来看看它是如何去解析切面的

首先会去创建一个缓存,然后去判断是否被解析过以及做一些过滤

那根据什么去过滤的呢

这里主要就是判断这个解析的类是不是Advice、Pointcut、Advisor等
Spring的AOP原理_第8张图片
如果没有需要过滤的话,那么就会调用shouldSkip()方去解析切面了
Spring的AOP原理_第9张图片
这里首先会去获取候选的Advisors
Spring的AOP原理_第10张图片
我们继续跟代码看findCandidateAdvisors()方法,这个方法里会调用父类的方法来获取实现了Advisor接口的Bean
Spring的AOP原理_第11张图片
这里为什么要去获取Advisor接口的实现类呢,主要就是为了兼容老版本配置AOP,那么到了下一步就是去调用buildAspectJAdvisors()方法,那这个方法就是用来解析切面类
Spring的AOP原理_第12张图片
这个方法里首先会去获取所有的BeanDefinition
Spring的AOP原理_第13张图片
在获取到所有的BeanDefinition之后就会一个一个去判断是不是切面
Spring的AOP原理_第14张图片
那如果来判断的呢,就是根据你这个类上面有没有@Aspect注解来判断的

我们可以进入到isAspect()方法里看看

Spring的AOP原理_第15张图片

如果是切面类的话就会加入到缓存中
Spring的AOP原理_第16张图片
然后再去获取切面当中所有的通知
Spring的AOP原理_第17张图片
这个方法会直接获取除@Pointcut注解之外的所有的方法
Spring的AOP原理_第18张图片
这个方法在获取完之后会根据注解类型对方法进行排序
Spring的AOP原理_第19张图片
Spring的AOP原理_第20张图片
获取之后,再一个一个去循环方法并解析成一个Advisors
Spring的AOP原理_第21张图片
getAdvisor()也会对切面类里的方法进行判断,判断方法上是否含有@Pointcut、@Around、@Before等这些注解
Spring的AOP原理_第22张图片
Spring的AOP原理_第23张图片
并会按照顺序来解析

在解析的时候会优先获取引用的切点表达式
Spring的AOP原理_第24张图片
获取完之后就会去创建InstantiationModelAwarePointcutAdvisorImpl类,这个类就是Advisor的实现类
Spring的AOP原理_第25张图片
这个类在初始化的时候会把切面中的通知构造为一个一个的advice通知对象
Spring的AOP原理_第26张图片
这里再构造的时候会根据不通的注解创建不同的advice
Spring的AOP原理_第27张图片
到了这里整个的advisor集合就创建好了,创建好了之后再将切面的BeanName加入到缓存中去
Spring的AOP原理_第28张图片
然后再将所有的advisor加入到集合里去,这里的集合就是候选的Advisor

Spring的AOP原理_第29张图片
在获取到候选的Advisor的之后还会去判断Advisor是不是xml解析出来的Advisor,如果是的话就需要跳过,因为xml配置的切面是没有注解的
Spring的AOP原理_第30张图片
所有的Advisor都解析完了之后,再根据Advisor去做匹配然后创建动态代理

而创建动态代理会在Bean初始化后去创建,而初始化后会调用BeanPostProcess里的postProcessAfterInitialization()方法来创建动态代理
Spring的AOP原理_第31张图片
Spring的AOP原理_第32张图片
这里首先会从缓存中去获取对应的Bean,如果是循环依赖创建动态代理并且是现在的Bean就不再创建,并且移除
Spring的AOP原理_第33张图片如果没有循环依赖则直接调用wrapIfNecessary()方法来创建代理实例这个方法里通过一系列的判断之外,就会根据BeanName去做匹配

Spring的AOP原理_第34张图片
这个方法会通过切面的BeanName来从缓存中获取对应的Advisor
Spring的AOP原理_第35张图片

匹配方法

在将切面类里的通知解析成一个Advisors之后,紧接着就会根据切点表达是来对方法进行匹配,匹配上了就会创建动态代理

在获取完之后就会判断所有的通知是否可以应用到bean上
Spring的AOP原理_第36张图片
这个方法是通过AspectJ相关的API去做判断,通过调用findAdvisorsThatCanApply()方法
Spring的AOP原理_第37张图片
这个方法里它会去循环所有的Advisor
Spring的AOP原理_第38张图片
在循环的时候会去判断是否实现了IntroductionAdvisor接口

这一步判断完之后就会去匹配对应的通知

Spring的AOP原理_第39张图片
在匹配完成之后又会往接口里加一个Advisor
Spring的AOP原理_第40张图片
加完之后就会对Advisor进行排序
Spring的AOP原理_第41张图片
匹配完成之后再给匹配上了的bean进行创建动态代理
Spring的AOP原理_第42张图片

创建动态代理

在createProxy()方法里面首先会创建一个代理工厂
Spring的AOP原理_第43张图片
创建完成之后会去判断有没有proxyTargetClass属性,如果又的话会去设置一下这个属性
Spring的AOP原理_第44张图片
最后去创建动态代理
Spring的AOP原理_第45张图片
这个方法会直接通过createAopProxy()方法来创建动态代理对象
Spring的AOP原理_第46张图片
这个方法里会根据对应的条件来创建对应的动态代理
Spring的AOP原理_第47张图片
如果没有接口,没有proxyTargetClass属性就是直接使用jdk动态代理
Spring的AOP原理_第48张图片
然后再去调用getProxy()方法来创建jdk动态代理
Spring的AOP原理_第49张图片
Spring的AOP原理_第50张图片
创建完之后就会直接返回回去
Spring的AOP原理_第51张图片
Spring的AOP原理_第52张图片
存放到一级缓存中
再调用Bean里的某个方法的时候会直接来到JdkDynamicAopProxy类里的invoke()方法
Spring的AOP原理_第53张图片
通过一系列的判断执行之后,会将Advisor转换为interceptor,因为只有实现了interceptor才会有invoke()方法,最后通过这个invoke()方法来进行责任链调用

Spring的AOP原理_第54张图片
转换完成之后再去调用proceed()方法
Spring的AOP原理_第55张图片
这个方法里面就是通过责任链的方式去一次调用通知
Spring的AOP原理_第56张图片

动态代理模式

Spring的AOP就是对动态代理模式的一种运用,我们具体再来看看这个动态代理模式

动态代理一种分为两种,一种是JDK的动态代理,还有一种是cglib动态代理

我们先来看看第一种

第一种主要是JDK提供的一种动态代理方法,这个动态代理方式需要一个接口,也就是被代理的那个类需要实现一个接口才可以

我们这里有个User的接口

package com.dlmo.dtdl;

public interface UserInterface {
     // 玩游戏
     public  void  playGame();
}

这个接口里面有个playGeme()方法

我们还需要一个实现类实现这个接口

package com.dlmo.dtdl;

public class UserImpl implements UserInterface{
    public void playGame() {
        System.out.println("玩游戏!!!");
    }
}

写完了被代理类之后,我们还需要写一个增强的方法

JDK给我们提供了一个InvocationHandler类,我们只需要实现这个类,重写里面的invoke()方法就行了

package com.dlmo.dtdl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class InvokeHandler implements InvocationHandler {
    //代表目标对象
    private Object target = null;

    public InvokeHandler(Object target) {
        //给目标对象赋值
        this.target = target;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("增强!增强!");
        return method.invoke(target, args);
    }
}

在执行目标方法之前需要做什么逻辑, 我们只需要在invoke()方法里写就行了

package com.dlmo.dtdl;

import java.lang.reflect.Proxy;

public class DtdlDemo {
    public static void main(String[] args) {
        UserInterface user = new UserImpl();
        InvokeHandler invokeHandler = new InvokeHandler(user);
        UserInterface userInterface = (UserInterface)Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), invokeHandler);
        userInterface.playGame();
    }
}

此时我们只需要调用代理对象的方法就可以进行增强了
Spring的AOP原理_第57张图片
我们再来看看第二种

cglib动态代理是不需要用到接口的,但是需要额外的进入一个cglib的jar包

   <dependency>
          <groupId>cglibgroupId>
          <artifactId>cglibartifactId>
          <version>2.2.2version>
    dependency>

我们重新创建一个被代理类

package com.dlmo.dtdl;

public class CglibUser {
    public void playGame(){
        System.out.println("打游戏!!!");
    }
}

写完被代理类之后还需要一个增强类

cglib给我们提供了一个MethodInterceptor 接口,我们只需要实现这个接口,实现这个intercept()方法就行了

package com.dlmo.dtdl;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibInterceptor implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class<?> clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
        System.out.println("增强!增强!");
        return arg3.invokeSuper(arg0, arg2);
    }
}

都写完了之后,我们就可以通过cglib的来创建动态代理了

package com.dlmo.dtdl;

public class CglibDtdlDemo {
    public static void main(String[] args) {
        CglibUser cglibInterceptor = (CglibUser)new CglibInterceptor().getProxy(CglibUser.class);
        cglibInterceptor.playGame();
    }
}

调用的时候会先执行我们那个增强的逻辑,然后再执行目标方法
Spring的AOP原理_第58张图片

你可能感兴趣的:(spring,java,后端)