Spring的AOP原理

Spring的AOP原理,第1张

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

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

解析切面类

我们在使用注解配置AOP的时候通常需要@EnableAspectJAutoProxy这个注解来开启AOP

那其实这个注解里的@Import注解里有个AspectJAutoProxyRegistrar类来注册对应的BeanDefinition

这个类里注册了一个AnnotationAwareAspectJAutoProxyCreator这个BeanDefinition

我们可以看看这个类的继承关系图

这个类最终是实现了InstantiationAwareBeanPostProcessor、SmarInstantiationAwareBeanPostProceessor、 BeanPostProcessor这三个后置处理器,所以在这个Bean创建的时候会第一次调用InstantiationAwareBeanPostProcessor接口里的postProcessBeforeInstantiation()方法来解析切面,会在初始化后调用BeanPostProcessor后置处理器的postProcessAfterInitialization()方法来创建动态代理,在遇到循环依赖的时候会调用SmarInstantiationAwareBeanPostProceessor后置处理器的getEarlyBeanReference()方法来解决循环引用AOP

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

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


这个方法里就是判断是否是继承了InstantiationAwareBeanPostProcessor,如果继承了的话则直接调用postProcessBeforeInstantiation()方法

而这里就会去解析我们配置的切面类

那我们再来看看它是如何去解析切面的

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

那根据什么去过滤的呢

这里主要就是判断这个解析的类是不是Advice、Pointcut、Advisor等

如果没有需要过滤的话,那么就会调用shouldSkip()方去解析切面了

这里首先会去获取候选的Advisors

我们继续跟代码看findCandidateAdvisors()方法,这个方法里会调用父类的方法来获取实现了Advisor接口的Bean

这里为什么要去获取Advisor接口的实现类呢,主要就是为了兼容老版本配置AOP,那么到了下一步就是去调用buildAspectJAdvisors()方法,那这个方法就是用来解析切面类

这个方法里首先会去获取所有的BeanDefinition

在获取到所有的BeanDefinition之后就会一个一个去判断是不是切面

那如果来判断的呢,就是根据你这个类上面有没有@Aspect注解来判断的

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

如果是切面类的话就会加入到缓存中

然后再去获取切面当中所有的通知

这个方法会直接获取除@Pointcut注解之外的所有的方法

这个方法在获取完之后会根据注解类型对方法进行排序


获取之后,再一个一个去循环方法并解析成一个Advisors

getAdvisor()也会对切面类里的方法进行判断,判断方法上是否含有@Pointcut、@Around、@Before等这些注解


并会按照顺序来解析

在解析的时候会优先获取引用的切点表达式

获取完之后就会去创建InstantiationModelAwarePointcutAdvisorImpl类,这个类就是Advisor的实现类

这个类在初始化的时候会把切面中的通知构造为一个一个的advice通知对象

这里再构造的时候会根据不通的注解创建不同的advice

到了这里整个的advisor集合就创建好了,创建好了之后再将切面的BeanName加入到缓存中去

然后再将所有的advisor加入到集合里去,这里的集合就是候选的Advisor


在获取到候选的Advisor的之后还会去判断Advisor是不是xml解析出来的Advisor,如果是的话就需要跳过,因为xml配置的切面是没有注解的

所有的Advisor都解析完了之后,再根据Advisor去做匹配然后创建动态代理

而创建动态代理会在Bean初始化后去创建,而初始化后会调用BeanPostProcess里的postProcessAfterInitialization()方法来创建动态代理


这里首先会从缓存中去获取对应的Bean,如果是循环依赖创建动态代理并且是现在的Bean就不再创建,并且移除
如果没有循环依赖则直接调用wrapIfNecessary()方法来创建代理实例这个方法里通过一系列的判断之外,就会根据BeanName去做匹配


这个方法会通过切面的BeanName来从缓存中获取对应的Advisor

匹配方法

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

在获取完之后就会判断所有的通知是否可以应用到bean上

这个方法是通过AspectJ相关的API去做判断,通过调用findAdvisorsThatCanApply()方法

这个方法里它会去循环所有的Advisor

在循环的时候会去判断是否实现了IntroductionAdvisor接口

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


在匹配完成之后又会往接口里加一个Advisor

加完之后就会对Advisor进行排序

匹配完成之后再给匹配上了的bean进行创建动态代理

创建动态代理

在createProxy()方法里面首先会创建一个代理工厂

创建完成之后会去判断有没有proxyTargetClass属性,如果又的话会去设置一下这个属性

最后去创建动态代理

这个方法会直接通过createAopProxy()方法来创建动态代理对象

这个方法里会根据对应的条件来创建对应的动态代理

如果没有接口,没有proxyTargetClass属性就是直接使用jdk动态代理

然后再去调用getProxy()方法来创建jdk动态代理


创建完之后就会直接返回回去


存放到一级缓存中
再调用Bean里的某个方法的时候会直接来到JdkDynamicAopProxy类里的invoke()方法

通过一系列的判断执行之后,会将Advisor转换为interceptor,因为只有实现了interceptor才会有invoke()方法,最后通过这个invoke()方法来进行责任链调用


转换完成之后再去调用proceed()方法

这个方法里面就是通过责任链的方式去一次调用通知

动态代理模式

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();
    }
}

此时我们只需要调用代理对象的方法就可以进行增强了

我们再来看看第二种

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();
    }
}

调用的时候会先执行我们那个增强的逻辑,然后再执行目标方法

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/868386.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存