- 浏览: 64591 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (55)
- spring (4)
- jboss (0)
- web (6)
- maven (5)
- oracle (5)
- mysql (6)
- tomcat (1)
- jvm (9)
- eclipse (2)
- qc (1)
- linux nfs (1)
- ha-proxy (1)
- http (1)
- java (9)
- mongo (2)
- database (1)
- javascript (2)
- sqlserver (2)
- ibatis (1)
- zookeeper (3)
- apache (1)
- linux (1)
- git (1)
- network (1)
- kafka (1)
- 并发编程 (1)
- 分布式一致性算法 (1)
- hystrix (1)
最新评论
项目里需要对方法进行监控,记录执行时间超长的方法。很自然会想用到AOP动态代理加强方式来解决。于是呼就了下面的代码,下面的是测试代码(项目代码不好贴),有需要测试代码的,可下载附件:
DemoMethodInterceptor类:
spring配置:
这里用spring的BeanNameAutoProxyCreator来对以'class'作为前缀的bean做动态代理。结果抛出了异常,如下:
从提示看是非最终版本的classA被注入到了循环依赖的classB里,但spring是支持循环依赖的啊,而且在代码里不可避免会有这种情况出现,那为什么会出现非最终版本被注入呢?经过debug发现答案在,spring的‘AbstractAutowireCapableBeanFactory’的getEarlyBeanReference方法里(我的spring版本是3.0.1-release)。
如果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对象,因此结局就有了最上面的异常。
我的解决方法:
解决方法是不用BeanNameAutoProxyCreator,改用<aop:config>。
原因:
因为在解析的时候会判断是否已经存在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方法里(我的spring版本是3.0.1-release)。
/** * 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对象,因此结局就有了最上面的异常。
我的解决方法:
解决方法是不用BeanNameAutoProxyCreator,改用<aop:config>。
<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>
原因:
因为在解析的时候会判断是否已经存在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
- demo-spring.rar (15.2 KB)
- 下载次数: 19
发表评论
-
spring bean标注了Autowired注解的属性没有自动注入对象问题
2017-07-03 17:14 01、问题描述 某些spring 托管的bean,被标注A ... -
spring mvc Could not resolve view 返回500问题
2016-01-27 17:07 1432问题: spring mvc找不到view时,返回http 5 ... -
Spring BeanNameUrlHandlerMapping自动探测问题
2014-12-15 21:58 1087BeanNameUrlHandlerMapping 会自动探测 ... -
用spring profile或maven profile实现多环境配置切换
2014-09-26 14:16 490开发环境配置、测试环境以及线上环境的配置肯定是不一样的,比如: ...
相关推荐
在Spring框架中,循环依赖(Circular Dependency)是指两个或多个Bean之间形成的一种相互依赖关系,导致Spring容器在初始化这些Bean时遇到困难。当一个Bean依赖于另一个Bean,而后者又反过来依赖于前者,就会出现...
报错信息`BeanCurrentlyInCreationException`明确指出存在多版本的循环依赖,这意味着bean在不同阶段被不同类型的代理所替代,导致了不一致。为了解决这个问题,我们可以采取以下策略: 1. 避免循环依赖:重新设计...
Spring 循环依赖是指在 Spring 框架中,多个 Bean 之间存在相互依赖关系,从而形成一个循环依赖链。如果在日常开发中,我们使用 new 对象的方式来实例化 Bean,这种循环依赖可能会导致程序在运行时一直循环调用,...
AOP是面向切面编程,在Spring中用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取公共模块复用,降低耦合。例如,可以使用AOP来记录系统的操作日志。通过使用环绕通知+切点表达式,可以找到要...
- 在面试中强调项目经验和技术细节,展示解决问题的能力。 综上所述,本面试宝典涵盖了Java基础知识、Spring框架、大数据技术及其相关项目实践经验等多个方面,旨在帮助面试者全面掌握所需技能,顺利通过面试。
这个问题通常是由于对象之间的引用循环导致的。在上述例子中,错误信息显示了在`net.zjitc.xxx.pojo.XXX`类的属性之间存在循环引用,导致Jackson库在尝试转化JSON时陷入了无限循环。 首先,我们需要理解Jackson库是...
标题 "tomcat5.5缺少jar包" 描述了在使用Tomcat 5.5版本时遇到的一个常见问题,即在部署和运行基于Webwork、Spring和iBatis的Web应用时,由于某些必需的JAR文件缺失,导致程序无法正常启动或运行。这个问题通常与...
解决这个问题需要以下步骤: 1. **检查JSTL库**:首先,确保你的项目中包含了JSTL的jar包。JSTL通常包含两个主要的jar文件:`jstl.jar`(提供核心标签)和`standard.jar`(提供EL表达式支持)。这两个jar文件需要...
- 一个`.java`源文件可以包含多个类,但是只能有一个公开的`public`类,且该类的名字必须与文件名相同。其他非`public`类可以在同一个文件中,无需对应文件名。 2. **保留字与关键字**: - `goto`是Java中的保留...
- 如果项目使用了Spring,那么它可能是MVC架构,涉及依赖注入、AOP、Spring Boot、Spring Data等模块。 12. **数据库连接**: - JDBC是Java访问数据库的标准API,还可以使用ORM框架如Hibernate或MyBatis简化...