spring的auto-proxy自动署理(融合机制实现)
背景
最近在实施并行加载,遇到一个问题: 重复代理,或者说是两次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都会被自动代理
切入代码:
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; }
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天死去的脑细胞!!