zoukankan      html  css  js  c++  java
  • Spring源码分析-SpringAop什么时候调用jdk动态代理?什么时候调用cglib

    1.导入log4j.jar,开启log4j DEBUG模式

    2.查看打印日志,可以发现一个重要信息:

    2020-03-03 15:13:31,870 DEBUG [org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator] - Creating implicit proxy for bean 'userService' with 0 common interceptors and 4 specific interceptors
    

    spring再执行aop时,执行了 AnnotationAwareAspectJAutoProxyCreator 类中的某个方法。

    备注:有时候通过打印日志,可以快速了解代码执行了哪些过程。

     
    3.通过打印日志提示的类 AnnotationAwareAspectJAutoProxyCreator 及“Creating implicit proxy”找到最终执行的是其父类 AbstractAutoProxyCreator 中的 protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) 方法

    4. 重新在 buildAdvisors 这个方法打断点进行调试

    5.AbstractAutoProxyCreator 类中发现有一个重要的方法createProxy 如下:

    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 (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            }
            else {
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }
    
        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        proxyFactory.addAdvisors(advisors);
        proxyFactory.setTargetSource(targetSource);
        customizeProxyFactory(proxyFactory);
    
        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }
        // 核心代码
        return proxyFactory.getProxy(getProxyClassLoader());
    }

    接着进入 ProxyFactory 中的 getProxy 方法

    public Object getProxy(ClassLoader classLoader) {
        // createAopProxy 方法中判断是选择jdk动态代理还是cglib方式代理
        return createAopProxy().getProxy(classLoader);
    }

    再进入到 ProxyCreatorSupport 中的 createAopProxy 方法

    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        // getAopProxyFactory() 返回的是 DefaultAopProxyFactory对象,所以重点还是在 createAopProxy 方法上
        return getAopProxyFactory().createAopProxy(this);
    }

    6.进入到 DefaultAopProxyFactory 类中的 createAopProxy 方法,这个方法就是如何选择JDK动态代理还是Cglib代理

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 1.config.isProxyTargetClass() 代表 配置中的proxy-target-class属性true/false,默认false
        // 
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            // 目标代理类,如 com.service.impl.UserServiceImpl
            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.");
            }
            // 目标类如果是一个接口 或者 (Proxy是JDK动态代理用到的一个类)也就是说这里表示目标类是否为这个Proxy 类型
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

     这段代码是确定Spring选择JDK动态代理还理CGLIB代理的核心,

    • config.isOptimize:表示是否使用了优化策略,配置的属性optimize值决定;
    • config.isProxyTargetClass:表示是否是代理目标类,配置的属性proxy-target-class值决定;
    • hasNoUserSuppliedProxyInterfaces:就是在判断代理的对象是否有实现接口

    总结: 

    1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
    2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
    3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

    从这个逻辑也可以间接看出spring推荐使用JDK动态代理,因为高版本的JDK中,至少JDK1.6以上JDK动态代理的效率远高于CGLIB(无论是单例模式还是多例模式)。但是如果代理的类没有相应的接口,此时只能使用CGLIB代理了,例如代理模式应用在POJO实体类上,或者是说你的JDK版本较低,这时你可以手动设置CGLIB代理。

     方法中参数config 是一个 AdvisedSupport类型, AdvisedSupport 继承了 ProxyConfig 类,

    public class ProxyConfig implements Serializable {
        // 代表 配置中的proxy-target-class,如果设置true,则使用Cglib方式代理
        private boolean proxyTargetClass = false;
       // 是否优化
        private boolean optimize = false;
    
        boolean opaque = false;
    
        boolean exposeProxy = false;
    
        private boolean frozen = false;
    
        // 省略部分代码
        
    }
  • 相关阅读:
    Jenkins可用环境变量列表以及环境变量的使用(Shell/Command/Maven/Ant)
    CreateJS结合Falsh工具生成Canvas动画(加密字符串的由来)
    Linux下使用mv重命名文件或者移动文件(增强版的工具为rename)
    Windows7/8/10中无法识别USB设备的问题解决
    Eclipse工程中Java Build Path中的JDK版本和Java Compiler Compiler compliance level的区别(转)
    使用Docker部署Spring boot项目
    豆瓣API
    scrapy
    elasticsearch x-pack
    Document
  • 原文地址:https://www.cnblogs.com/caoxb/p/12406005.html
Copyright © 2011-2022 走看看