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 /> 

  <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的有很多配置都是只在文件级起作用的。

ps:

在iteye找到一篇类似文章 http://jinnianshilongnian.iteye.com/blog/1894465

Spring AOP 不同配置方式产生的冲突问题的更多相关文章

  1. spring 5.x 系列第4篇 —— spring AOP (代码配置方式)

    文章目录 一.说明 1.1 项目结构说明 1.2 依赖说明 二.spring aop 2.1 创建待切入接口及其实现类 2.2 创建自定义切面类 2.3 配置切面 2.4 测试切面 2.5 切面执行顺 ...

  2. spring 5.x 系列第3篇 —— spring AOP (xml配置方式)

    文章目录 一.说明 1.1 项目结构说明 1.2 依赖说明 二.spring aop 2.1 创建待切入接口及其实现类 2.2 创建自定义切面类 2.3 配置切面 2.4 测试切面 附: 关于切面表达 ...

  3. 基于注解的Spring AOP的配置和使用

    摘要: 基于注解的Spring AOP的配置和使用 AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.可以通过预编译方式和运行期动态代理实现在不 ...

  4. spring aop 使用注解方式总结

    spring aop的注解方式:和xml的配置方式略有区别,详细如下: 1.首先还是建立需要的切面类:切面类里面定义好切点配置,以及所有的需要实现的通知方法. /** * */ package com ...

  5. 循序渐进之Spring AOP(3) - 配置代理

    上一篇介绍了几种Advice(增强),并通过代码演示了生成代理的方式,下面来看通过配置文件配置方式把Advice织入目标类. 注意,配置文件方式仍然不是spring AOP的最好方式,学习配置方式也是 ...

  6. spring AOP为什么配置了没有效果?

     spring Aop的配置一定要配置在springmvc配置文件中         springMVC.xml 1 <!-- AOP 注解方式 :定义Aspect --> <!-- ...

  7. SpringBoot学习(二)-->Spring的Java配置方式

    二.Spring的Java配置方式 Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置. 1.@Configuration 和 @Bean Spring的Java配置方式是通过 @ ...

  8. Spring 的java 配置方式

    Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置. 1.1@Configuration 和 @Bean Spring的Java配置方式是通过 @Configuration 和 @ ...

  9. Spring的Java配置方式—@Configuration和@Bean实现Java配置

    Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置. 1.@Configuration 和 @BeanSpring的Java配置方式是通过 @Configuration 和 @Be ...

随机推荐

  1. 20145219《网络对抗》PC平台逆向破解

    20145219<网络对抗>PC平台逆向破解 shellcode注入 1.shellcode shellcode是一段代码,溢出后,执行这段代码能开启系统shell. 2.构造方法 (1) ...

  2. Javaworkers团队第二周项目总结

    本周项目进展 本周我们小组成员在图书馆和网上查阅了很多资料,基本达到了预期的计划,完成了项目框架设计及代码模块设计. 设计思路 1.首先,根据用户需求合理分配代码密度,即: 用户直接操作(点击运行)的 ...

  3. 关于spring框架工作原理的初解

    一:spring基本概念 1)struts2是web框架,hibernate是orm框架 2)spring是容器框架,创建bean,维护bean之间的关系 3)spring可以管理web层,持久层,业 ...

  4. Python3基础 print(,end=) 输出内容的末尾加入空格

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  5. SwitchyOmega 设置修改代理

    在开发过程中,web.config中很多配置都是生产的域名,但是在本地开发时并不能用这些生产的地址,就想在打开该域名时跳转到本地ip地址. 修改方式: 1.hosts文件修改 这种方式是在整个电脑上面 ...

  6. Java8 Predicate

    code: package com.qhong; import java.util.ArrayList; import java.util.Arrays; import java.util.List; ...

  7. Python的hasattr() getattr() setattr() 函数使用方法详解--转载

    hasattr(object, name)判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False.需要注意的是name要用括号括起来 1 ...

  8. Less开发指南(三)- 代码文件跟踪调试

    案例背景:在大型网站中,css样式划分为多个模块文件,如reset.css,layout.css,skin.css等等(颗粒化越小,样式重用率越高),页面需要的时候引入它们即可! 回到less项目中这 ...

  9. IOS-网络(数据安全:MD5加密)

    // // ViewController.m // IOS_0129_HTTP请求 // // Created by ma c on 16/1/29. // Copyright © 2016年 博文科 ...

  10. js数字进制转换

    其他进制转十进制: 使用 parseInt()函数,parseInt解析一个字符串参数,并返回一个指定基数的整数 ,用法如下: parseInt(string, radix); 以二进制为例,用法如下 ...