美文网首页Spring 源码
spring的bean二次代理问题

spring的bean二次代理问题

作者: carway | 来源:发表于2017-12-24 22:27 被阅读535次

最近在一个spring+springmvc+shiro+druid的项目里面,由于配置文件xml的错误,遇到了二次代理问题,即一个bean被代理两次。
刚好我需要用druid去监控spring的controller层,controller是只有实体类,没有不是实现接口的,所以两次代理后,可能是因为优先级就选择了JDK代理,那肯定会报错啦。
所以今天就来讲讲spring的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" class="com.sishuok.proxy.MyBean">  
    <property name="target" ref="target"/>  
</bean>  
  
<bean id="target" class="com.sishuok.proxy.Target"/>  
<bean id="myAspect" class="com.sishuok.proxy.aspect.MyAspect"/>  
  
<aop:config proxy-target-class="true">  
    <aop:aspect ref="myAspect">  
        <aop:before method="before" pointcut="execution(* com.sishuok.proxy.*.*(..))"/>  
    </aop:aspect>  
</aop:config>  

2.2、配置文件other-config.xml

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>  

问题分析:

1、首先spring-config.xml配置文件的<aop:config>会交给AopNamespaceHandler处理:

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,具体参考如下两篇文章,此处就不详述了:

所以问题就出现了(以下顺序默认应该看成无序,可以修改order属性来指定顺序,但没有作用):
1、AspectJAwareAdvisorAutoProxyCreator会创建一个代理(因为<aop:config proxy-target-class="true">),这个代理是CGLIB代理
2、DefaultAdvisorAutoProxyCreator会对代理对象再创建代理,但是因为没有告诉它代理类,所以默认代理接口,即代理是JDK动态代理

即虽然AspectJAwareAdvisorAutoProxyCreator创建了类代理,但DefaultAdvisorAutoProxyCreator还是创建了JDK动态代理(接口)。

解决办法:

1、DefaultAdvisorAutoProxyCreator也采用cglib代理,虽然是能解决问题,但实际上spring还是创建两个cglib代理,一个是DefaultAdvisorAutoProxyCreator,一个是AspectJAwareAdvisorAutoProxyCreator

    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
        <property name="proxyTargetClass" value="true"/>
    </bean>            

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

1.见到$Proxy开头的类,基本上可以确定是JDK动态代理

2.此处可以看到$Proxy0 实现了 一堆接口,能看到一个org.springframework.cglib.proxy.Factory,从这个已经能判断出其是二次代理了,即当前的JDK动态代理代理了CGLIB代理。

  1. 如果见到如输出的class是com.sishuok.proxy.Target$$EnhancerByCGLIB$$12c22b67,那就是CGLIB代理了。

总结

  1. 首选如<aop:config>,而不是自己定义如×××AutoProxyCreator,而且使用<aop:config>方式能更好的描述切面。

  2. 观察类是$Proxy…… 还是 ……$$EnhancerByCGLIB$$……,来判断是JDK动态代理还是CGLIB代理。

  3. 通过观察$Proxy的实现中是否包含org.springframework.cglib.proxy.Factory来判断是否是二次代理。

  4. 通过《Spring事务不起作用 问题汇总》 中介绍的方式查看是否创建了代理。

转载于:http://jinnianshilongnian.iteye.com/blog/1894465

相关文章

  • spring的bean二次代理问题

    最近在一个spring+springmvc+shiro+druid的项目里面,由于配置文件xml的错误,遇到了二次...

  • Spring注解--AOP原理(五):代理对象目标方法的执行

    在前面的章节中,谈到代理对象的创建:参考Spring注解--AOP原理(四):业务bean与代理bean的创建, ...

  • 06-Spring 核心

    Spring做了两件事:1.管理Bean的生命周期 2. 创建Bean的代理对象 一、Spring容器 1.1 ...

  • spring 代理实现

    背景:动态代理 + cglib 代理 技巧:借助 spring 内置的 bean 对象实现代理创建比如内置的:Be...

  • 分享一下

    springSpring的bean如何解决相互依赖的问题里面用到的设计模式动态代理在哪里用,全部列举 spring...

  • spring 简单Bean 实例化过程

    说明:本文只介绍简单的bean实例化过程,没有注入,没有代理等等 实例 代码 运行结果 spring Bean 实...

  • Spring面试常问内容

    Spring 面试问题 TOP 50 IOC、AOP、AOP原理动态代理和cglib原理与二者之间的区别、Bean...

  • Spring_platform

    Spring的注解 ,长生命周期的bean依赖于短生命周期的bean时,对短生命周期的bean使用代理。

  • aop

    使用: spring aop使用简单示例 开启单个bean的代理,ProxyFactoryBean aop失效 a...

  • Spring源码解析之Spring bean的生命周期

    本文将从Spring源码探讨2个问题,Spring 容器是如何管理bean,以及如何从容器中获取bean Bean...

网友评论

    本文标题:spring的bean二次代理问题

    本文链接:https://www.haomeiwen.com/subject/frfkgxtx.html