`
jinnianshilongnian
  • 浏览: 21435359 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2405208
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:2997857
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5631575
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:257590
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1593252
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:248993
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5847698
Group-logo
跟我学Nginx+Lua开...
浏览量:698206
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:780544
社区版块
存档分类
最新评论

请不要再使用低级别的AOP API

阅读更多

 

在iteye上,咨询我Spring问题中最多的一个就是:AOP方面的问题,我之前也写过类似的帖子解答那些疑问:

 

 

大家有兴趣可以参考我的《java开发常见问题分析》分类。里边收集了许多在开发过程中我遇到的或别人遇到的问题。

 

其中最主要的问题一方面是我们使用问题,另一方面是其他原因(比如bug、设计缺陷等)。

 

Spring已经太庞大了,大的连我这个熟手有时候都载了。

 

所以我想做一件事情:

 

很希望大家遇到问题时能反馈给我,我做个收集,方便后来人。点击这前往《 那些年我们遇到的各种坑

 

当然spring还是足够好的,只讨论问题,不讨论其他。

===========================================================

在此我再给大家举一个使用低级别AOP API遇到的坑。

 

出问题的配置

    <bean class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator">
        <property name="proxyTargetClass" value="true"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/> 

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

 

但是如果是这样配置: 

    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <tx:annotation-driven transaction-manager="transactionManager"/>

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

 

分析

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

registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

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

	public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		extendBeanDefinition(element, parserContext);
		return null;
	}

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

return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);

但是注意了: 

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

大家可以看到一句话: 

  • 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处理: 

registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());

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

	public BeanDefinition parse(Element element, ParserContext parserContext) {
		String mode = element.getAttribute("mode");
		if ("aspectj".equals(mode)) {
			// mode="aspectj"
			registerTransactionAspect(element, parserContext);
		}
		else {
			// mode="proxy"
			AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
		}
		return null;
	}

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

AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
public static void registerAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {

		BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

AopConfigUtils.registerAutoProxyCreatorIfNecessary是: 

registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.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;
		}
//省略

此处我们又看到了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的朋友都应该知道如下配置:

<!-- Enable Shiro Annotations for Spring-configured beans.  Only run after -->
<!-- the lifecycleBeanProcessor has run: -->
<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 proxy-target-class="true"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

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

 

可以参考我的配置spring-mvc-shiro.xml

 

 

21
3
分享到:
评论
15 楼 archerie 2017-04-19  
谢谢开涛大神的分享,完美的解决了我的问题,也从 es 项目中学到很多,谢谢了。。。。。。。。。。
14 楼 a1219542638 2016-12-21  
谢谢大神的分享
13 楼 tony1016 2014-08-21  
我擦,为了不让cglib去代理final类和没有默认构造器的类,继承重写了AnnotationAwareAspectJAutoProxyCreator,结果cglib怎么都不工作,原来是这个坑。非常感谢。
12 楼 qq122343779 2014-04-21  
 
11 楼 jinnianshilongnian 2013-07-10  
iq527 写道


如果解决 -> 如何解决

1、没有必要永远不自己使用低级别API,如之上自己去创建AutoProxyCreator

没有必要永远不...

谢谢 

确实不通顺。。我改了下
“如果没有必要,请不要使用低级别API”
10 楼 iq527 2013-07-10  


如果解决 -> 如何解决

1、没有必要永远不自己使用低级别API,如之上自己去创建AutoProxyCreator

没有必要永远不...
9 楼 elan1986 2013-07-10  
mark!!!
8 楼 jinnianshilongnian 2013-07-10  
jacking124 写道
感谢分享,受益匪浅!!

7 楼 jacking124 2013-07-10  
感谢分享,受益匪浅!!
6 楼 jinnianshilongnian 2013-07-09  
hgpeng 写道
感谢龙年分享的每个贴,都是精华啊

精华不敢 都是坑啊
5 楼 hgpeng 2013-07-09  
感谢龙年分享的每个贴,都是精华啊
4 楼 jinnianshilongnian 2013-07-09  
white_crucifix 写道
楼主就是spring的活的百科全书啊。。。

这个肯定不敢,好多其他的spring相关框架都没用过 如webservice这块
3 楼 white_crucifix 2013-07-09  
楼主就是spring的活的百科全书啊。。。
2 楼 jinnianshilongnian 2013-07-09  
kyfxbl 写道
龙年的spring贴,我要友情DING一下

感谢
1 楼 kyfxbl 2013-07-09  
龙年的spring贴,我要友情DING一下

相关推荐

    com-aop-annotation-permission-demo:源码主要是学习SpringBoot + AOP +注解,通过切面实现日志记录和请求权限校验功能,日志通过logback-spring.xml配置按不同日志级别记录输出到文件,内容包括使用JWT用户令牌验证机制,以及使用Spring Security拦截请求和过虑站点请求,使用Swagger-UI 2.9.2提供API可视化调试操作

    com-aop注释权限演示

    springboot学习

    chapter4-2-4:使用AOP统一处理Web请求日志 chapter4-2-5:使用log4j记录日志到MongoDB chapter4-2-6:Spring Boot 1.5.x新特性:动态修改日志级别] 安全管理 chapter4-3-1:使用Spring Security chapter4-3-2:...

    spring boot demo 是一个用来深度学习并实战 spring boot 的项目

    该项目已成功集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过AOP记录web请求日志)、统一异常处理(json级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、Beetl(模板引擎)、Enjoy...

    Spring.html

    BeanFactory:使用这个工厂创建对象的方式都是懒加载,在调用的时候再创建 ClassPathXmlApplicationContext:使用这个工厂创建对象,他会根据scope智能判断是否懒加载,如果是单例则创建容器时就会创建里面bean的实例,...

    spring-boot-demo_xkcoding.tar.gz

    该项目已成功集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过AOP记录web请求日志)、统一异常处理(json级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、Beetl(模板引擎)、Enjoy...

    关于和spring-boot相关的demo大全

    该项目已成功集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过AOP记录web请求日志)、统一异常处理(json级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、Beetl(模板引擎)、Enjoy...

    Spring.3.x企业应用开发实战(完整版).part2

    16.7.2 使用Spring Servlet API模拟对象 16.7.3 使用Spring RestTemplate测试 16.7.4 使用Selenium测试 16.8 小结 第17章 实战案例开发 17.1 论坛案例概述 17.1.1 论坛整体功能结构 17.1.2 论坛用例描述 17.1.3 主要...

    Spring3.x企业应用开发实战(完整版) part1

    16.7.2 使用Spring Servlet API模拟对象 16.7.3 使用Spring RestTemplate测试 16.7.4 使用Selenium测试 16.8 小结 第17章 实战案例开发 17.1 论坛案例概述 17.1.1 论坛整体功能结构 17.1.2 论坛用例描述 17.1.3 主要...

    spring.doc

    2.3 Spring API 8 3 Spring基本功能详解 8 3.1 SpringIOC 8 3.2别名Alias 11 别名拓展: 11 3.3 Spring容器内部对象的创建 12 Spring容器内部对象创建拓展: 12 3.3.1使用类构造器实例化(默认无参数) 14 3.3.2使用...

    ArchetypeProject:Spring Web 项目原型(创建原型)

    使用spring AOP实现参数校验 使用 logback 实现日志 使用 logback 功能调整特定信息的用户日志级别(默认:info -&gt; debug) DB连接设置为使用MyBatis和CUBRID 使用Redis的查找方法缓存功能 基本 CRUD 屏幕 REST ...

    spring boot集成demo大全.zip

    该项目已成功集成 actuator(`监控`)、admin(`可视化监控`)、logback(`日志`)、aopLog(`通过AOP记录web请求日志`)、统一异常处理(`json级别和页面级别`)、freemarker(`模板引擎`)、thymeleaf(`模板引擎`)、Beetl(`...

    java8源码-sprintboot_test:sprintboot_test

    java8源码sbia_examples: SpringBoot 实战源代码 Fourinone-3.04.25:详细的系统架构与设计实战源代码 Spring-Boot-2.0-Samples:spring ...请注意,此项目需要使用 JDK 8 构建。 jpa21 - 显示对 JPA 2.1 特

    低清版 大型门户网站是这样炼成的.pdf

    低清版51M 大型门户网站是这样炼成的!(Struts 2+Spring 2+Hibernate 3).pdf(完整版) 网上有高清版350M的。可以去下 http://115.com/file/be5gwid8 请于下载后 24H 内及时删除!请抱着学习的态度下载此资料。 ...

    spring boot 全面的样例代码

    - chapter4-2-4:[使用AOP统一处理Web请求日志](http://blog.didispace.com/springbootaoplog/) - chapter4-2-5:[使用log4j记录日志到MongoDB](http://blog.didispace.com/springbootlog4jmongodb/) - chapter4-2-6...

    Java语言基础下载

    事务的级别控制 343 使用存储过程 345 操作元数据 347 ResultSetMetaData(结果集元数据) 349 可滚动的和可更新的结果集 350 批处理更新 354 二进制大对象BLOB 357 RowSet 新特性 359 JdbcRowSet 360 ...

    J2EE开发全程实录(JAVA项目开发)

    1.2.2 Java中的正则表达式API 5 1.2.3 java.util.regex的使用... 6 1.3 实战正则表达式... 8 第2章 程序最优化.... 14 2.1 空间与时间... 14 2.1.1 空间与时间的概念和度量... 14 2.1.2 空间与时间的背反... 15 ...

    Java源码 SpringMVC Mybatis Shiro Bootstrap Rest Webservice

    项目Maven构建,真实大型互联网架构,做到高并发,大数据处理,整个项目使用定制化服务思想,提供模块化、服务化、原子化的方案,将功能模块进行拆分,可以公用到所有的项目中。架构采用分布式部署架构,所有模块...

    spring-boot示例项目

    Spring Boot简化了基于Spring的应用开发,通过少量的代码就能创建一个独立的、产品级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数Spring Boot应用只...

    InfraRED-开源

    Java / J2EE应用程序性能监视工具。 功能:轻量级的探查器,基于AOP,列出API时间,JDBC / SQL统计信息,中央日志记录,集群支持,Web UI,调用树,可配置的监视级别。

    JAVA核心知识点整理(有效)

    1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................

Global site tag (gtag.js) - Google Analytics