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 /> <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 不同配置方式产生的冲突问题的更多相关文章
- spring 5.x 系列第4篇 —— spring AOP (代码配置方式)
文章目录 一.说明 1.1 项目结构说明 1.2 依赖说明 二.spring aop 2.1 创建待切入接口及其实现类 2.2 创建自定义切面类 2.3 配置切面 2.4 测试切面 2.5 切面执行顺 ...
- spring 5.x 系列第3篇 —— spring AOP (xml配置方式)
文章目录 一.说明 1.1 项目结构说明 1.2 依赖说明 二.spring aop 2.1 创建待切入接口及其实现类 2.2 创建自定义切面类 2.3 配置切面 2.4 测试切面 附: 关于切面表达 ...
- 基于注解的Spring AOP的配置和使用
摘要: 基于注解的Spring AOP的配置和使用 AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.可以通过预编译方式和运行期动态代理实现在不 ...
- spring aop 使用注解方式总结
spring aop的注解方式:和xml的配置方式略有区别,详细如下: 1.首先还是建立需要的切面类:切面类里面定义好切点配置,以及所有的需要实现的通知方法. /** * */ package com ...
- 循序渐进之Spring AOP(3) - 配置代理
上一篇介绍了几种Advice(增强),并通过代码演示了生成代理的方式,下面来看通过配置文件配置方式把Advice织入目标类. 注意,配置文件方式仍然不是spring AOP的最好方式,学习配置方式也是 ...
- spring AOP为什么配置了没有效果?
spring Aop的配置一定要配置在springmvc配置文件中 springMVC.xml 1 <!-- AOP 注解方式 :定义Aspect --> <!-- ...
- SpringBoot学习(二)-->Spring的Java配置方式
二.Spring的Java配置方式 Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置. 1.@Configuration 和 @Bean Spring的Java配置方式是通过 @ ...
- Spring 的java 配置方式
Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置. 1.1@Configuration 和 @Bean Spring的Java配置方式是通过 @Configuration 和 @ ...
- Spring的Java配置方式—@Configuration和@Bean实现Java配置
Java配置是Spring4.x推荐的配置方式,可以完全替代xml配置. 1.@Configuration 和 @BeanSpring的Java配置方式是通过 @Configuration 和 @Be ...
随机推荐
- 20145219《网络对抗》PC平台逆向破解
20145219<网络对抗>PC平台逆向破解 shellcode注入 1.shellcode shellcode是一段代码,溢出后,执行这段代码能开启系统shell. 2.构造方法 (1) ...
- Javaworkers团队第二周项目总结
本周项目进展 本周我们小组成员在图书馆和网上查阅了很多资料,基本达到了预期的计划,完成了项目框架设计及代码模块设计. 设计思路 1.首先,根据用户需求合理分配代码密度,即: 用户直接操作(点击运行)的 ...
- 关于spring框架工作原理的初解
一:spring基本概念 1)struts2是web框架,hibernate是orm框架 2)spring是容器框架,创建bean,维护bean之间的关系 3)spring可以管理web层,持久层,业 ...
- Python3基础 print(,end=) 输出内容的末尾加入空格
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- SwitchyOmega 设置修改代理
在开发过程中,web.config中很多配置都是生产的域名,但是在本地开发时并不能用这些生产的地址,就想在打开该域名时跳转到本地ip地址. 修改方式: 1.hosts文件修改 这种方式是在整个电脑上面 ...
- Java8 Predicate
code: package com.qhong; import java.util.ArrayList; import java.util.Arrays; import java.util.List; ...
- Python的hasattr() getattr() setattr() 函数使用方法详解--转载
hasattr(object, name)判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False.需要注意的是name要用括号括起来 1 ...
- Less开发指南(三)- 代码文件跟踪调试
案例背景:在大型网站中,css样式划分为多个模块文件,如reset.css,layout.css,skin.css等等(颗粒化越小,样式重用率越高),页面需要的时候引入它们即可! 回到less项目中这 ...
- IOS-网络(数据安全:MD5加密)
// // ViewController.m // IOS_0129_HTTP请求 // // Created by ma c on 16/1/29. // Copyright © 2016年 博文科 ...
- js数字进制转换
其他进制转十进制: 使用 parseInt()函数,parseInt解析一个字符串参数,并返回一个指定基数的整数 ,用法如下: parseInt(string, radix); 以二进制为例,用法如下 ...