`

转:请不要再使用低级别的AOP API ( spring+struts有关问题)

阅读更多

http://jinnianshilongnian.iteye.com/blog/1901694

知识很合胃口艾,千万不要删除

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

 

 

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

 

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

 

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

 

所以我想做一件事情:

 

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

 

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

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

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

 

出问题的配置

Java代码 复制代码 收藏代码
  1. <bean class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator">  
  2.     <property name="proxyTargetClass" value="true"/>  
  3. </bean>  
  4. <tx:annotation-driven transaction-manager="transactionManager"/>   
    <bean class="org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator">
        <property name="proxyTargetClass" value="true"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/> 

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

 

但是如果是这样配置: 

Java代码 复制代码 收藏代码
  1. <aop:aspectj-autoproxy proxy-target-class="true"/>  
  2. <tx:annotation-driven transaction-manager="transactionManager"/>  
    <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处理: 

Java代码 复制代码 收藏代码
  1. registerBeanDefinitionParser("aspectj-autoproxy"new AspectJAutoProxyBeanDefinitionParser());  
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());

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

Java代码 复制代码 收藏代码
  1. public BeanDefinition parse(Element element, ParserContext parserContext) {  
  2.     AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);  
  3.     extendBeanDefinition(element, parserContext);  
  4.     return null;  
  5. }  
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		extendBeanDefinition(element, parserContext);
		return null;
	}

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

Java代码 复制代码 收藏代码
  1. return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);  
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);

但是注意了: 

Java代码 复制代码 收藏代码
  1. if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
  2.     BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
  3.     if (!cls.getName().equals(apcDefinition.getBeanClassName())) {  
  4.         int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());  
  5.         int requiredPriority = findPriorityForClass(cls);  
  6.         if (currentPriority < requiredPriority) {  
  7.             apcDefinition.setBeanClassName(cls.getName());  
  8.         }  
  9.     }  
  10.     return null;  
  11. }  
		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处理: 

Java代码 复制代码 收藏代码
  1. registerBeanDefinitionParser("annotation-driven"new AnnotationDrivenBeanDefinitionParser());  
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());

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

Java代码 复制代码 收藏代码
  1. public BeanDefinition parse(Element element, ParserContext parserContext) {  
  2.     String mode = element.getAttribute("mode");  
  3.     if ("aspectj".equals(mode)) {  
  4.         // mode="aspectj"  
  5.         registerTransactionAspect(element, parserContext);  
  6.     }  
  7.     else {  
  8.         // mode="proxy"  
  9.         AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);  
  10.     }  
  11.     return null;  
  12. }  
	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,其代码中第一句话是: 

Java代码 复制代码 收藏代码
  1. AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);  
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
Java代码 复制代码 收藏代码
  1. public static void registerAutoProxyCreatorIfNecessary(  
  2.             ParserContext parserContext, Element sourceElement) {  
  3.   
  4.         BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(  
  5.                 parserContext.getRegistry(), parserContext.extractSource(sourceElement));  
  6.         useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);  
  7.         registerComponentIfNecessary(beanDefinition, parserContext);  
  8.     }  
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是: 

Java代码 复制代码 收藏代码
  1. registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);  
registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
Java代码 复制代码 收藏代码
  1. private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {  
  2.         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");  
  3.         if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {  
  4.             BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);  
  5.             if (!cls.getName().equals(apcDefinition.getBeanClassName())) {  
  6.                 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());  
  7.                 int requiredPriority = findPriorityForClass(cls);  
  8.                 if (currentPriority < requiredPriority) {  
  9.                     apcDefinition.setBeanClassName(cls.getName());  
  10.                 }  
  11.             }  
  12.             return null;  
  13.         }  
  14. //省略  
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的朋友都应该知道如下配置:

Java代码 复制代码 收藏代码
  1. <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after -->  
  2. <!-- the lifecycleBeanProcessor has run: -->  
  3. <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>  
  4. <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
  5.     <property name="securityManager" ref="securityManager"/>  
  6. </bean>  
<!-- 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>

 

其实我们可以这样:

Java代码 复制代码 收藏代码
  1. <aop:config proxy-target-class="true"/>  
  2. <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
  3.     <property name="securityManager" ref="securityManager"/>  
  4. </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

 

分享到:
评论

相关推荐

    spring+struts2+ibatis整合的jar包

    在Java Web开发中,Spring、Struts2和iBatis是三个非常重要的框架,它们各自在不同的层面上提供了强大的功能。Spring是一个全面的后端应用框架,提供了依赖注入(DI)、面向切面编程(AOP)、事务管理等功能;Struts...

    CXF2.1.3+spring3.0+struts2.3.4

    通过CXF创建服务,利用Spring进行组件管理和依赖注入,再借助Struts2处理用户交互,可以构建出高效且稳定的业务系统。压缩包中的"CXFWebService"可能包含了项目配置、源代码、依赖库等资源,帮助开发者理解和学习...

    Spring+Struts2+JPA

    **Spring+Struts2+JPA 整合详解** 在Java Web开发中,Spring、Struts2和JPA是三个非常重要的框架。Spring作为一个全面的轻量级框架,提供了依赖注入(DI)和面向切面编程(AOP)等功能;Struts2是一个强大的MVC框架...

    java web 实战宝典_JSP+Servlet+Struts_2+Hibernate+Spring+Ajax

    根据标题、描述和部分内容,知识点覆盖范围主要集中在Java Web开发领域,并且包括了JSP、Servlet、Ajax以及SSH框架(Struts 2、Spring、Hibernate)的技术整合和实战案例分析。 JSP(Java Server Pages)是一种动态...

    spring+hibernate+struts

    标题 "spring+hibernate+struts" 指涉的是一个经典的Java Web开发框架组合,也被称为SSH(Spring、Struts和Hibernate)三位一体。这个组合在2000年代中期到后期非常流行,用于构建企业级应用。下面将详细阐述这三个...

    Spring+Struts2+Ibatis

    在"Spring+Struts2+Ibatis"的整合中,通常会使用Spring作为整个应用的基础架构,提供DI和AOP功能,管理Struts2的Action和iBatis的SqlSession。Struts2作为前端控制器,处理HTTP请求,调用Spring管理的Service层方法...

    JPA+Spring+Struts整合实例,JPA+Spring+Struts整合实例

    同时,可以使用Spring插件(struts2-spring-plugin)来实现Spring与Struts的集成,这样Action类可以直接从Spring容器获取服务层对象。 **4. 实现业务逻辑** 创建JPA实体类,使用`@Entity`注解标识。接着,创建对应...

    SSH项目(spring+hibernate+struts2)

    SSH项目是一种经典的Java Web开发框架组合,由Spring、Hibernate和Struts2三个开源框架组成。这个项目示例提供了一个基于这些技术的简单应用,帮助开发者理解如何将它们整合在一起进行实际开发。 **Spring框架**是...

    spring4+hiberante4+struts2+maven

    SSH是Java开发中的一种经典企业级应用框架组合,由Spring、Hibernate和Struts2组成,分别负责控制层、持久层和表现层。Spring是整个应用的核心,提供了强大的依赖注入(DI)和面向切面编程(AOP)功能,极大地简化了...

    hibernate+spring+struts开发api

    标题中的“hibernate+spring+struts开发api”指的是使用这三个开源框架进行API开发的技术栈。这三者是Java企业级应用开发中常见的组合,被称为经典的SSH(Struts、Spring、Hibernate)架构。 1. **Struts2**:...

    spring+struts2+ibatis简单例子

    在IT行业中,Spring、Struts2和iBatis是三个非常重要的开源框架,它们分别在不同的层面上解决了Java Web开发中的问题。Spring作为一款全面的后端应用框架,提供了依赖注入(DI)和面向切面编程(AOP)等功能;Struts...

    spring+hibernate+struts整合jar包

    Spring、Hibernate和Struts是Java开发中非常经典的三大框架,它们分别负责控制层、持久层和展示层,常被组合使用以构建企业级Web应用程序。本文将详细介绍这三大框架的整合过程及其关键知识点。 首先,Spring框架是...

    Spring+Struts+HibernateAPI

    1. `struts_api.chm`:这是Struts的API帮助文档,包含了所有Struts类和接口的详细信息,可以帮助开发者了解如何使用Struts的各种组件。 2. `Spring-Reference_zh_CN2.5.chm`:这是Spring 2.5版本的中文参考文档,...

    Spring+proxool+hibernate+struts2+aop_Jar包

    在IT行业中,Spring、Proxool、Hibernate、Struts2和AOP是五个非常关键的框架和概念,它们在企业级Java应用开发中扮演着重要角色。这些技术的整合使用可以构建出高效、灵活且可维护的Web应用程序。现在我们逐一深入...

    hibernate+spring+struts

    【标题】:“Hibernate+Spring+Struts”是一个经典的Java Web开发框架组合,也被称为SSH(Struts、Spring、Hibernate)。 【描述】:这个组合在Java应用开发领域中有着广泛的应用,尤其在企业级Web应用中,它们各自...

    MyBatis+Spring+Struts2

    【标题】"MyBatis+Spring+Struts2"是一个经典的Java Web开发框架组合,它在过去的许多年里被广泛使用。这个组合通常被称为SSH(Struts2、Spring、Hibernate)或者SSM(Spring、SpringMVC、MyBatis),但在这里我们...

    Spring + Struts2 需要的核心jar包,亲测

    - commons-logging.jar:Apache Commons Logging,日志抽象层,Spring和Struts2都使用它。 - commons-fileupload.jar和commons-io.jar:Apache Commons库,处理HTTP上传文件和IO操作。 - servlet-api.jar和jsp-...

    Hibernate+Spring+Struts2+ExtJS整合开发实例

    2. **读取(Read)**: 当需要显示数据时,Struts2 Action调用Service层,Service层通过Hibernate的Query或Criteria API从数据库中检索数据,然后返回给Action,Action再将数据封装成模型并转发到视图(ExtJS的Grid或...

    三大框架帮助文档合集(Spring3.0.2_API + Struts2.3.1_API + Hibernate3.2_API)

    Java开发中的三大框架——Spring、Struts和Hibernate,是企业级应用开发的基石,它们各自承担着不同的职责,共同构建了高效、稳定的后端架构。本文将深入探讨这三大框架的核心概念、主要功能以及如何相互协作,以...

    struts+spring+hibernate 英文 API

    Struts 是一个用于构建 MVC(模型-视图-控制器)架构的框架,Spring 提供了一个全面的依赖注入(DI)和面向切面编程(AOP)解决方案,而 Hibernate 则是一个对象关系映射(ORM)工具,它简化了数据库与Java对象之间...

Global site tag (gtag.js) - Google Analytics