美文网首页
Spring 4.3 源码分析之 配置式Aop (一)

Spring 4.3 源码分析之 配置式Aop (一)

作者: 爱吃鱼的KK | 来源:发表于2018-02-10 23:16 被阅读47次
    1. Spring Aop 配置式Aop Demo

    上篇中通过手动写代码, 将 Pointcut, MethodInterceptor, ProxyFactory, TargetBean 组装起来, 最终通过 ProxyFactory.getProxy() 来获取代理的对象; 而本篇将结合 Spring 中的 FactoryBean 结合起来, 只需要在 xml 中配置少许 信息就能对目标对象生成代理对象, 配置的信息如下:

    <!-- Simple target -->
    <bean id="kk" class="com.lami.foodie.aop.aop1.KK"></bean>
    
    <!-- MethodInterceptor -->
    <bean id="simpleMethodInterceptor" class="com.lami.foodie.aop.aop1.SimpleMethodInterceptor"/>
    
    <!-- ProxyFactoryBean -->
    <bean id="test1" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces"><value>com.lami.foodie.aop.aop1.KK</value></property>
        <property name="target"><ref bean="kk" /></property>
        <property name="interceptorNames"><value>simpleMethodInterceptor</value></property>
    </bean>
    

    PS: 上面配置文件对应的类在上篇中已存在

    2. Spring Aop 配置式Aop 组件 ProxyFactoryBean

    ProxyFactoryBean 是通过 FactoryBean 来针对指定的 targetBean 来创建动态代理对象 (PS: FactoryBean 是 Spring IOC 中针对创建指定对象的抽象工厂, 毕竟有些类的创建过程是很复杂的, 所以出现了 FactoryBean 这个角色), 其主要有一下属性:

    /**
     * This suffix in a value in an interceptor list indicates to expand globals.
     */
    public static final String GLOBAL_SUFFIX = "*";
    
    protected final Log logger = LogFactory.getLog(getClass());
    
    // 从字面意思上, 下面是拦截器, 其实不然, 下面支持获取指定前缀的 advice/MethodInterceptor, 当然也可以在下面的数组的最后一位设置 target 的名字 (PS: 这在checkInterceptorNames中解析)
    private String[] interceptorNames;
    
    // 代理的目标类 target, 这个 String 主要是通过xml 设置, 或通过 checkInterceptorNames 方法上
    private String targetName;
    
    // 自动获取 target 上的 interface
    private boolean autodetectInterfaces = true;
    
    // PS: 这里的 singleton 指的是 advice 是否是 singleton
    private boolean singleton = true;
    
    // 前置 Spring aop 中主要使用的是 MethodInterceptor, 而在 xml 中配置的 <aop> 标签中解析出来的都是 advice, 这时需要对应的适配器 -> 将 advice 适配成 MethodInterceptor
    // 这里的 advisorAdapterRegistry 中主要是将 advice, MethodInterceptor 包装成 Advisor, 与将 Advisor 转成 MethodInterceptor
    private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
    
    // 设置是否 动态代理的配置信息是否变化
    private boolean freezeProxy = false;
    
    // 创建动态代理时使用的 classLoader
    private transient ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader();
    
    // 配置是否已经配置了 classLoader
    private transient boolean classLoaderConfigured = false;
    
    // 创建动态时使用的  BeanFactory
    private transient BeanFactory beanFactory;
    
    // 标示动态代理中的 advisorChain 链是否已经创建好
    /** Whether the advisor chain has already been initialized */
    private boolean advisorChainInitialized = false;
    
    // 这个类其实就是动态代理创建的最终对象
    /** If this is a singleton, the cached singleton proxy instance */
    private Object singletonInstance;
    

    以上这些属性中最重要的还是 interceptorNames(拦截器链), targetName(代理的类), 将 advice/advisor 转换成 MethodInterceptor 的适配器注册工厂 advisorAdapterRegistry, 有了以上的属性, 则再通过下面的的两个方法就能创建代理对象:

    public Object getObject() throws BeansException {
        // 这里初始化通知器链
        initializeAdvisorChain();
        // 这里对 Singleton prototype 的类型进行区分, 生成对应的 Proxy
        if (isSingleton()) {
            return getSingletonInstance();
        }
        else { // prototype 模式则每次创建一个类
            if (this.targetName == null) {
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }
            return newPrototypeInstance();
        }
    }
    
    // 动态代理对象 singletonInstance
    private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            this.targetSource = freshTargetSource();                // 返回对应的 target
            // 自动获取需要动态代理的接口
            if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                // Rely on AOP infrastructure to tell us what interfaces to proxy.
                Class<?> targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }
                // 这里设置代理对象的接口
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));     // 获取 targetClass 的所有的 interface
            }
            // Initialize the shared singleton instance.
            super.setFrozen(this.freezeProxy);
            // 注意这里的方法将会使用 ProxyFactory 来生成
            // 通过 createAopProxy 返回 合适的 AopProxy(JdkDynamicAopProxy, CglibAopProxy), 并将 AopProxy 传如 getProxy 方法中进行动态代理
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }
    
    3. Spring Aop 配置式Aop ProxyFactoryBean 创建对象流程

    整个创建代理对象的整个过程是在 ProxyFactoryBean 的 getObject 方法中触发的, 大体流程如下:

    1. 初始化 AdvisorChain
        1.1 循环遍历 interceptorNames, 从 BeanFactory 中获取对应 Advice, MethodInterceptor, 通过 AdvisorAdaptorRegistry 包装成 Advisor 加入到 AdvisorChain 里面
    2. 调用 getSingletonInstance方法, 创建动态代理对象
        2.1 根据 targetName 从 BeanFactory 中拿到对应对象
        2.2 获取 target 类实现的所有接口 <- 为代理这些接口准备数据
        2.3 DefaultAopProxyFactory 根据 AdvisedSupport 来创建合适的 AopProxy (JdkDynamicAopProxy, CglibAopProxy) , 判断的条件主要是 optimize, targetClass, hasNoUserSuppliedProxyInterfaces
        2.4 Aop 根据已经获取到的 Advisor, Target 等信息创建代理对象
    (PS: 其实理解了上篇的编程式 Aop, 则这里的 ProxyFactoryBean 就很好理解了; 另外插一下 aop 很重要, Spring 里面的很多特性都是基于 aop, 比如 事务, 缓存, Hystrix 等)
    
    4. 总结:

    本篇讲述通过 ProxyFactoryBean 来创建代理对象, 整个创建过程其实和上篇差不多, 也就是说 ProxyFactoryBean 只是做了 MethodInterceptor 的自动获取, 以及 通过 ProxyFactory 创建代理对象, 而问题也在 ProxyFactoryBean 中, 从这个类中我们发现, 只要需要创建一个动态代理对象, 则就需要配置一个 ProxyFactoryBean <-- 而在一般项目中需要创建很多动态代理对象, 可想而知这是多么可怕的事情, 所以就出现了 AbstractAutoProxyCreator(自动获取 BeanFactory 中的所有 Advice/MethodInterceptor, 并针对所有符合 Pointcut 的对象创建代理对象, 这也是下篇将叙述的内容)。

    5. 参考:

    Spring Aop核心源码分析
    Spring技术内幕
    Spring 揭秘
    Spring 源码深度分析
    开涛 Spring 杂谈
    伤神 Spring 源码分析
    Spring源码情操陶冶

    相关文章

      网友评论

          本文标题:Spring 4.3 源码分析之 配置式Aop (一)

          本文链接:https://www.haomeiwen.com/subject/xjamzxtx.html