Spring AOP 的搭建与源码分析

2023-11-15

AOP 面向切面编程

一. AOP 概述

  1. 什么是 AOP : 指定程序在运行期间动态的将某段代码功能切入到指定的位置运行,底层是通过动态代理来实现的
  2. AOP 优点 : 降低代码的耦合,提高代码复用性,提高系统的扩展性
  3. AOP 常见使用场景 : 例如日志记录, 性能统计, 权限控制, 事物处理等

AOP 的实现步骤

  1. Spring 引入 AOP 需要的依赖
  2. 设置 Spring 开启 AOP 功能 (xml 方式开启,与注解方式开启)
  3. 创建存有需要通过 AOP 增强监管的业务代码,并注入到 Spring 容器中
  4. 创建 AOP 切面类,将切面类注入到 Spring 容器中,
  5. 告诉 Spring 容器,我是一个切面类 @Aspect
  6. 切面类中创建通知方法 :
  1. 前置通知 @Before 目标方法执行前运行
  2. 后置通知 @After 目标方法执行完毕,或抛出异常时运行
  3. 返回通知 @AfterReturning 目标方法正常返回之后运行
  4. 异常通知 @AfterThrowing 目标方法发生异常时运行
  5. 环绕通知 @Around 该通知中可以获取到被监管的所有数据,可以获取到被监管执行的方法,在环绕通知方法中代理执行,所以可以自定义在被监管的方法前后添加需要执行的其他功能
  1. 设置通知方法在指定位置标注位置运行,切入点 (需要进行增强的业务代码位置, xml方式, 注解方式 @Pointcut(“execution()”)
    Spring 支持 AOP :需要添加依赖

示例

  1. Spring 中引入 AOP 依赖
			<dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aop</artifactId>
                <version>${spring-version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>${spring-version}</version>
            </dependency>
  1. 注解方式配置开启 AOP ,创建配置类,配置类使用 @EnableAspectJAutoProxy 注解修饰
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.*;
//通过配置类,使用注解的方式开启 Spring 的 aop 功能
@EnableAspectJAutoProxy
@Configuration
public class MyConfiguration {
}
  1. xml 方式配置开启AOP, resources 文件夹中创建.xml配置文件,配置文件中添加 < aop:aspectj-autoproxy/ >,注意添加命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop=http://www.springframework.org/schema/aop
xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans"
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<!--开启aop-->
<aop:aspectj-autoproxy/>

<!--向Spring 容器中注入bean 的包扫描,原因是一般情况下
AOP 切面类与,controller,server,dao,不在一个包下,在使用注解
向spring中注入时,没有扫描,无法注入,如果在一个包下,或其它位置已经扫描过了,则可以不定义-->
<context:component-scan base-package="com.aop.自定义切面类所在的包" />
</beans>
  1. 创建业务类,编写业务代码,假设在业务代码执行前后,或发生异常时通过 AOP 增加其它功能
@Controller
public class TestController2 {
    @RequestMapping(value = "/aoptest", method = RequestMethod.GET)
    @ResponseBody
    public void aoptest(){
        System.out.println("我是被监管的业务代码......");
    }
}
  1. 创建切面类,将切面类注入到 Spring 容器中, 告诉 Spring 我是一个切面类,设置切入点,注入切入点扫描的目标方法所在路径是否正确,定义通知方法
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect //1.代表该类时切面类
@Component //2.将切面类注入到 Spring 容器中
public class LogCut {

    //3.定义切入点方法,方法上添加@Pointcut()注解,设置当前切面类监管的目标方法位置
    //为了方便可以单独修饰一个空方法,然后通知注解上引用这个方法,也可以直接修饰通知方法
    /*解释:execution代表是个表达式
     * 扫描 com.ssm.controller 包下的所有类的所有方法
     * 第一个星的位置:代表这个方法的权限修饰符,星为任何权限都可以
     * com.com.controller 代表哪个包下的
     * controller 后的点有两个:一个代表该包下的根目录下的直接类中方法不包括子包
     * 两个点代表包括该包下的子包中类的方法
     * 点后的第一个星号位置代表包下的类,此处为星号代表任意类
     * 第二个星号位置代表方法,此处为星号代表任意方法
     * 括号中的两个点,为方法传入的参数类型,此处为两个点代表任意参数*/
    @Pointcut("execution(* com.ssm.controller..*.*(..))")
    public void cut() {
    }

    //4.定义通知方法,方法上添加对应的注解,

    //前置通知,设置前置通知监管的目标方法
    //如果被监管的目标方法有参数需要获取,可以在通知
    //方法中定义一个 JoinPoint 的形参,通过这个参数
    //可以拿到目标方法的相关数据,注意点 JoinPoint
    //这个参数一定要放在形参的第一个位置,否则报错
    @Before("cut()")
    public void before(JoinPoint joinPoint) {
        //获取目标方法签名
        Signature signature = joinPoint.getSignature();
        //获取目标方法签名获取方法参数
        Object [] args = joinPoint.getArgs();
        //通过目标方法签名获取目标方法名
        String name = signature.getName();
        System.out.println("前置通知方法执行,监管了"+name);
    }

    //返回通知,目标方法如果出现异常没有执行完毕,该通知方法不会执行
    //@AfterReturning("cut()")
    //如果被监管的方法有返回值,需要获取方法执行后的返回值,可以返回通知注解中
    //设置 returning 属性,属性值是返回的数据名,然后在方法形参中定义与该值同名的接收对象
    @AfterReturning(value = "cut()", returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {
        System.out.println("返回通知,正常完毕会执行该方法");
    }

    //后置通知,不管目标类方法是否发生异常,该通知方法都会执行
    @After("cut()")
    public void after() {
        System.out.println("后置通知,不管是否执行完毕,都会执行");
    }

    //异常通知目标类方法执行时发生异常执行,
    //throwings 代表目标方法抛出的异常,注解中 throwing的值要与方法中形参相同
    @AfterThrowing(value = "cut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e) {
        System.out.println("异常通知,发生异常时执行该终止" + e);
    }
    
    //环绕通知,目标方法执行前后,都可以做出相应的处理
    //环绕通知方法中需要传入一个ProceedingJoinPoint对象,该对象可以看为被拦截的方法对象
    //在该通知方法中,通过 ProceedingJoinPoint 对象,手动的调用目标方法,
    //这样就可以在目标方法前后添加其它操作了
    //注意环绕通知与异常通知配合使用时,环绕通知中的逻辑处理如果异常,方法执行后的代码会失效
    @Around("cut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知的前置执行");
        //通过环绕通知中传入的ProceedingJoinPoint对象可以获取目标类对象中的所有数据
        //pjp.getTarget()方法获取到的是目标类对象的内存信息(可以理解为可以获取到目标对象)
        //pjp.getSignature()方法可以获取到被代理类的方法
        System.out.println(pjp.getTarget() + "----" + pjp.getSignature());
        //只需目标方法,被拦截方法执行后执行?(为什么注释了该方法还是会执行后面的)
        Object result = pjp.proceed();
        System.out.println("环绕通知的后置执行");
        return result;
    }
}
  1. 此时如果 TestController2 类中的 aoptest() 方法,被切入点扫描,如果该方法执行,对应的通知方法也会执行

二. 通过 @EnableAspectJAutoProxy 了解 AOP 原理

  1. @EnableAspectJAutoProxy 注解方式,开启 Spring 的 AOP 功能,查看该注解的源码
    在这里插入图片描述

1. 分析 @EnableAspectJAutoProxy

  1. 在@EnableAspectJAutoProxy中使用 @Import()向容器中注入了 AspectJAutoProxyRegistrar 类 ,查看 AspectJAutoProxyRegistrar 类,通过该类中的 registerBeanDefinitions() 方法,使用 @Import() 向容器中注入一个 AnnotationAwareAspectJAutoProxyCreator 这是一个注解模式装配切面的自动代理创建器(注入过程了解@Import注入的几种方式)
  2. 分析 AnnotationAwareAspectJAutoProxyCreator 类的继承关系,该类间接实现了SmartInstantiationAwareBeanPostProcessor 与 BeanFactoryAware接口
  1. 注意点 SmartInstantiationAwareBeanPostProcessor 继承了InstantiationAwareBeanPostProcessor 继承了BeanPostProcessor,InstantiationAwareBeanPostProcessor 与BeanPostProcessor不同的是增加了一个 postProcessBeforeInstantiation(),抽象方法,这个方法是在被监管的bean创建前执行的,这些后置处理器相关的方法在 AnnotationAwareAspectJAutoProxyCreator 的父类 AbstractAutoProxyCreator 中进行了实现,并且 bean 初始化前执行的方法 postProcessBeforeInitialization() 没做任何操作,直接进行了返回
  2. 类间接实现 BeanFactoryAware 接口,在AbstractAutoProxyCreator父类中重写 了setBeanFactory()方法,获取容器中的 BeanFactory
 AnnotationAwareAspectJAutoProxyCreator
	---->|AnnotationAwareAspectJAutoProxyCreator
	--------->|AbstractAdvisorAutoProxyCreator
    --------------->|AbstractAutoProxyCreator
	-------------------------->| ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
	(其中 SmartInstantiationAwareBeanPostProcessor 又继承了 InstantiationAwareBeanPostProcessor
	InstantiationAwareBeanPostProcessor又继承了BeanPostProcessor)

2.复习 Spring 容器启动后 bean 的注入执行过程,了解 AnnotationAwareAspectJAutoProxyCreator 的注入

  1. 复习Spring注入bean过程:
  1. Spring启动容器时会调用refresh()方法刷新容器, 先创建BeanFactory,BeanPostProcessor,最终创建其它单实例bean注入到容器中
  2. 在创建bean以前先调用getBean(),getSingleton()方法,通过beanName在容器中获取,如果获取不到,执行createBean()创建
  3. 在createBean()方法内首先会执行 resolveBeforeInstantiation(beanName, mbdToUse)方法,尝试创建当前bean的代理类并返回,如果不能则继续向下执行(AOP重点关注的步骤)
  4. 执行doCreateBean()方法创建当前bean实例, 创建完成后会执行populateBean()方法,对当前创建的bean的属性赋值,
  5. populateBean()执行完成后,执行initializeBean()方法,获取所有后置处理器,执行初始化bean先后需要执行的方法
  6. initializeBean()中有四个方法需要关注 (AOP重点关注的步骤)
  1. invokeAwareMethods() ,通过该方法,判断要初始化的bean 是否是某个Aware类型,通过执行接口中的setXX()方法(Aware,通常用来获取 Spring 提供的组件,通过重写的抽象方法赋值给当前初始化的 bean AOP重点关注步骤)
  2. applyBeanPostProcessorsBeforeInitialization() 通过该方法获取容器中的BeanPostProcessor,执行初始化bean前需要执行的方法postProcessBeforeInitialization()
  3. invokeInitMethods() 方法,通过该方法执行初始化方法(例如调用InitializingBean的afterPropertiesSet,调用自定义的init-method 等)
  4. applyBeanPostProcessorsAfterInitialization() 通过该方法获取容器中的BeanPostProcessor,执行初始化bean后需要执行的方法postProcessAfterInitialization() (AOP重点关注步骤)
  1. Spring 默认提供了一个实现BeanPostProcessor 接口的ApplicationContextAwareProcessor 后置处理器,每个bean的创建都会被该后置处理器监控到,执行初始化bean之前要执行invokeAwareInterfaces() 方法,判断当前要创建的 bean 是否是 Aware 类型,如果是,调用这个 bean 重写的Aware中的抽象方法,用来获取 Spring 的组件赋值给当前要创建的bean对象
  2. 使用@EnableAspectJAutoProxy开启AOP又向容器中注入了一个 AnnotationAwareAspectJAutoProxyCreator 间接继承了 BeanFactoryAware与BeanPostProcessor接口,在创建这个bean时,被invokeAwareInterfaces() 判断到,方法中调用重写BeanFactoryAware 接口中的 setBeanFactory() 方法获取 Spring 提供的 BeanFactory,自此 AnnotationAwareAspectJAutoProxyCreator 持有 BeanFactory 先一步注入到了 Spring容器中

3.通过 AnnotationAwareAspectJAutoProxyCreator 生成代理对象

  1. 此时 AnnotationAwareAspectJAutoProxyCreator 已经创建注入到了容器中,开始创建其它 bean
  2. 了解Spring注入bean过程后,在执行createBean()方法,创建bean示例前首先会执行一个resolveBeforeInstantiation()尝试创建代理类
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Creating instance of bean '" + beanName + "'");
        }
        RootBeanDefinition mbdToUse = mbd;
        Class<?> resolvedClass = this.resolveBeanClass(mbd, beanName, new Class[0]);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }
        try {
            mbdToUse.prepareMethodOverrides();
        } catch (BeanDefinitionValidationException var7) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", var7);
        }
        Object beanInstance;
        try {
        	//======通过后置处理器,创建 当前需要创建的 bean 的代理对象并返回================
            beanInstance = this.resolveBeforeInstantiation(beanName, mbdToUse);
            //==========================================================================
            if (beanInstance != null) {
                return beanInstance;
            }
        } catch (Throwable var8) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", var8);
        }
		//=============创建 bean ===============
        beanInstance = this.doCreateBean(beanName, mbdToUse, args);
        //======================================================
        
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) {
                Class<?> targetType = this.determineTargetType(beanName, mbd);
                if (targetType != null) {
                
                    bean = this.applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        bean = this.applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = bean != null;
        }
        return bean;
    }
  1. resolveBeforeInstantiation() 方法中调用了: applyBeanPostProcessorsBeforeInstantiation()
  2. applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);该方法会获取容器中所有后置处理器,遍历判断获取到的后置处理器是否有 InstantiationAwareBeanPostProcessor 类型的,如果有执行该后置处理器的 postProcessBeforeInstantiation(beanClass, beanName),通过该方法创建出当前要创建的 bean 的代理类对象并返回
  3. AOP 中 通过 @EnableAspectJAutoProxy 向容器中注入的 AnnotationAwareAspectJAutoProxyCreator 间接继承了 InstantiationAwareBeanPostProcessor 接口,判断通过,执行 postProcessBeforeInstantiation() 方法
    在这里插入图片描述

postProcessBeforeInstantiation() 后置处理器中,创建 bean 对象前自动执行的方法

  1. Spring注入bean 执行 resolveBeforeInstantiation() 方法,该方法中调用了AnnotationAwareAspectJAutoProxyCreator 实现的postProcessBeforeInstantiation(),查看该方法
  1. 判断要创建的bean 是否在 advisedBeans 中, advisedBeans中保存了所有要增强的bean
  2. 判断当前要创建的bean 是否是基础类型的,是否是切面类(被切入点拦截的则需要代理增强)
  3. 判断是否需要跳过,shouldSkip() 调用的是AspectJAwareAdvisorAutoProxyCreator 中的,什么时候需要跳过,例如创建的bean是通知方法包装的增强器时…
  1. 自己理解该方法中判断当前创建的bean是否需要增强,不需要则将创建的bean存入advisedBeans集合中,并标识为false,后续创建这个bean时,就不会对这个bean进行代理处理
  2. 该方法执行完毕后,没有创建bean,createBean() 方法继续向下执行调用doCreateBean() 方法,调用目标类对象构造器,创建bean,
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        Object cacheKey = this.getCacheKey(beanClass, beanName);
        if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
        	//adviseBeans中存储是否需要为某个Bean生成代理,如果里面存在cacheKey,
			//说明之前已经初始化过Bean的代理类了,此时不需要再次初始化
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
			//标识不代理bean,
			//不代理的条件一:不是 Advice、Pointcut、Advisor、AopInfrastructureBean类的子类(切面类)
            //不代理的条件二:shouldSkip返回 true,默认返回 false
            //不代理的条件三:AspectJAwareAdvisorAutoProxyCreator:应该跳过所有 AspectJPointcutAdvisor指定的增强 bean
            if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
        if (beanName != null) {
            TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
            if (targetSource != null) {
                this.targetSourcedBeans.add(beanName);
                Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
        }
        return null;
    }

postProcessAfterInitialization()后置处理器后置方法

  1. 上方Spring通过执行resolveBeforeInstantiation()并没有创建出bean的代理类,而是判断当前bean是否可以代理增强,将不可以被代理增强的bean存入advisedBeans中并标识为false,然后createBea()方法继续向下执行,调用doCreateBean()方法,调用构造器创建bean
  2. Spring创建bean成功后,执行populateBean()方法,对当前创建的bean的属性赋值,然后执行initializeBean(),获取BeanPostProcessor,执行初始化bean前后要执行的方法postProcessBeforeInitialization()与postProcessAfterInitialization()
  3. 查看 AnnotationAwareAspectJAutoProxyCreator重写的postProcessBeforeInitialization()方法,直接进行了返回没有其它操作
  4. 查看AnnotationAwareAspectJAutoProxyCreator重写的postProcessAfterInitialization()
  1. 该方法中通过advisedBeans获取当前创建的bean是否需要代理增强,不需要则跳过
  2. 需要代理增强的获取增强器(也就是需要切入到当前bean的通知方法)
  3. 修改代理增强标识为已增强
  4. 调用createProxy()方法创建当前bean的代理类
  5. 返回被代理bean
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
            //判断是否已经代理过了
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
 protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
            //判断是否是已经在advisedBeans中集合中,该集合中存放了被标识为不用增强的和已经增强过的bean
        } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
          	//判断是否是切面类  
        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
        	//获取当前bean可以使用的增强器也就是通知方法,并根据前置通知,后置通知...对增强器进行排序(通过切入点匹配监管当前bean的通知方法)
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            if (specificInterceptors != DO_NOT_PROXY) {
            	//记录当前bean 已被增强处理
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                //创建当前 bean 的代理对象
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            } else {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    }

createProxy() 代理类的创建

  1. createProxy()逻辑概述
  1. 首先会创建一个代理工厂ProxyFactory
  2. 增强器(通知方法),设置到ProxyFactory
  3. 执行createAopProxy()创建代理对象,有接口使用JDK动态代理,没有接口使用Cglib 动态代理 ObjenesisCglibAopProxy(config)
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);
        }
		//创建一个代理创建工厂
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.copyFrom(this);
        if (!proxyFactory.isProxyTargetClass()) {
            if (this.shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            } else {
                this.evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }
		//获取监管当前bean的所有增强器通知方法
        Advisor[] advisors = this.buildAdvisors(beanName, specificInterceptors);
        Advisor[] var7 = advisors;
        int var8 = advisors.length;

        for(int var9 = 0; var9 < var8; ++var9) {
            Advisor advisor = var7[var9];
            //将增强器保存到代理工厂中
            proxyFactory.addAdvisor(advisor);
        }

        proxyFactory.setTargetSource(targetSource);
        this.customizeProxyFactory(proxyFactory);
        proxyFactory.setFrozen(this.freezeProxy);
        if (this.advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
		//获取代理对象
        return proxyFactory.getProxy(this.getProxyClassLoader());
    }
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
        	//JDK 动态代理
            return new JdkDynamicAopProxy(config);
        } else {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            } else {
            	//Cglib 动态代理 ObjenesisCglibAopProxy(config)
                return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
            }
        }
    }

4. 目标方法的执行

  1. 在执行目标方法时,获取目标方法的所有增强器通知方法,将增强器包装为 MethodInterceptor 拦截器链,也就是获取所有拦截器,执行时判断目标方法否有拦截器拦截,如果没有拦截器,或执行到了目标方法位置,通过反射,执行目标方法,如果有拦截器,执行拦截器
  2. 实际根据JDK动态代理,Cgilb动态代理,不同的代理方式,底层执行的逻辑不太一样,但是两个代理的实现方式都差不多,创建方法调用链,不同的是jdk的动态代理创建的是ReflectiveMethodInvocation调用链,而cglib创建的是Cglib MethodInvocation。

三. AOP 总结

AOP 的搭建步骤

  1. Spring 中引入 AOP 相关依赖
  2. Spring 设置开启 AOP
  3. 创建 AOP 切面类,将界面类注入到容器中
  4. 切面类中编写通知方法
  5. 设置切入点,将通知方法织组目标方法

AOP 原理

  1. 通过 @EnableAspectJAutoProxy 注解设置 Spring 开启 AOP 了解到,在使用 AOP 时,会向 Spring 容器中注入一个AnnotationAwareAspectJAutoProxyCreator 后置处理器, AnnotationAwareAspectJAutoProxyCreator 间接实现了 InstantiationAwareBeanPostProcessor 与 BeanFactoryAware 接口

InstantiationAwareBeanPostProcessor 继承了 BeanPostProcessor 接口,与 BeanPostProcessor 不同的是,该接口中增加了一个 postProcessBeforeInstantiation() 抽象方法, 该方法是监控在创建bean以前执行的

  1. 分析 AnnotationAwareAspectJAutoProxyCreator 间接实现 BeanFactoryAware 接口, 是为了拿到 Spring 提供的BeanFactory ,因为 Spring 在提供容器时默认提供了一个 ApplicationContextAwareProcessor 后置处理器,注入到了容器中,在后续初始化 bean 时,都会被这个后置处理器监控到执行初始化的方法,判断当前要创建的 bean 是否是InstantiationAwareBeanPostProcessor 类型,如果是执行invoke() 方法,判断当前要创建的 bean 是否实现了某个 Aware 接口,如实现了,调用重写的该接口中的抽象方法获取, 获取Spring 提供的组件,此处获取的是 BeanFactory
  2. 分析 AnnotationAwareAspectJAutoProxyCreator 间接继承 InstantiationAwareBeanPostProcessor 接口,当前这个类就就是一个后置处理器,在该类继承的 AbstractAutoProxyCreator 抽象父类中实现了后置处理器监控 bean 的创建前执行的方法,初始化前执行的方法与初始化后执行的方法
  3. Spring 容器在创建时调用 refresh() 方法刷新容器,方法中创建获取创建 bean 时,首先调用registerBeanPostProcessors() 方法,将直接或间接继承了 BeanPostProcessor 接口的 bean 创建注入到容器中,也就是 IOC 容器在创建注入 bean 时会首先创建注入后置处理器,然后调用 finishBeanFactoryInitialization() 方法创建其他所有单例对象,自此 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器,就先一步获取到 BeanFactory 创建注入到了容器中
  4. 后续再创建其它 bean 时,在creatBean方法中(),首先会调用 resolveBeforeInstantiation(beanName, mbdToUse) 方法,获取所有后置处理器,循环判断是否存在InstantiationAwareBeanPostProcessor 这个类型的后置处理器 AnnotationAwareAspectJAutoProxyCreator 是该类型的实现子类,如果是调用执行创建 bean 以前的方法 postProcessBeforeInstantiation()
  1. postProcessBeforeInstantiation() 创建 bean 以前执行的方法 : 该方法在 AOP 相关的处理中并没有太多实际的动作,只是判断当前 bean 是否是切面类,是否是增强器(也就是切面类中的通知方法,在Spring 容器启动时,会将切面中的通知方法封装成一个个的增强器),是否允许被增强,如果不允许增强,将当前类放入一个集合中 advisedBeans,中并标识为false,不允许增强
  2. postProcessBeforeInitialization() 初始化 bean 以前执行的方法,该方法在此处没有任何操作,直接进行了返回
  3. postProcessAfterInitialization() 初始化 bean 后执行的方法,该方法中调用判断当前要创建的 bean 是否允许增强处理,是否已经被增强处理,创建ProxyFactory代理类工厂,通过切入点匹配获取监管当前 bean 的所有增强器(通知方法)将获取到的通知方法存入代理工厂中,通过 JDK 动态代理,或 Gglib 动态代理生成代理对象
  1. 执行 在执行目标方法时,获取目标方法的所有增强器通知方法,将增强器包装为 MethodInterceptor 拦截器链,如果有拦截器执行拦截器,如果没有通过反射执行目标方法?

AOP问题点

SpringBoot AOP的动态代理
  1. Spring中AOP会判断是否有接口,有接口使用JDK动态代理否则使用Cglib动态代理
  2. 查看SpringBoot AOP配置类为AopAutoConfiguration,该配置类中通过@ConditionalOnProxperty注解指定如果spring.aopproxy-target-class不指定,默认值为true,使用Cglib动态代理,如果指定spring.aopproxy-target-class=false才会判断是否有接口,有接口使用JDK动态代理否则使用Cglib动态代理
AOP 底层使用的什么机制
  1. 动态代理
AOP 常见的概念名词
  1. 切面: 指切面类,管理切入点与通知方法
  2. 连接点: 需要增强的方法就是连接点
  3. 切入点: 由切入点来哪些方法需要增强,哪些方法不需要增强,@Pointcut指定切点表达式
  4. 通知: 就是需要加入到业务方法中的公共代码,其中包含
  1. 前置通知 @Before 目标方法执行前运行
  2. 后置通知 @After 目标方法执行完毕,或抛出异常时运行
  3. 返回通知 @AfterReturning 目标方法正常返回之后运行
  4. 异常通知 @AfterThrowing 目标方法发生异常时运行
  5. 环绕通知 @Around
  1. 目标对象: 增强的对象,需要增强的方法所在类
  2. 织入: AspectJ独有的,Spring使用的织入方式是动态代理,也就是为目标对象创建动态代理的过程
  3. 顾问: 是通知的一种包装体现,是切入点与通知的一个结合,用来管理切入点与通知,应用中无需关心
  4. 引入: 可以将其它接口和实现动态引入到targetClass中
Spring AOP与AspectJ AOP有什么区别

Spring中要使用AOP时需要引入AspectJ AOP相关依赖,SpringAOP提供了AspectJ AOP的支持,但是只用到了AspectJ的切入点解析和匹配,AOP基于代理模式实现,分为动态代理与今天代理,静态代理表示AspectJ,动态代理表示SpringAOP,是主要区别:

  1. SpringAOP基于动态代理实现,如果目标类使用的接口则通过JDK动态代理实现,如果没有使用Cglib实现
  2. AspectJ是基于静态代理,在编译阶段生成AOP代理类,因此又被称为编译时增强
JDK动态代理与Cglib动态代理的区别
  1. 首先解释一下Spring怎么实现动态代理: 当Spring中的Bean有接口时,Spring则使用JDK动态代理,当被代理Bean没有实现接口时,Spring使用Cglib动态代理,也可以强制使用Cglib代理,在Spring配置文件中添加
    < aop:aspectj-autoproxy proxy-target-cass=“true”/>
  1. jdk动态代理: 实现 InvocationHandler 接口,重写invoke方法,通过Proxy.newProxyInstance()获取代理对象
  2. cglib动态代理: 需要引入依赖 ,实现MethodInterceptor 接口,通过 重写intercept()方法实现代理执行目标类方法
  1. JDK动态代理
  1. JDK动态代是基于反射实现的,被代理类需要有接口,被代理执行的方法要在接口中,并且可以被重写,否则不能动态代理,运行时JDK动态的为目标接口创建一个$proxy*.class,调用时先执行处理类进行增强,再通过反射调用目标方法
  2. 动态代理中的重点主要是通过Proxy.newProxyInstance()方法在内存中动态生成代理对象,通过InvocationHandler接口中的invoke()方法,通过反射,执行被代理的方法,根据两种不同方式的调用执行,区分实现InvocationHandler接口,与通过接口的匿名实现方式设计JDK动态代理的不同
  1. Cglib动态代理
  1. 底层通过ASM字节码,在运行时动态的生产目标类的子类,该方式是针对类的动态代理,通过子类覆盖父类,由于是子类继承父类的方式该类中的方法不可以使用final,static修饰,Cglib动态代理时除了会生成代理类以外还会生成一个FastClass类,通过这个FastClass解决了JDK动态代理本类方法调用增强失效的问题(Spring中不管是JDK动态代理,还是Cglib,本类调用代理方法增强都会失效)
  1. 那么JDK与Gglib动态代理有什么优缺点: JDK生成速度快调用慢,Cglib生成速度慢后续调用快,JDK好像进行了优化,JDK78版本性能比CGLIB好20%左右
如何强制使用Cglib动态代理
  1. xml方式: < aop:aspectj-autoproxy proxy-target-cass=“true”/>
  2. 注解方式@EnableAspectJAutoProxy时指定proxyTargetClass=true
AOP什么情况下代理失效,如何解决
  1. 常见失效原因: 方法是private权限,目标类没有配置为bean交给Spring管理,切入点配置问题,重点关注本类方法调用失效
  2. 本类中调用会失效,两种解决方式
  1. 自己注入自己的接口, 通过自己注入的发起调用
  2. 设置@EnableAspectJAutoProxy中的exposeProxy属性为true,表示在线程中暴露代理对象,底层会将动态代理对象存到ThreadLocal中,然后在方法中通过AopContext.currentProxy()方法获取当前线程中的代理对象,通过这个代理对象调用目标方法
Spring AOP是在哪里创建的动态代理
  1. 正常情况下动态代理的执行
  1. 首先在resolveBeforeInstantiation()方法中判断创建的bean是否需要代理增强,存到advisedBeans中
  2. doCreate(),populateBean实例化,属性赋值后,执行initializeBean()初始化bean获取BeanPostProcessor,执行postProcessAfterInitialization()时进行的动态代理
  3. postProcessAfterInitialization中会调用一个wrapIfNecessary(),根据切入点表达式进行匹配,匹配成功创建ProxyFactory,执行createAopProxy()创建代理对象
  1. 在属性注入时创建动态代理,在属性注入时如果发生循环依赖,当调用getEarlyBeanRefrence()通过缓存获取对象时,该方法中也会调用wrapIfNecessary()尝试创建代理对象,并且在wrapIfNecessary()方法中也会执行earlyProxyReferences.remove()方法,将属性注入发生循环依赖,已经动态代理过的对象删除掉,防止后续初始化时重复创建代理对象
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring AOP 的搭建与源码分析 的相关文章

随机推荐

  • Angular4基础开发文档

    Angular4基础开发文档
  • netstat命令详解

    命令介绍 netstat命令用于显示与IP TCP UDP和ICMP协议相关的统计数据 一般用于检验本机各端口的网络连接情况 netstat是在内核中访问网络及相关信息的程序 它能提供TCP连接 TCP和UDP监听 进程内存管理的相关报告
  • java/php/net/pythonMES生产线控制系统设计

    本系统带文档lw万字以上 答辩PPT 查重 如果这个题目不合适 可以去我上传的资源里面找题目 找不到的话 评论留下题目 或者站内私信我 有时间看到机会给您发 生产线控制系统 的设计主要是为了满足生产线管理员的实际需求 因此 它需要通过Int
  • 移动应用开发期末总结

    移动应用开发 什么是intent 问答题 Intent是一个动作的完整描述 包含了动作的产生组件 接收组件和传递的数据信息 Intent为Activity Service和BroadcastReceiver等组件提供交互能力 将一个组件的数
  • 使用Modelarts快速开发Hilens Kit实现人脸识别功能

    导语 在华为云平台上线的Modelarts模型训练平台结合华为智能终端产品Hilens kit 对Hilens Kit进行开发 实现产品的快速使用以及功能的实现 自从2020年疫情开始 使得人与人的接触变得更加不方便 间接促使了人工智能产业
  • Java中9种常见的CMS GC问题分析与解决

    目前 互联网上 Java 的 GC 资料要么是主要讲解理论 要么就是针对单一场景的 GC 问题进行了剖析 对整个体系总结的资料少之又少 前车之鉴 后事之师 美团的几位工程师历时一年多的时间 搜集了内部各种 GC 问题的分析文章 并结合个人的
  • unity开发小贴士之三 UGUI-Lua Component回收

    ugui tolua local test test b gameobjecttest c gameobject GetComponent typeof UnityEngine UI Button 首先调用UnityEngine GameO
  • Java Logging

    最后一次实验要求用日志来记录信息 学习的内容整理如下 Java 中的 Logging API 让 Java 应用可以记录不同级别的信息 它在debug过程中非常有用 如果系统因为各种各样的原因而崩溃 崩溃原因可以在日志中清晰地追溯 日志工作
  • 在小程序中使用图标

    因为最近在自学微信小程序 掌握了他的基础的使用 包括小程序的语法 小程序的自有组件 小程序的自有API及小程序的自定义组件 在学玩以上的各方面的知识体系后 我就想着学了这么就的微信小程序 自己总要写出点什么东西来才对的起自己这段时间来的努力
  • Android退出应用程序方法总结

    Android退出应用程序方法总结 在Android开发中 我们运行了应用程序后 都需要退出应用的 那么该如何退出应用 又都有哪些实现方式呢 今天就为大家整理分享一些退出应用程序的方法 一起来看看吧 更新内容 Ver v1 任务管理器方法补
  • 简短的char*与char[]

    include
  • 这就是搜索引擎——索引压缩

    对于海量数据 建立倒排索引往往需要较大的磁盘空间 尤其是一些常见的单词 这些单词对应的倒排列表可能有几百兆 如果搜索引擎在相应用户查询的时候 用户查询包含了常见的单词 就需要将大量的倒排列表信息从磁盘读入内存 由于磁盘读写速度往往是个瓶颈
  • RLHF 技术:如何能更有效?又有何局限性?

    编者按 自ChatGPT推出后 基于人类反馈的强化学习 RLHF 技术便成为大模型构建和应用人员关注的热点 但该方法一些情况下效果却差强人意 有些基础模型经RLHF调优后反而表现更差 RLHF技术的适用性和具体操作细节似乎成谜 这篇文章探讨
  • 平板电脑黑苹果EFI_保姆级别教你安装黑苹果,提供大量EFI与工具驱动!

    最近无聊 在电脑上装了个黑苹果 可能是我的机型比较好找吧 安装的过程中没有遇到太大的问题 接下教大家安装 也为大家准备了大量的EFI N卡驱动 蓝牙驱动 键盘驱动等工具 需要工具的小伙伴可以点击此处 所需要工具 1 黑果镜像 2 U盘 3
  • Python音视频剪辑库MoviePy1.0.3中文教程导览及可执行工具下载

    前往老猿Python博文目录 一 简介 MoviePy是一个用于视频编辑的Python模块 可用于进行视频的基本操作 如剪切 拼接 标题插入 视频合成 也称非线性编辑 视频处理或创建高级效果 它可以读写最常见的视频格式 MoviePy能处理
  • Xshell连接Ubuntu详细过程

    一 打开虚拟机VMware 二 启动虚拟机 输入密码 进入Ubuntu系统界面 三 鼠标右键 选择 在终端打开 出现终端对话框 四 在命令行输入 sudo apt get install openssh server 安装openssh s
  • 2017校招Java开发笔试题集

    美丽联合 二分查找要求结点 A 有序 顺序存储 B 有序 链接存储 C 无序 顺序存储 D 无序 链接存储 答案选 A 引入线索二叉树的目的是 A 加快查找结点的前驱或后继结点的速度 B 为了能在二叉树中方便插入和删除 C 为了能方便找到双
  • 使用STM32CubeMx配置STM32输入捕获功能

    输入捕获原理 在输入捕获模式下 当检测到ICx信号上相应的边沿后 计数器的当前值被锁存到捕获 比较寄存 器 TIMx CCRx 中 当发生捕获事件时 相应的CCxIF标志 TIMx SR寄存器 被置1 如果开放 了中断或者DMA操作 则将产
  • tools:context=".MainActivity的作用

    http blog csdn net caiwenfeng for 23 article details 8373569
  • Spring AOP 的搭建与源码分析

    AOP 面向切面编程 一 AOP 概述 AOP 的实现步骤 示例 二 通过 EnableAspectJAutoProxy 了解 AOP 原理 1 分析 EnableAspectJAutoProxy 2 复习 Spring 容器启动后 bea