Spring的AOP原理

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>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>2.2.2</version>
    </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();
    }
}

调用的时候会先执行我们那个增强的逻辑,然后再执行目标方法
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值