zoukankan      html  css  js  c++  java
  • Spring AOP 不同配置方式产生的冲突问题

    Spring AOP的原理是 JDK 动态代理和CGLIB字节码增强技术,前者需要被代理类实现相应接口,也只有接口中的方法可以被JDK动态代理技术所处理;后者实际上是生成一个子类,来覆盖被代理类,那么父类的final方法就不能代理,因为父类的final方法不能被子类所覆盖。一般而言Spring默认优先使用JDK动态代理技术,只有在被代理类没有实现接口时,才会选择使用CGLIB技术来实现AOP。

    但是也提供了配置参数来强制选择使用 CGLIB 技术,如下:

    <aop:config proxy-target-class="true" /> 

    proxy-target-class="true" 表示强制使用 CGLIB 技术来实现AOP,因为CGLIB是生成子类也就是代理类来实现的,所以proxy-target-class,表示是否代理目标类。<aop:config /> 就会由spring来选择,spring优先使用JDK动态代理来实现AOP。

    <aop:config /> 那么这句配置,会起到什么作用呢?首先它是 aop 命名空间中的配置,所以:

    /**
     * NamespaceHandler for the aop namespace.
     * @author Rob Harrop
     * @author Adrian Colyer
     * @author Juergen Hoeller
     * @since 2.0
     */
    public class AopNamespaceHandler extends NamespaceHandlerSupport {
        /**
         * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
         * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
         * and '{@code scoped-proxy}' tags.
         */
        @Override
        public void init() {
            // In 2.0 XSD as well as in 2.1 XSD.
            registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
            registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
            registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
            // Only in 2.0 XSD: moved to context namespace as of 2.1
            registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        }
    }

    上面的代码表名,aop命名空间有三个元素:<aop:config />, <aop:aspectj-autoproxy />, <aop:scoped-proxy />,而spring-configured被移到了context命名空间了,也就是变成了: <context:spring-configured />

    <aop:config /> 所有的配置,由 ConfigBeanDefinitionParser 来解析:

    class ConfigBeanDefinitionParser implements BeanDefinitionParser {
        @Override
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            CompositeComponentDefinition compositeDef =
                    new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
            parserContext.pushContainingComponent(compositeDef);
    
            configureAutoProxyCreator(parserContext, element);
        /**
         * Configures the auto proxy creator needed to support the BeanDefinitions
         * created by the <aop:config/> tag. Will force class proxying if the 'proxy-target-class' attribute is set to 'true'.
         * @see AopNamespaceUtils
         */
        private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
            AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
        }
    }

    继续追踪 AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element):

    /**
     * Utility class for handling registration of auto-proxy creators used internally by the 'aop' namespace tags.
     * Only a single auto-proxy creator can be registered and multiple tags may wish
     * to register different concrete implementations. As such this class delegates to
     * AopConfigUtils which wraps a simple escalation protocol. Therefore classes
     * may request a particular auto-proxy creator and know that class, or a subclass
     * thereof, will eventually be resident in the application context.
     *
     * @author Rob Harrop
     * @author Juergen Hoeller
     * @author Mark Fisher
     * @since 2.0
     * @see AopConfigUtils
     */
    public abstract class AopNamespaceUtils {
        /**
         * The  proxy-target-class attribute as found on AOP-related XML tags.
         */
        public static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
        /**
         * The expose-proxy attribute as found on AOP-related XML tags.
         */
        private static final String EXPOSE_PROXY_ATTRIBUTE = "expose-proxy";
    
        public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
            BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
                    parserContext.getRegistry(), parserContext.extractSource(sourceElement));
            useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
            registerComponentIfNecessary(beanDefinition, parserContext);
        }
        public static void registerAspectJAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
            BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
                    parserContext.getRegistry(), parserContext.extractSource(sourceElement));
            useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
            registerComponentIfNecessary(beanDefinition, parserContext);
        }
        public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
            BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                    parserContext.getRegistry(), parserContext.extractSource(sourceElement));
            useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
            registerComponentIfNecessary(beanDefinition, parserContext);
        }
    }

    很显然的这里针对Spring AOP的三种配置方法分别提供了相应的注册 AutoProxyCreator 的方法:

    <aop:config /> 方式对应的注册AutoProxyCreator 的方法是:registerAspectJAutoProxyCreatorIfNecessary;

    <aop:aspectj-autoproxy/> 方式对应的注册AutoProxyCreator 的方法是:registerAspectJAnnotationAutoProxyCreatorIfNecessary;

    DefaultAdvisorAutoProxyCreator 方式对应的注册AutoProxyCreator 的方法是:registerAutoProxyCreatorIfNecessary;

    注:DefaultAdvisorAutoProxyCreator的配置方式一般如下所示:

    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" />

    这三个方法最终调用的都是 AopConfigUtils 类同一个方法:

      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;
        }

    很显然,AOP的三种方式配置,无论如果是最后在bean factory中是只能存在一个AUTO_PROXY_CREATOR_BEAN的,它的name或者说id就是:

        /**
         * The bean name of the internally managed auto-proxy creator.
         */
        public static final String AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";

    但是,如果在多个配置文件中,混用了上面所说的AOP的三种配置方法,那么就有可能产生混乱,产生错误,比如下面的几个配置就会报错:

    1.将 <aop:config /> 与 DefaultAdvisorAutoProxyCreator 配置在一起

      <aop:config /> 
        
      <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
      <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" />
      <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
          <property name="securityManager" ref="securityManager"/>
      </bean>

    上面采用了 <aop:config /> 来配置 AUTO_PROXY_CREATOR_BEAN,而下面有采用了DefaultAdvisorAutoProxyCreator来配置AUTO_PROXY_CREATOR_BEAN,发布时没有报错,但是运行时最终报错:

    java.lang.IllegalStateException: The mapped controller method class 'com.xx.controller.xxController' is not an instance of the
    actual controller bean instance 'com.sun.proxy.$Proxy45'. If the controller requires proxying (e.g. due to @Transactional),
    please use class-based proxying.
    HandlerMethod details: 
    Controller [com.sun.proxy.$Proxy45]
    Method [public void com.xx.controller.xxController.xxo(xxx)]

    2.如果将上面的 <aop:config />  改成 <aop:config proxy-target-class="true" />  也是一样报相同的错误。

    3.但是如果将上面的DefaultAdvisorAutoProxyCreator修改成下面这样,不管是 <aop:config />  还是 <aop:config proxy-target-class="true" /> 却都是可以的。

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

    4.将 <aop:aspectj-autoproxy /> 与 DefaultAdvisorAutoProxyCreator 配置在一起,哪怕是通过<import resource="" /> 放在一起,都会报错。

        <aop:aspectj-autoproxy expose-proxy="true"/> 
        <import resource="../shiro/spring-shiro.xml"/>  
    
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager"/>
        </bean>    
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
            <property name="proxyTargetClass" value="true"/>
        </bean>

    报错信息:

    2015-07-06 17:50:02,270 WARN [org.springframework.beans.factory.support.DefaultListableBeanFactory] - 
    Bean creation exception on FactoryBean type check: org.springframework.beans.factory.BeanCurrentlyInCreationException: 
    Error creating bean with name 'shiroFilter': Requested bean is currently in creation: Is there an unresolvable circular reference?

    而将<aop:aspectj-autoproxy /> 与 DefaultAdvisorAutoProxyCreator 分开配置在不同xml文件中,则不存在该问题。

    总结

    1. Spring AOP有三种配置方式,<aop:config />,<aop:aspectj-autoproxy />, DefaultAdvisorAutoProxyCreator. 如果在一个文件中进行混用,那么就可能会产生错误。最好在同一个文件中只采用一种配置方式,推荐使用 <aop:config /> 和 <aop:aspectj-autoproxy />,淘汰DefaultAdvisorAutoProxyCreator的配置方式。

    2. Spring的有很多配置都是只在文件级起作用的。

  • 相关阅读:
    Java 中日常使用的 IO 流总结
    NIO 实现非阻塞 Socket 通讯
    Java NIO 的简单介绍和使用
    常用设计模式 -- 一分钟就能学会的门面模式(外观模式)
    Java日志框架介绍和 Slf4j 使用
    Linux学习一
    JavaScript-数组
    javascript
    idea 快捷键汇总
    正则表达式
  • 原文地址:https://www.cnblogs.com/onlymate/p/8794943.html
Copyright © 2011-2022 走看看