在iteye上,咨询我Spring问题中最多的一个就是:AOP方面的问题,我之前也写过类似的帖子解答那些疑问:
- 注入FactoryBean失败分析+解决方案
- spring的二次代理原因及如何排查
- struts2+spring集成bug——使用AOP时可能遇到的问题分析
- 关于spring的aop拦截的问题 protected方法代理问题
- Spring事务不起作用 问题汇总
- Shiro+Struts2+Spring3 加上@RequiresPermissions 后@Autowired失效
- 如何为spring代理类设置属性值
- 在spring中获取代理对象代理的目标对象工具类
- Spring事务处理时自我调用的解决方案及一些实现方式的风险
大家有兴趣可以参考我的《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。
相关推荐
com-aop注释权限演示
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:...
该项目已成功集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过AOP记录web请求日志)、统一异常处理(json级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、Beetl(模板引擎)、Enjoy...
BeanFactory:使用这个工厂创建对象的方式都是懒加载,在调用的时候再创建 ClassPathXmlApplicationContext:使用这个工厂创建对象,他会根据scope智能判断是否懒加载,如果是单例则创建容器时就会创建里面bean的实例,...
该项目已成功集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过AOP记录web请求日志)、统一异常处理(json级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、Beetl(模板引擎)、Enjoy...
该项目已成功集成 actuator(监控)、admin(可视化监控)、logback(日志)、aopLog(通过AOP记录web请求日志)、统一异常处理(json级别和页面级别)、freemarker(模板引擎)、thymeleaf(模板引擎)、Beetl(模板引擎)、Enjoy...
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 主要...
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 主要...
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使用...
使用spring AOP实现参数校验 使用 logback 实现日志 使用 logback 功能调整特定信息的用户日志级别(默认:info -> debug) DB连接设置为使用MyBatis和CUBRID 使用Redis的查找方法缓存功能 基本 CRUD 屏幕 REST ...
该项目已成功集成 actuator(`监控`)、admin(`可视化监控`)、logback(`日志`)、aopLog(`通过AOP记录web请求日志`)、统一异常处理(`json级别和页面级别`)、freemarker(`模板引擎`)、thymeleaf(`模板引擎`)、Beetl(`...
java8源码sbia_examples: SpringBoot 实战源代码 Fourinone-3.04.25:详细的系统架构与设计实战源代码 Spring-Boot-2.0-Samples:spring ...请注意,此项目需要使用 JDK 8 构建。 jpa21 - 显示对 JPA 2.1 特
低清版51M 大型门户网站是这样炼成的!(Struts 2+Spring 2+Hibernate 3).pdf(完整版) 网上有高清版350M的。可以去下 http://115.com/file/be5gwid8 请于下载后 24H 内及时删除!请抱着学习的态度下载此资料。 ...
- chapter4-2-4:[使用AOP统一处理Web请求日志](http://blog.didispace.com/springbootaoplog/) - chapter4-2-5:[使用log4j记录日志到MongoDB](http://blog.didispace.com/springbootlog4jmongodb/) - chapter4-2-6...
事务的级别控制 343 使用存储过程 345 操作元数据 347 ResultSetMetaData(结果集元数据) 349 可滚动的和可更新的结果集 350 批处理更新 354 二进制大对象BLOB 357 RowSet 新特性 359 JdbcRowSet 360 ...
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 ...
项目Maven构建,真实大型互联网架构,做到高并发,大数据处理,整个项目使用定制化服务思想,提供模块化、服务化、原子化的方案,将功能模块进行拆分,可以公用到所有的项目中。架构采用分布式部署架构,所有模块...
Spring Boot简化了基于Spring的应用开发,通过少量的代码就能创建一个独立的、产品级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数Spring Boot应用只...
Java / J2EE应用程序性能监视工具。 功能:轻量级的探查器,基于AOP,列出API时间,JDBC / SQL统计信息,中央日志记录,集群支持,Web UI,调用树,可配置的监视级别。
1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................