首页 > 编程语言 >【Spring AOP】【六】Spring AOP源码解析-代理对象创建过程

【Spring AOP】【六】Spring AOP源码解析-代理对象创建过程

时间:2023-02-21 08:33:05浏览次数:44  
标签:return Spring Advisor 代理 源码 proxyFactory AOP new config

1  前言

我们看过Spring对AOP配置的解析以及通知器的筛选,也看过了动态代理的实现,这节那我们就看下Spring创建代理的过程,为下节看代理具体的执行过程做铺垫哈。

那我们从哪看起呢?还记得创建代理的那个切入时机么?也就是这里

那么让我们开始看createProxy。

2  源码分析

2.1  方法通读

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    // 略过 暂时先不看
    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }
    // 创建了ProxyFactory代理工厂 看来我们的代理应该是交给这个工厂给我们生成了
    ProxyFactory proxyFactory = new ProxyFactory();
    // proxyFactory属性初始化  告诉他应该用什么样的代理方式 比如是jdk还是cglib
    proxyFactory.copyFrom(this);
    /**
     * 下边一大段都是在判断是否要将proxyTargetClass设置为true 但是不管设置的是true还是false 最后都是由DefaultAopProxyFactory进行代理创建决定用哪种方式创建 最后看这个方法
     * proxyTargetClass 这个属性的的默认值是false 但是通过上边的copyFrom 值已经被覆盖了一次 而上边的值我们可以
     *             @EnableAspectJAutoProxy(proxyTargetClass = true)
     *             <aop:aspectj-autoproxy proxy-target-class="true" /> 通过这两种方式更改  默认当然也是为false
     */
    if (proxyFactory.isProxyTargetClass()) {
        // Explicit handling of JDK proxy targets and lambdas (for introduction advice scenarios)
        if (Proxy.isProxyClass(beanClass) || ClassUtils.isLambdaClass(beanClass)) {
            // Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
            for (Class<?> ifc : beanClass.getInterfaces()) {
                proxyFactory.addInterface(ifc);
            }
        }
    }
    else {
        // 又是在判断是否需要讲setProxyTargetClass = true     具体我没看哈 主要是我不知道他判断的依据是个什么原理
        // No proxyTargetClass flag enforced, let's apply our default checks...
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            // 还是在判断是否需要讲setProxyTargetClass = true 具体我没看哈 主要是我不知道他判断的依据是个什么原理
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
    /**
     * 构建一个 Advisor 列表 问:specificInterceptors是个啥?
     * 答:getAdvicesAndAdvisorsForBean()方法的结果 就是当前bean筛选后的通知器Advisors
     * 问:Advisor是个啥? 答:可以理解为就是切点+通知
     * buildAdvisors估计是要再进行加工一下 猜的哈
     */
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // 设置proxyFactory工厂的通知其数组
    proxyFactory.addAdvisors(advisors);
    // 设置proxyFactory工厂的targetSource
    proxyFactory.setTargetSource(targetSource);
    // 空方法用于自定义扩展
    customizeProxyFactory(proxyFactory);
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
    // 加载器的变换 略过
    // Use original ClassLoader if bean class not locally loaded in overriding class loader
    ClassLoader classLoader = getProxyClassLoader();
    if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
        classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
    }
    // 调用工厂创建代理对象
    return proxyFactory.getProxy(classLoader);
}

看完创建代理的方法,大概总结下执行的几个关键步骤:

  1. 创建代理工厂对象
  2. 代理工厂属性初始化
  3. 判断是否将工厂的proxyTargetClass设置为true
  4. buildAdvisors根据bean名称和bean筛选后的通知器重新打包构建通知其数组
  5. 调用工厂创建代理对象

那么接下来我们重点看下4、5两个步骤。

2.2  buildAdvisors

/**
 *
 * @param beanName bean名称
 * @param specificInterceptors 筛选后的通知器
 * @return
 */
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
    // 公共的通知器
    Advisor[] commonInterceptors = resolveInterceptorNames();
    // 将参数的通知器和公共的通知器都合并到allInterceptors
    List<Object> allInterceptors = new ArrayList<>();
    if (specificInterceptors != null) {
        if (specificInterceptors.length > 0) {
            allInterceptors.addAll(Arrays.asList(specificInterceptors));
        }
        if (commonInterceptors.length > 0) {
            if (this.applyCommonInterceptorsFirst) {
                allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
            }
            else {
                allInterceptors.addAll(Arrays.asList(commonInterceptors));
            }
        }
    }
    if (logger.isTraceEnabled()) {
        int nrOfCommonInterceptors = commonInterceptors.length;
        int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
        logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
                " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
    }
    Advisor[] advisors = new Advisor[allInterceptors.size()];
    for (int i = 0; i < allInterceptors.size(); i++) {
        // wrap对每个通知器进行打包,那我们看下wrap
        advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
    }
    return advisors;
}

里边事情比较简单,就是把参数的通知器和公共的通知器进行合并,然后对每个通知器进行wrap,那我们看下wrap里都干了些什么。

private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
// 默认的3个适配器
/**
 * Create a new DefaultAdvisorAdapterRegistry, registering well-known adapters.
 */
public DefaultAdvisorAdapterRegistry() {
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
@Override
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    // 本身是不是Advisor 是的话不需要再装饰了 直接返回
    if (adviceObject instanceof Advisor) {
        return (Advisor) adviceObject;
    }
    // 不是Advice通知,直接报错
    if (!(adviceObject instanceof Advice)) {
        throw new UnknownAdviceTypeException(adviceObject);
    }
    Advice advice = (Advice) adviceObject;
    // 是Advice 并且是个MethodInterceptor方法型拦截器 就用DefaultPointcutAdvisor给他包起来
    if (advice instanceof MethodInterceptor) {
        // So well-known it doesn't even need an adapter.
        return new DefaultPointcutAdvisor(advice);
    }
    // 否则 通过适配器进行转换 上边有3个默认的适配器
    for (AdvisorAdapter adapter : this.adapters) {
        /**
         * 调用adapter的supportsAdvice方法,判断遍历到的适配器是不是与当前的拦截器适配,
         * 如果得到结果true,则将拦截器封装为 DefaultPointcutAdvisor 返回。
         * 如果adapters中没有能够预知适配的适配器,则表示无法将当前的拦截器包装成 Advisor,会在wrap方法的最后抛出异常。
         */
        if (adapter.supportsAdvice(advice)) {
            return new DefaultPointcutAdvisor(advice);
        }
    }
    throw new UnknownAdviceTypeException(advice);
}

主要是根据类型进行做了判断,如果本身就是Advisor就直接返回了,是Advice的话通过三个适配器进行匹配转换,如果转换失败或者不是Advisor和Advice类型的话直接报错结束,好了,接下来我们看下创建代理。

2.3  proxyFactory.getProxy(classLoader)创建代理

/**
 * createAopProxy().getProxy(classLoader) 慢点看
 * createAopProxy() 返回的是JdkDynamicAopProxy或者ObjenesisCglibAopProxy对象 也就是jdk代理或者cglib代理
 * 然后调用他们的getProxy返回代理对象哈
 * @param classLoader
 * @return
 */
public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
}
/**
 * Create a new ProxyCreatorSupport instance.
 */
public ProxyCreatorSupport() {
    // 默认工厂DefaultAopProxyFactory
    this.aopProxyFactory = new DefaultAopProxyFactory();
}
/**
 * Return the AopProxyFactory that this ProxyConfig uses.
 */
public AopProxyFactory getAopProxyFactory() {
    return this.aopProxyFactory;
}
/**
 * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
 * create an AOP proxy with {@code this} as an argument.
 */
protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    // 默认工厂createAopProxy
    return getAopProxyFactory().createAopProxy(this);

最后其实就是调用DefaultAopProxyFactory的getAopProxyFactory(),来返回一个jdk类对象或者cglib对象,那么让我们来看下。

/**
 * 最后由这家伙 决定是创建jdk还是cglib的代理
 * @param config the AOP configuration in the form of an
 * AdvisedSupport object
 * @return
 * @throws AopConfigException
 */
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    /**
     * 一个一个条件看哈
     * NativeDetector.inNativeImage() 这个是啥   用于判断当前程序是否运行在GraalVM上(类似JVM),是否使用GVM编译,一般为false;如果是着这种情况,就只能只用JDK动态代理
     * config.isOptimize(),这里的config是方法参数传入的,其实就是之前创建的 ProxyFactory 工厂对象,它的optimize属性是 ProxyFactory 创建后,通过copyFrom方法从后处理器中复制的属性值,它的默认值是false。
     * config.isProxyTargetClass():是否是基于类的代理,如果想使用cglib代理,则设此值为true,但是不一定设置了就能用,你看下边还有个判断
     * hasNoUserSuppliedProxyInterfaces():如果存在一个接口,还是SpringProxy类型的,就返回true,否则就是false,自定义的肯定是false,如果没接口也是false。一般为false;
     */
    if (!NativeDetector.inNativeImage() &&
            (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
        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.");
        }
        /**
         * 目标类是接口 或者是本身是个代理类 或者是个lambda表达式 就用jdk代理
         */
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

最后就是这个方法决定是创建什么样的代理,看网上说的proxyTargetClass只要置为true就必须用cglib代理么,可能是版本不同哈,我这个版本的你看下边还有个判断,如果是接口它还是走的jdk代理了哈。

2.4  JDK动态代理的getProxy()和实例化方法

我暂时只看了JDK动态代理的哈,cglib的暂时没看。

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
    Assert.notNull(config, "AdvisedSupport must not be null");
    if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
        throw new AopConfigException("No advisors and no TargetSource specified");
    }
    this.advised = config;
    this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
}
@Override
public Object getProxy() {
    return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
    }
    /**
     * 是不是很熟悉的感觉
     * 三个参数 类加载器 接口数组proxiedInterfaces 还有InvocationHandler实现 
     * 接口数组咋来的的? 实例化的时候是不是传了AdvisedSupport 这个里边是不是那会创建过程里有我们的advisors、接口等信息 是不是 想不起来的话回去再看一遍哈
     * InvocationHandler实现  它传的自己说明它实现了InvocationHandler 就有invoke方法 就在下边是不是 我们下节讲通知器链执行过程的时候 会分析这个invoke方法
     */ 
    return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
} 

3  小结

好了,创建代理的过程我们也看了,主要是看了JDK的创建过程哈,现在就剩下invoke里的东西了,也就是我们的通知器数组是怎么串联起来执行的,下节我们讲这个。

理解哪里不对的地方,欢迎指正哈,加油。

 

标签:return,Spring,Advisor,代理,源码,proxyFactory,AOP,new,config
From: https://www.cnblogs.com/kukuxjx/p/17139596.html

相关文章

  • 基于 springboot + mybatis-plus + MySQL 重构过去一个项目的踩坑总结(持续更新)
    ①使用mybatis-plus时,如果根据某个条件查询没有查到数据,那么返回的实体类是null,如果使用get方法就会导致空指针异常,这点要规避②如果Map初始化在循环体外,那么在循......
  • SpringBoot 配置 HTTPS 安全证书的两种方案
    使用JDK自带的工具生成证书使用FreeSSL提供的证  使用JDK自带的工具生成证书确保安装了JDK并正确配置了环境变量;进入你的JAVA_HOME目录中的bin目录;在这个目......
  • spring中处理json
    1.使用fastJson2.controller层@PostMapping("")@ResponseBodypublicBaseResponsejsonFunction(@RequestBodyStringjsonData){BaseResponsebaseResponse=ne......
  • 若依升级spring boot3过程
    一、为何要升级到springboot3?因为新发布的springboot3本身就支持springnative了,意味着可以用更小的内存和更快的启动速度,而更小的内存意味着服务器可以运行更多的项目,......
  • SpringBoot02 - 基础配置
    SpringBoot基础配置1.属性配置​ SpringBoot通过配置文件application.properties就可以修改默认的配置,那咱们就先找个简单的配置下手,当前访问tomcat的默认端口是8080,好熟......
  • SpringBoot03 - 整合JUnit
    整合JUnit​ SpringBoot技术的定位用于简化开发,再具体点是简化Spring程序的开发。所以在整合任意技术的时候,如果你想直观感触到简化的效果,你必须先知道使用非SpringBoot技......
  • SpringBoot04 - 整合MyBatis
    整合MyBatis​ 整合完JUnit下面再来说一下整合MyBatis,这个技术是大部分公司都要使用的技术,务必掌握。如果对Spring整合MyBatis不熟悉的小伙伴好好复习一下,下面列举出原始......
  • SpringBoot01 - 入门案例
    1.SpringBoot入门程序制作(一)​ 下面让我们开始做第一个SpringBoot程序吧,本课程基于Idea2020.3版本制作,使用的Maven版本为3.6.1,JDK版本为1.8。如果你的环境和上述环境不同,......
  • day15-SpringMVC执行流程
    SpringMVC执行流程1.SpringMVC执行流程分析图例子(1)创建HaloHandlerpackagecom.li.web.debug;importorg.springframework.stereotype.Controller;importorg.s......
  • MyBatis-RedisCache源码分析
    回顾在前面,我们通过redis​集成了MyBatis​的二级缓存,440.MyBatis的二级缓存整合redis,接下来,我们来分析一下RedisCache​的源码。源码分析RedisCache主要是通过......