• spring aop切面不生效


    由于<aop:aspectj-autoproxy proxy-target-class="true"/>和<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>冲突,导致aop
    切面强制使用cglib失效(2次代理),产生Bean named 'XXX' must be of type [XXX], but was actually of type [com.sun.proxy.$Proxy270]的错误,即在对类进行切面时没有使用cglib,而是用了java的默认代理
    java的默认代理只能作用于接口,所以导致出错。

       <aop:aspectj-autoproxy proxy-target-class="true"/>
        <!--    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>-->


    具体原因:

    出问题的配置

    Java代码  收藏代码
    1. <bean class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator">  
    2.     <property name="proxyTargetClass" value="true"/>  
    3. </bean>  
    4. <tx:annotation-driven transaction-manager="transactionManager"/>   

    此配置的目的是想进行cglib类代理。但是实际上当进行直接注入类,而不是接口时会找不到Bean错误。

    但是如果是这样配置: 

    Java代码  收藏代码
    1. <aop:aspectj-autoproxy proxy-target-class="true"/>  
    2. <tx:annotation-driven transaction-manager="transactionManager"/>  

    此配置可以很好的工作,并注入类(不是接口)。 

    分析

    1、<aop:aspectj-autoproxy proxy-target-class="true"> 该命名空间会交给org.springframework.aop.config.AopNamespaceHandler处理: 

    Java代码  收藏代码
    1. registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());  

    在AspectJAutoProxyBeanDefinitionParser中,会执行parse方法解析配置:

    Java代码  收藏代码
    1. public BeanDefinition parse(Element element, ParserContext parserContext) {  
    2.     AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);  
    3.     extendBeanDefinition(element, parserContext);  
    4.     return null;  
    5. }  

    其中AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);目的是注册AnnotationAwareAspectJAutoProxyCreator: 

    Java代码  收藏代码
    1. return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);  

    但是注意了: 

    Java代码  收藏代码
    1. if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
    2.     BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
    3.     if (!cls.getName().equals(apcDefinition.getBeanClassName())) {  
    4.         int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());  
    5.         int requiredPriority = findPriorityForClass(cls);  
    6.         if (currentPriority < requiredPriority) {  
    7.             apcDefinition.setBeanClassName(cls.getName());  
    8.         }  
    9.     }  
    10.     return null;  
    11. }  

    大家可以看到一句话: 

    • if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) 
    • AUTO_PROXY_CREATOR_BEAN_NAME=“org.springframework.aop.config.internalAutoProxyCreator”,
    • 即首先判断当前容器中是否包含名字为AUTO_PROXY_CREATOR_BEAN_NAME的Bean, 如果包含:然后判断优先级,谁优先级高谁获胜,即最后那个获胜的是实际的AutoProxyCreator

    到此我们可以看到跟"<bean class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator">"配置没什么区别,除了没有名字外。

    2、接下来看一下<tx:annotation-driven>:

    该命名空间交给org.springframework.transaction.config.TxNamespaceHandler处理: 

    Java代码  收藏代码
    1. registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());  

    其中<annotation-driven> 会交给AnnotationDrivenBeanDefinitionParser进行解析: 

    Java代码  收藏代码
    1. public BeanDefinition parse(Element element, ParserContext parserContext) {  
    2.     String mode = element.getAttribute("mode");  
    3.     if ("aspectj".equals(mode)) {  
    4.         // mode="aspectj"  
    5.         registerTransactionAspect(element, parserContext);  
    6.     }  
    7.     else {  
    8.         // mode="proxy"  
    9.         AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);  
    10.     }  
    11.     return null;  
    12. }  

    默认mode="proxy",所以走AopAutoProxyConfigurer.configureAutoProxyCreator,其代码中第一句话是: 

    Java代码  收藏代码
    1. AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);  
    Java代码  收藏代码
    1. public static void registerAutoProxyCreatorIfNecessary(  
    2.             ParserContext parserContext, Element sourceElement) {  
    3.   
    4.         BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(  
    5.                 parserContext.getRegistry(), parserContext.extractSource(sourceElement));  
    6.         useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);  
    7.         registerComponentIfNecessary(beanDefinition, parserContext);  
    8.     }  

    AopConfigUtils.registerAutoProxyCreatorIfNecessary是: 

    Java代码  收藏代码
    1. registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);  
    Java代码  收藏代码
    1. private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {  
    2.         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");  
    3.         if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
    4.             BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
    5.             if (!cls.getName().equals(apcDefinition.getBeanClassName())) {  
    6.                 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());  
    7.                 int requiredPriority = findPriorityForClass(cls);  
    8.                 if (currentPriority < requiredPriority) {  
    9.                     apcDefinition.setBeanClassName(cls.getName());  
    10.                 }  
    11.             }  
    12.             return null;  
    13.         }  
    14. //省略  

    此处我们又看到了registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME),如果是:

    • 配置1,那么实际是两个AutoProxyCreator;
    • 配置2,那么实际是共用一个AutoProxyCreator;

    而且如果配置1时,因为我们没有指定<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> 所以是JDK动态代理,因此不管怎么样,都无法注入类的。

    问题找到了,原因是注册了两个AutoProxyCreator,造成了二次代理引发的问题,这个和之前的《spring的二次代理原因及如何排查》一样。

    如果解决

    • 给配置1起名字为”org.springframework.aop.config.internalAutoProxyCreator“;
    • 或者使用配置2 

    建议

    1、如果没有必要,请不要使用低级别API,如上述-->自己去创建AutoProxyCreator

    2、首先选择使用如:

    <aop:config>

    <org.springframework.aop.config.internalAutoProxyCreator>

    如上配置已经非常好了,根本没必要使用低级别API。

    如<tx:annotation-driven>使用的AutoProxyCreator都是和上边是一样的。这样还能防止二次代理。

    声明式/@AspectJ风格的AOP都非常好了,完全没必要使用低级别API,请不要再使用低级别API了。

    如果用过shiro的朋友都应该知道如下配置:

    Java代码  收藏代码
    1. <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after -->  
    2. <!-- the lifecycleBeanProcessor has run: -->  
    3. <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>  
    4. <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
    5.     <property name="securityManager" ref="securityManager"/>  
    6. </bean>  

    其实我们可以这样:

    Java代码  收藏代码
    1. <aop:config proxy-target-class="true"/>  
    2. <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
    3.     <property name="securityManager" ref="securityManager"/>  
    4. </bean>  

    或者使用<aop:aspectj-autoproxy>也行,这样也不会存在二次代理的问题。  

  • 相关阅读:
    iOS UITableView的cell重用标识
    iOS SDWebImage清理缓存数据
    iOS UITextView 根据输入text自适应高度
    iOS 网络请求 NSURLSession 的上传文件方法
    iOS开发之tintColor属性详解
    iOS SDWEBImage和collectionView的组合,以及collectionView的随意间距设置
    iOS9 Xcode7 设置Launch Image 启动图片
    iOS
    iOS 浅谈AFNetworking网络请求
    贝塞尔曲线
  • 原文地址:https://www.cnblogs.com/sidesky/p/12718218.html
Copyright © 2020-2023  润新知