`
agapple
  • 浏览: 1584205 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

spring的auto-proxy自动代理(融合机制实现)

    博客分类:
  • java
阅读更多

背景

最近在实施并行加载,遇到一个问题: 重复代理,或者说是两次cglib代理。

主要是并行加载技术本身是采用了cglib+拦截的技术进行控制,所以势必会要求进行一次代理配置那

1. 如果需要代理的原始对象已经是一个cglib代理后的对象,比如性能监控,日志记录等等。

2. 其他同事在做的自动路由,按需加载都会要求进行一次cglib代理

 

如何平衡多次代理的问题,就冒出来了。

思路

接近于spring的autoProxyCretor的一套机制,利用了BeanPostProcessor,就是在bean的生命周期上做点文章。

 

spring默认提供的几种auto-proxy: 

 

  • BeanNameAutoProxyCreator   :  可以配置需要被进行auto-proxy的bean names列表,它控制的是需要代理的bean列表
  • InfrastructureAdvisorAutoProxyCreator  
  • DefaultAdvisorAutoProxyCreator :  将对应匹配的advisor,自动添加到spring的bean。它控制的是advisor的匹配,所有的bean都会被自动代理

再思考一下我自己的需求:
1.  允许和BeanNameAutoProxyCreator指定对应的bean names和inteceptorNames,而不是自动代理所有的bean。

 

2.  如果原始对象是proxyFactoryBean,配置的并行加载拦截器是基于同一个proxyFactoryBean
3.  多次的融合机制,是可以进行合并处理。 因为不同的框架会自定义配置一份代理拦截,需要将两份拦截器进行合并处理。

针对该需求的解决方案:
1. 继续引用auto-proxy的那套机制,基于BeanPostProcessor在bean的生命周期初始化的最后一环节进行切入。
切入代码:
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        // 不做处理
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            return wrapIfNecessary(bean, beanName, cacheKey);
        }

        return bean;
    }
2.  识别bean的对象类型
*  如果原先的bean是proxyFactoryBean,则将原先定义的inteceptorNames和融合的配置进行合并
*  其他的bean进行代理对象构造。
3.  多次融合的拦截器合并,并不是真正的配置项的合并(那样成本太大,需要在spring解析时插入点代码)。换种思路就是将多次融合做为一个chian的处理,前一个融合会创建一个ProxyFactoryBean,后一个融合识别出是一个ProxyFactoryBean就会在原先的基础上在做融合,这样就解决了多次融合的合并问题。

对应的核心代码:(也就没几行)
if (ProxyFactoryBean.class.isAssignableFrom(bean.getClass())) {
    ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;
    String[] orignInterceptorNames = getInterceptorFromProxyFactoryBean(proxyFactoryBean);
    String[] newInterceptorNames = new String[orignInterceptorNames.length
      + interceptorNames.length];
    if (applyCommonInterceptorsFirst) {// 如果是true,则将Auto-proxy的拦截器定义到最前面
        // 构造新的的拦截器列表
        System.arraycopy(interceptorNames, 0, newInterceptorNames, 0, interceptorNames.length);
        System.arraycopy(orignInterceptorNames, 0, newInterceptorNames, interceptorNames.length,
     orignInterceptorNames.length);
    } else {
        System.arraycopy(orignInterceptorNames, 0, newInterceptorNames, 0,
     orignInterceptorNames.length);
        System.arraycopy(interceptorNames, 0, newInterceptorNames, orignInterceptorNames.length,
     interceptorNames.length);
    }
    // 重新设置新的inteceptorNames
    proxyFactoryBean.setInterceptorNames(newInterceptorNames);
    return proxyFactoryBean;
} else {
    // 如果是单例,对应的代理bean对象为同一个
    ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    proxyFactoryBean.setBeanFactory(beanFactory);
    proxyFactoryBean.setBeanClassLoader(proxyClassLoader);
    proxyFactoryBean.setInterceptorNames(interceptorNames);
    proxyFactoryBean.copyFrom(this); // 拷贝对应的一些Proxy config
    proxyFactoryBean.setTarget(bean);
    return proxyFactoryBean.getObject();
}
 
对应的配置文件示例:
     <!-- 多例测试 -->
     <bean id="asyncLoadTestServiceForCompsitePrototype" class="com.agapple.asyncload.domain.AsyncLoadTestServiceImpl" scope="prototype" />
     <!-- 单例测试 -->
     <bean id="asyncLoadTestServiceForCompsiteSingleton" class="com.agapple.asyncload.domain.AsyncLoadTestServiceImpl" scope="singleton" />
     <!-- 原本是ProxyFactoryBean -->
     <bean id="logInteceptor" class="com.agapple.asyncload.spring.LogInteceptor" />
     <bean id="asyncLoadTestServiceForCompsiteFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean" scope="prototype">
    	<property name="interceptorNames">
    		<list>
    			<value>logInteceptor</value>
    		</list>
    	</property>
    	<property name="targetName" value="asyncLoadTestService" />
    	<property name="proxyTargetClass" value="true" /> <!-- 强制申明为cglib代理 -->
     </bean>
     
     <!-- 第一次融合代理 -->
     <bean class="com.agapple.asyncload.impl.spring.CompositeAutoProxyCreator">
        <property name="beanNames">
        	<list>
        		<value>asyncLoadTestServiceForCompsitePrototype</value>
        		<value>asyncLoadTestServiceForCompsiteSingleton</value>
        		<value>asyncLoadTestServiceForCompsiteFactoryBean</value> <!-- 代理的对象原本已经是一个proxyFactoryBean的final cglib-->
        	</list>
        </property>
        <property name="interceptorNames">
        	<list>
	            <value>asyncLoadInterceptor</value>
        	</list>
        </property>
     </bean>
     <!-- 多次的代理会只作用于一个代理对象 -->
     <bean class="com.agapple.asyncload.impl.spring.CompositeAutoProxyCreator">
     	<property name="applyCommonInterceptorsFirst" value="true"/>
        <property name="beanNames">
        	<list>
        		<value>asyncLoadTestServiceForCompsitePrototype</value>
        		<value>asyncLoadTestServiceForCompsiteSingleton</value>
        		<value>asyncLoadTestServiceForCompsiteFactoryBean</value>  <!-- 代理的对象原本已经是一个proxyFactoryBean的final cglib-->
        	</list>
        </property>
        <property name="interceptorNames">
        	<list>
    			<value>logInteceptor</value>
    		</list>
        </property>
     </bean>

代码

配置文件:https://code.google.com/p/asyncload/source/browse/trunk/src/test/resources/asyncload/applicationContext.xml,查找对应的Compsite

 

最后

在查找具体的解决方案是一个比较曲折的过程,基本上把ProxyFactoryBean机制,auto-proxy机制代码实现都看了一遍。

过程1 : 原先是寄希望于spring可以提供类似的一个global advisor的概念,每个ProxyFatoryBean除了自己配置的inteceptorNames的拦截器之外,还回从一些global的定义中获取一些大家公用的advisor。找了一圈发现没有,曾经的GlobalAdvisorAdapterRegistry给了我一些希望,最后发现是一个绝望的内容。 

 

过程2:在看AutoProxyCreator那套机制时,其实它预留了一些扩展点,主要是根据bean name获取对应的auto-proxy信息。bean name到具体的bean的处理,因为是在BeanPostProcessor,处于getBean()生命周期的最后一步,如果此时进行this.beanFactory.getBean()就是一个死循环,此路不通,所以原先的auto-proxy相关的扩展点基本走不同。

 

过程3: 在2走不通后,一直在琢磨是否可以通过直接处理生成后的object,获取原始object对应的ProxyFactoryBean.getAdvsisor()方法。后来看了半天代码,发现也是条思路,因为proxyFactoryBean生成的proxy对象完全和ProxyFactoryBean无关,也就不会有getAdvisor()方法,因为生成的cglib代理或者jdk代理,cglib代理到还有对advised的引用持有,有办法通过反射获取。而jdk那个压根没则,所以有是一条思路。

 

过程4: 2,3都走不通了,今天在debug另一个问题时,发现BeanPostProcessor的相应callback方法中传递的bean object居然是原生的ProxyFactoryBean,也就是说未进行getObject()调用之前的原始对象。大喜,总于找到突破口,最后就是自己实现了一套auto-proxy机制,因为原先的扩展点不支持传递bean object对象。

 

最后只能说自己对spring的一些机制还不够了解,需要持续加强。发现去扩展spring的一些点,是学习spring最快的一种方式,找扩展点的过程是对spring的一个总体把握的过程


悟了3天的问题,总于有了解决方案,仅以此文几年一下我那3天死去的脑细胞!!

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics