spring的二次代理原因及如何排查
最近一个朋友使用javamelody时遇到一个二次代理的问题,即一个Bean被代理了两次。
?
我还原了一下问题,并简化出一个工程方便大家观察。可以下载附件代码还原场景。
?
代码如下:
1、接口及目标类?
package com.sishuok.proxy;public interface Interface { public void sayHello();}
package com.sishuok.proxy;public class Target implements Interface { public void sayHello() { System.out.println("===hello"); }}
2.1、spring-config.xml配置: ?
<bean id="myBean" ref="target"/> </bean> <bean id="target" pointcut="execution(* com.sishuok.proxy.*.*(..))"/> </aop:aspect> </aop:config>
?
aop:config proxy-target-name="code"><bean name="code">http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
?
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
2、aop:config委托给ConfigBeanDefinitionParser处理,并通过如下代码注册自动代理创建器:?
configureAutoProxyCreator(parserContext, element);
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);}
最终会委托给如下代码(中间过程省略,都是委托):??
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);}
??
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;}
即最终会创建一个AspectJAwareAdvisorAutoProxyCreator,如上代码意思就是:如果当前容器中已经有一个AUTO_PROXY_CREATOR_BEAN_NAME,那么根据实际情况修改配置,否则添加一个(也就是说一个容器中不管有多少个aop:config也最多只添加一个AspectJAwareAdvisorAutoProxyCreator)
?
2、接着会添加other-config.xml的DefaultAdvisorAutoProxyCreator,即又添加了一个自动代理创建器;
?
注意 :这两个AutoProxyCreator都是BeanPostProcessor,具体参考如下两篇文章,此处就不详述了:
《Spring提供的BeanPostProcessor的扩展点-1》《Spring开闭原则的表现-BeanPostProcessor扩展点-2?》?
所以问题就出现了(以下顺序默认应该看成无序,可以修改order属性来指定顺序,但没有作用):
??
2、全部使用<aop:config>,不要自己去定义自己的AutoProxyCreator,这也是推荐的方式,因为这样一个容器永远只有一个AutoProxyCreator。
?
?
如何判断是二次代理
观察异常:
Caused by: java.lang.IllegalStateException: Cannot convert value of type [$Proxy0?implementing org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised,org.springframework.cglib.proxy.Factory,com.sishuok.proxy.Interface] to required type [com.sishuok.proxy.Target] for property 'target': no matching editors or conversion strategy found
?
?
?
总结
分析完毕。
?