spring多个动态代理导致循环依赖报错问题解决
2014-05-22 17:27
676 查看
项目里需要对方法进行监控,记录执行时间超长的方法。很自然会想用到AOP动态代理加强方式来解决。于是呼就了下面的代码,下面的是测试代码(项目代码不好贴),有需要测试代码的,可下载附件:
DemoMethodInterceptor类:
spring配置:
这里用spring的BeanNameAutoProxyCreator来对以'class'作为前缀的bean做动态代理。结果抛出了异常,如下:
从提示看是非最终版本的classA被注入到了循环依赖的classB里,但spring是支持循环依赖的啊,而且在代码里不可避免会有这种情况出现,那为什么会出现非最终版本被注入呢?经过debug发现答案在,spring的‘AbstractAutowireCapableBeanFactory’的getEarlyBeanReference方法里([color=red]我的spring版本是3.0.1-release[/color])。
如果spring设置为支持循环依赖(默认支持),那么在创建的时候,如果有循环依赖发生,就会执行上面这段代码。在DEBUG的时候发现了2个代理创建类,也就是InfrastructureAdvisorAutoProxyCreator(项目里用了spring-security)和BeanNameAutoProxyCreator,这两个类都会执行各自的getEarlyBeanReference方法,都来自父类AbstractAutoProxyCreator。
getEarlyBeanReference方法会缓存创建的代理对象,在各自this.earlyProxyReferences里,两个类都会创建代理,因此有了2个代理对象,前者先执行(InfrastructureAdvisorAutoProxyCreator),假设对象分别得proxy1和proxy2,最终proxy2又代理了proxy1,那么执行完后,返回的是proxy2对象。
接下来的关键点是在bean生命周期里的后置处理器了,BeanPostProcessor是对bean的最后一次加强。跟踪代码,spring会执行下面这段代码,AbstractAutoProxyCreator的postProcessAfterInitialization方法,InfrastructureAdvisorAutoProxyCreator和BeanNameAutoProxyCreator都是一种BeanPostProcessor,因此会执行2次,顺序跟执行getEarlyBeanReference方法一致,前者先执行:
这里又会执行2次?为什么呢?因为前面的getEarlyBeanReference方法执行2次后,后面的对象proxy2代理了proxy1。这里传入的bean还是原生对象,第一次执行的时候还是正确的,但第2次执行时(也就是BeanNameAutoProxyCreator)就会出现问题,因为之前earlyProxyReferences里保存的是对proxy1对象的创建状态,而传入的bean还是原生对象,那么显然是没有的,因此又有了对象proxy3,这个proxy3返回后,赋值给了变量'exposedObject ',如下:
接下来是异常抛出的地点了:
上面第2行代码的earlySingletonReference=proxy2,exposedObject=proxy3,bean=原始的bean对象,因此结局就有了最上面的异常。
[size=large][color=red]我的解决方法:[/color][/size]
[color=red]解决方法是不用BeanNameAutoProxyCreator,改用<aop:config>。[/color]
[size=large][color=red]原因:[/color][/size]
因为在解析的时候会判断是否已经存在InfrastructureAdvisorAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator三者之一,有就合并,否则就注册一个,因此始终就只会有一个代理创建器。
<aop:config>会交给org.springframework.aop.config.AopNamespaceHandler处理
ConfigBeanDefinitionParser的parse方法会解析配置:
看其中的configureAutoProxyCreator方法,会委托给AopNamespaceUtils的registerAspectJAutoProxyCreatorIfNecessary方法处理:
AopNamespaceUtils的registerAspectJAutoProxyCreatorIfNecessary方法:
AopConfigUtils的registerAspectJAutoProxyCreatorIfNecessary方法:
registerOrEscalateApcAsRequired方法:
上面代码,判断是否存在id='org.springframework.aop.config.internalAutoProxyCreator'的bean,否则注册一个,如果有,那么进行合并。
参考:
http://jinnianshilongnian.iteye.com/blog/1901694 阅读更多
DemoMethodInterceptor类:
public class DemoMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("invoke start...");
return invocation.proceed();
}
}
spring配置:
<bean id="demoMethodInterceptor" class="name.zhengwei.demo.spring.aop.DemoMethodInterceptor">
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<value>class*</value>
</property>
<property name="interceptorNames">
<list>
<value>demoMethodInterceptor</value>
</list>
</property>
</bean>
这里用spring的BeanNameAutoProxyCreator来对以'class'作为前缀的bean做动态代理。结果抛出了异常,如下:
Error creating bean with name 'classA': Bean with name 'classA' has been injected into other beans [classB] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
从提示看是非最终版本的classA被注入到了循环依赖的classB里,但spring是支持循环依赖的啊,而且在代码里不可避免会有这种情况出现,那为什么会出现非最终版本被注入呢?经过debug发现答案在,spring的‘AbstractAutowireCapableBeanFactory’的getEarlyBeanReference方法里([color=red]我的spring版本是3.0.1-release[/color])。
/**
* Obtain a reference for early access to the specified bean,
* typically for the purpose of resolving a circular reference.
* @param beanName the name of the bean (for error handling purposes)
* @param mbd the merged bean definition for the bean
* @param bean the raw bean instance
* @return the object to expose as bean reference
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
如果spring设置为支持循环依赖(默认支持),那么在创建的时候,如果有循环依赖发生,就会执行上面这段代码。在DEBUG的时候发现了2个代理创建类,也就是InfrastructureAdvisorAutoProxyCreator(项目里用了spring-security)和BeanNameAutoProxyCreator,这两个类都会执行各自的getEarlyBeanReference方法,都来自父类AbstractAutoProxyCreator。
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.add(cacheKey);
return wrapIfNecessary(bean, beanName, cacheKey);
}
getEarlyBeanReference方法会缓存创建的代理对象,在各自this.earlyProxyReferences里,两个类都会创建代理,因此有了2个代理对象,前者先执行(InfrastructureAdvisorAutoProxyCreator),假设对象分别得proxy1和proxy2,最终proxy2又代理了proxy1,那么执行完后,返回的是proxy2对象。
接下来的关键点是在bean生命周期里的后置处理器了,BeanPostProcessor是对bean的最后一次加强。跟踪代码,spring会执行下面这段代码,AbstractAutoProxyCreator的postProcessAfterInitialization方法,InfrastructureAdvisorAutoProxyCreator和BeanNameAutoProxyCreator都是一种BeanPostProcessor,因此会执行2次,顺序跟执行getEarlyBeanReference方法一致,前者先执行:
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
这里又会执行2次?为什么呢?因为前面的getEarlyBeanReference方法执行2次后,后面的对象proxy2代理了proxy1。这里传入的bean还是原生对象,第一次执行的时候还是正确的,但第2次执行时(也就是BeanNameAutoProxyCreator)就会出现问题,因为之前earlyProxyReferences里保存的是对proxy1对象的创建状态,而传入的bean还是原生对象,那么显然是没有的,因此又有了对象proxy3,这个proxy3返回后,赋值给了变量'exposedObject ',如下:
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
接下来是异常抛出的地点了:
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
上面第2行代码的earlySingletonReference=proxy2,exposedObject=proxy3,bean=原始的bean对象,因此结局就有了最上面的异常。
[size=large][color=red]我的解决方法:[/color][/size]
[color=red]解决方法是不用BeanNameAutoProxyCreator,改用<aop:config>。[/color]
<bean id="methodProfileAdvice" class="name.zhengwei.demo.spring.aop.MethodProfileAdvice"></bean>
<!-- 进行aop配置 -->
<aop:config proxy-target-class="false">
<!-- 配置日志切面 -->
<aop:aspect id="methodProfileAspect" ref="methodProfileAdvice">
<aop:pointcut id="methodProfilePointcut" expression="execution(* name.zhengwei.demo.spring.service..*.*(..))" />
<!-- 将methodProfileAdvice通知中的方法指定为环绕通知 -->
<aop:around method="myAroundAdvice" pointcut-ref="methodProfilePointcut"/>
</aop:aspect>
</aop:config>
[size=large][color=red]原因:[/color][/size]
因为在解析的时候会判断是否已经存在InfrastructureAdvisorAutoProxyCreator,AspectJAwareAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator三者之一,有就合并,否则就注册一个,因此始终就只会有一个代理创建器。
<aop:config>会交给org.springframework.aop.config.AopNamespaceHandler处理
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
ConfigBeanDefinitionParser的parse方法会解析配置:
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element);
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
String localName = parserContext.getDelegate().getLocalName(node);
if (POINTCUT.equals(localName)) {
parsePointcut((Element) node, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor((Element) node, parserContext);
}
else if (ASPECT.equals(localName)) {
parseAspect((Element) node, parserContext);
}
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
看其中的configureAutoProxyCreator方法,会委托给AopNamespaceUtils的registerAspectJAutoProxyCreatorIfNecessary方法处理:
/**
* Configures the auto proxy creator needed to support the {@link BeanDefinition BeanDefinitions}
* created by the '<code><aop:config/></code>' tag. Will force class proxying if the
* '<code>proxy-target-class</code>' attribute is set to '<code>true</code>'.
* @see AopNamespaceUtils
*/
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}
AopNamespaceUtils的registerAspectJAutoProxyCreatorIfNecessary方法:
public static void registerAspectJAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
AopConfigUtils的registerAspectJAutoProxyCreatorIfNecessary方法:
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
registerOrEscalateApcAsRequired方法:
private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
上面代码,判断是否存在id='org.springframework.aop.config.internalAutoProxyCreator'的bean,否则注册一个,如果有,那么进行合并。
参考:
http://jinnianshilongnian.iteye.com/blog/1901694 阅读更多
相关文章推荐
- Spring中的循环依赖问题介绍及解决方法
- 解决spring循环依赖的问题:has been injected into other beans
- 解决spring使用动态代理类型转换失败的问题--java.lang.ClassCastException: com.sun.proxy.$Proxy$ cannot be cast to ...今天
- Spring源码学习--Bean对象循环依赖问题解决(四)
- 解决Proxy.newProxyInstance创建动态代理导致类型转换错误的问题
- 解决spring使用动态代理类型转换失败的问题--java.lang.ClassCastException: com.sun.proxy.$Proxy$ cannot be cast to ...
- 解决spring循环依赖的问题:has been injected into other beans
- spring 源码探索--单例bean解决循环依赖问题
- 解决spring使用动态代理类型转换失败的问题--java.lang.ClassCastException: com.sun.proxy.$Proxy$ cannot be cast to ...
- Spring中的循环依赖问题介绍及解决方法
- java实现动态代理代码实例(死循环溢出的问题的解决)
- spring 注入问题 (AOP 动态代理)
- Cglib实现动态代理-解决大对象值传递问题
- jquery动态改变onclick属性导致失效的问题解决方法
- 解决apache/nginx做反向代理导致request.getServerName()外网环境获取不到代理地址(或域名)的问题
- 解决apache/nginx做反向代理导致request.getServerName()外网环境获取不到代理地址(或域名)的问题
- spring动态代理的实现原理InvocationHandler中invoke()方法的调用问题
- spring bom 解决spring依赖多版本问题
- SPRING循环依赖(circular reference)的解决方法
- 关于Spring AOP动态代理类型转换失败问题--java.lang.ClassCastException: com.sun.proxy.$Proxy2 cannot be cast to ...