【Spring04】Aop动态代理的使用和原理

Aop动态代理的使用和原理

Aop【动态代理】:在程序运行期间动态的将某段代码切入到指定方法的指定位置进行运行的编程方式。

一、Aop使用步骤

1、导入aop模块依赖:Spring aop (spring-aspects)/ SpringBoot aop (spring-boot-starter-aop)

<!-- aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

2、定义一个业务逻辑类(MathOperate),在业务逻辑运行时将日志进行打印

public class MathOperate {
    
    public int div(int num1, int num2) {
        System.out.println("MathOperate...div...");
        return num1/num2;
    }
}

3、定义一个日志切面类(Aop_Aspects),切面类动态感知 MathOperate.div 执行
通知方法:
前置通知(@Before):beforeOperate
后置通知(@After):afterOperate
返回通知(@AfterReturning):afterReturningOperate
异常通知(@AfterThrowing):afterThrowingOperate
环绕通知(@Around):aroundOperate

4、给切面类目标方法标注何时何地运行(通知注解)

5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中

6、必须告诉Spring哪个类是切面类:给切面类加上一个注解(@Aspect)

【7】、开启AOP切面自动代理功能(@EnableAspectJAutoProxy)

// 开启AOP切面自动代理功能
@EnableAspectJAutoProxy
@Configuration
public class Aop_ConfigBean {

    // 切面类加入容器
    @Bean
    Aop_Aspects aop_aspects() {
        return new Aop_Aspects();
    }

    // 业务类加入容器
    @Bean
    MathOperate mathOperate() {
        return new MathOperate();
    }
}

编写一个测试方法进行测试,可以看出业务方法被我们自定义的AOP动态代理了。

@Test
public void test() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Aop_ConfigBean.class);

    // 1、不要自己创建对象,只有加在容器中的组件才能提供aop功能
    //MathOperate mathOperate1 = new MathOperate();
    //int div = mathOperate1.div(1, 1);

    String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
        System.out.println(beanDefinitionName);
    }
    // 2、从容器中获取
    MathOperate mathOperate2 = applicationContext.getBean(MathOperate.class);
    System.out.println(mathOperate2);
    mathOperate2.div(1, 1);
    System.out.println("======");
}
输出:
div...@Before...beforeOperate...参数列表为:[1, 1]
MathOperate...div...
div...@AfterReturning...afterReturningOperate...参数列表为:[1, 1]...返回值为:1
div...@After...afterOperate...参数列表为:[1, 1]

二、Aop原理解析

了解一个东西的原理,可以分析其给容器注册了什么组件,组件什么时候工作的,工作时候做了什么事情。

现在我们来看一下Aop的原理,首先注意到其需要使用注解@EnableAspectJAutoProxy开启AOP切面的自动代理功能。

2.1 @EnableAspectJAutoProxy是什么?

点击进去可以看到该注解,使用@Import({AspectJAutoProxyRegistrar.class}),给容器导入了 AspectJAutoProxyRegistrar.class

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {}

其利用 AspectJAutoProxyRegistrar 给容器中注册bean:

​ 点击其中可以发现一个重要的类AnnotationAwareAspectJAutoProxyCreator.class ,其核心操作就是注册了这个类的bean,其ID设置为:org.springframework.aop.config.internalAutoProxyCreator


在这里插入图片描述

2.2 AnnotationAwareAspectJAutoProxyCreator是什么?

AnnotationAwareAspectJAutoProxyCreator
——》 extends AspectJAwareAdvisorAutoProxyCreator
——》 extends AbstractAdvisorAutoProxyCreator
——》 extends AbstractAutoProxyCreator
——》 extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware

​ 【重点关注】实现了 一个后置处理器 和 自动装配BeanFactory

2.3 其BeanFactoryAware的作用?

BeanFactoryAware的作用是自动装配BeanFactory

AbstractAutoProxyCreator.setBeanFactory()
——》 AbstractAdvisorAutoProxyCreator.setBeanFactory() -> this.initBeanFactory()
——》 AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()

在这里插入图片描述

2.4 容器启动过程中后置处理器是怎么执行的?

1)传入配置类,创建IOC容器;
2)注册配置类,调用 this.refresh(); 刷新容器;

3)this.registerBeanPostProcessors(beanFactory); 注册bean的后置处理器来方便拦截bean的创建;

在这里插入图片描述

​ 1)先获取ioc容器中已经定义了需要创建对象的所有 BeanPostProcessor

​ String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

​ 2)容器中添加其他的 BeanPostProcessor -> BeanPostProcessorChecker

​ 3)优先注册实现了 PriorityOrdered 接口的 BeanPostProcessor

​ 4)再注册实现了 Ordered接口的 BeanPostProcessor

​ 5) 注册没有实现优先级接口的 BeanPostProcessor

​ 6)注册 BeanPostProcessor,实际上就是创建 BeanPostProcessor 对象,保存在容器中

  • 创建 internalAutoProxyCreator 的 BeanPostProcessor 【AnnotationAwareAspectJAutoProxyCreator】
  • 首先获取bean,getBean() -> doGetBean() -> 获取不到进行创建bean -> createBean() -> doCreateBean()
  • 1)this.createBeanInstance(); 创建Bean的实例
  • 2)populateBean(),给bean进行各种属性的赋值
  • 3)initializeBean(),初始化bean
    • 1)invokeAwareMethods():处理Aware接口的回调
    • 2)applyBeanPostProcessorsBeforeInitialization():执行后置处理器的初始化方法 postProcessBeforeInitialization
    • 3)invokeInitMethods():执行自定义的初始化方法
    • 4)applyBeanPostProcessorsAfterInitialization():执行后置处理器的后置方法 postProcessAfterInitialization
  • 4)AnnotationAwareAspectJAutoProxyCreator 创建成功,设置了 aspectJAdvisorsBuilder

​ 7)把 BeanPostProcessor 注册到 BeanFactory中:

​ beanFactory.addBeanPostProcessor(postProcessor);

以上三大步骤为:创建和注册 后置处理器 AnnotationAwareAspectJAutoProxyCreator的过程。

4)this.finishBeanFactoryInitialization(beanFactory); 完成BeanFactory初始化工作:创建剩余的单实例bean

​ 1)遍历获取容器中所有的bean,依次创建对象getBean(beanName);

​ List beanNames = new ArrayList(this.beanDefinitionNames);

​ this.getBean(beanName); -> doGetBean() -> getSingleton -> createBean() -> doCreateBean();

​ 2)创建Bean

​ 1)先从缓存中获取当前bean,如果能获取到说明bean是之前被创建过的,直接使用,否则再创建;

​ Object sharedInstance = this.getSingleton(beanName);

​ 只要创建好的bean会被缓存起来

​ 2)createBean() 创建bean

在这里插入图片描述

​ 1)beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse); 解析 BeforeInstantiation;

​ 希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
在这里插入图片描述

hasInstantiationAwareBeanPostProcessors():判断所有后置处理器中是否存在【InstantiationAwareBeanPostProcessor】

【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】

【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】

​ InstantiationAwareBeanPostProcessor 后置处理器:

bean = this.applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

// 拿到所有的后置处理器,如果是 InstantiationAwareBeanPostProcessor 就执行 postProcessBeforeInstantiation()

if (bean != null) {

    如果bean不为空则执行后置处理器的后置方法

        bean = this.applyBeanPostProcessorsAfterInitialization(bean, beanName);

}

​ 2)doCreateBean();真正创建一个bean实例,和3.6的流程一致

​ Object beanInstance = doCreateBean(beanName, mbdToUse, args);

因为 AnnotationAwareAspectJAutoProxyCreator 实现了 InstantiationAwareBeanPostProcessor 所以其会在任何bean创建之前先尝试返回bean的实例。

所以研究AnnotationAwareAspectJAutoProxyCreator 的【InstantiationAwareBeanPostProcessor 】作用,就了解了其生效的原理。

2.5 其InstantiationAwareBeanPostProcessor的作用?

1)首先能看到每一个bean创建前,拿到所有的后置处理器,如果是 InstantiationAwareBeanPostProcessor 就执行其 postProcessBeforeInstantiation()

在这里插入图片描述

2)接下来看下AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的

postProcessBeforeInstantiation()方法做了什么操作?

在其AnnotationAwareAspectJAutoProxyCreator父类——》AbstractAutoProxyCreator下看到了重写的postProcessBeforeInstantiation()方法

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);

    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        // 判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        // isInfrastructureClass(beanClass) 判断当前bean是否为 Advice、Pointcut、Advisor、AopInfrastructureBean 或者 是否是切面 @Aspect
        // shouldSkip(beanClass, beanName) 是否需要跳过
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }

    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    return null;
}

isInfrastructureClass(beanClass) 判断当前bean是否为 Advice、Pointcut、Advisor、AopInfrastructureBean 或者 是否是切面 @Aspect
在这里插入图片描述

shouldSkip(beanClass, beanName) 是否需要跳过

在这里插入图片描述

​ (1)findCandidateAdvisors() 获取所有的增强器

​ beforeOperate()等每个封装方法的增强器是 InstantiationModelAwarePointcutAdvisor

​ 判断每个增强器是否是 AspectJPointcutAdvisor 类型的:返回true

​ (2)父类 shouldSkip() 永远返回 false

3)2.4的3.6中doCreateBean() ——》 initializeBean()
在这里插入图片描述

执行了 AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的 postProcessAfterInitialization() 方法

postProcessAfterInitialization() 方法做了什么操作?

创建代理对象 return wrapIfNecessary(bean, beanName, cacheKey);

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // 获取容器中所有的增强器
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // 如果增强器不为空,保存bean至增强器缓存中,存值为TRUE
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建当前bean的代理对象 
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // 给容器中返回当前组件增强了的代理对象 
        return proxy;
    }

    // 如果增强器为空,保存bean至增强器缓存中,存值为False 
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

总结来说:

1)首先获取容器中所有的增强器

1)找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的) List<Advisor> candidateAdvisors = findCandidateAdvisors();
2)获取到能在bean使用的增强器 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);          3)给增强器进行排序 sortAdvisors(eligibleAdvisors);

2)如果增强器为空,保存bean至增强器缓存中,存值为False

3)如果增强器不为空,保存bean至增强器缓存中,存值为TRUE,创建当前bean的代理对象,返回当前组件增强了的代理对象

重点解析:Object proxy = createProxy()
在这里插入图片描述

(1)获取所有的增强器(通知方法)

​ Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);

(2)保存到 proxyFactory 对象中;proxyFactory.addAdvisors(advisors);

(3)创建代理对象 proxyFactory.getProxy(classLoader); spring自动决定

在这里插入图片描述

4)以后容器中获取的这个bean是这个组件增强的代理对象,执行目标方法时,代理对象就会执行通知方法;

2.6 目标方法的执行

经过上面容器的启动,容器中保存了组件的代理对象(cglib增强的对象),这个对象里面保存了详细详细(比如:增强器、目标对象,xxx等等)。

在对象目标方法被执行时,内部操作是怎样的?

在目标方法执行处,打上断点,Step into其中

1)CglibAopProxy 的 intercept() 方法,拦截目标方法的执行

2)根据ProxyFactory对象获取目标方法的拦截器链

​ List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

​ 1)List interceptorList保存所有拦截器 5,一个默认的ExposeInvocationInterceptor 和 4个增强器;

​ 2)遍历所有的增强器,将其转为 Interceptor; registry.getInterceptors(advisor);

​ 3)、将增强器转为List;

​ 如果是 PointcutAdvisor、IntroductionAdvisor,将增强器转为MethodInterceptor;

​ 如果不是,直接加入到集合中

拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor 方法拦截器机制)

3)如果没有拦截器链,直接执行目标方法

4)如果存在拦截器链,把需要执行的代理对象、目标对象、目标方法、参数、目标类、拦截器链、代理方法传入创建一个 CglibMethodInvocation 对象,并调用其的 proceed() 方法;

// 获取目标方法的拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 如果没有拦截器链
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
   Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    // 直接执行目标方法
   retVal = methodProxy.invoke(target, argsToUse);
}
else {
   // We need to create a method invocation...
   // 如果存在拦截器链,创建一个 CglibMethodInvocation 对象,并调用其的 proceed() 方法;
   retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}

【5】拦截器链的触发过程

​ (1)如果没有拦截器执行目标方法或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;

​ (2)链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;

​ 拦截器链的机制,保证通知方法与目标方法的执行顺序;

执行完后,结果输出为:

div...@Before...beforeOperate...参数列表为:[1, 1]
MathOperate...div...
div...@AfterReturning...afterReturningOperate...参数列表为:[1, 1]...返回值为:1
div...@After...afterOperate...参数列表为:[1, 1]

可见目标方法执行时,其代理对象的增强方法,转变为方法拦截器,通过拦截器链的调用机制,保证通知方法与目标方法的执行顺序;

三、AOP原理的总结

1、@EnableAspectJAutoProxy 开启AOP切面自动代理功能

2、@EnableAspectJAutoProxy 会给容器中注册一个 AnnotationAwareAspectJAutoProxyCreator 组件

3、AnnotationAwareAspectJAutoProxyCreator 是一个后置处理器

4、容器创建的流程

​ (1)this.registerBeanPostProcessors(beanFactory); 注册bean的后置处理器;创建 AnnotationAwareAspectJAutoProxyCreator 对象

​ (2)this.finishBeanFactoryInitialization(beanFactory); 完成BeanFactory初始化工作:创建剩余的单实例bean;

​ 1)创建业务逻辑组件和切面组件

​ 2)AnnotationAwareAspectJAutoProxyCreator 后置处理器拦截组件的创建过程

​ 3)组件创建完成之后,判断组件是否需要增强

​ 是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象

5、执行目标方法

​ (1)对象为代理对象,执行目标方法

​ (2)CglibAopProxy 的 intercept() 方法,拦截目标方法的执行

​ 1)获取目标方法的拦截器链(增强器包装成方法拦截器MethodInterceptor)

Processors(beanFactory); 注册bean的后置处理器;创建 AnnotationAwareAspectJAutoProxyCreator 对象

​ (2)this.finishBeanFactoryInitialization(beanFactory); 完成BeanFactory初始化工作:创建剩余的单实例bean;

​ 1)创建业务逻辑组件和切面组件

​ 2)AnnotationAwareAspectJAutoProxyCreator 后置处理器拦截组件的创建过程

​ 3)组件创建完成之后,判断组件是否需要增强

​ 是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOP中,动态代理是实现AOP的一种方式。Spring AOP使用Java动态代理机制来创建代理对象,并将切面逻辑织入到目标对象的方法调用中。 动态代理是在运行时生成代理对象的一种机制。它不需要在编写代码时就确定要代理的类和方法,而是在运行时根据需要创建代理对象。在Spring AOP中,主要有两种类型的动态代理:基于接口的代理和基于类的代理。 1. 基于接口的代理(JDK动态代理):当目标对象实现了至少一个接口时,Spring AOP使用JDK动态代理来生成代理对象。JDK动态代理通过实现目标对象所实现的接口来生成代理对象,在调用代理对象的方法时,会通过InvocationHandler接口将方法调用转发给实际的目标对象。 2. 基于类的代理(CGLIB动态代理):当目标对象没有实现任何接口时,Spring AOP使用CGLIB动态代理来生成代理对象。CGLIB动态代理通过继承目标对象生成一个子类,并覆盖其中的方法来实现代理。调用代理对象的方法时,会先进入子类的方法,然后再调用目标对象的方法。 使用动态代理可以实现对目标对象的拦截和增强,而不需要修改目标对象的源代码。代理对象可以在目标对象的方法执行前、执行后或执行过程中插入额外的逻辑,例如日志记录、性能统计、事务管理等。 需要注意的是,动态代理只能对公共方法进行拦截,对私有方法、静态方法或final方法无法进行拦截。同时,动态代理也只能拦截通过代理对象调用的方法,直接通过目标对象调用方法时无法实现拦截和增强。 总结来说,Spring AOP使用动态代理来实现切面逻辑的织入,可以通过JDK动态代理或CGLIB动态代理来生成代理对象,并将切面逻辑应用到目标对象的方法调用中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值