`
agapple
  • 浏览: 1595548 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

spring autoproxy问题(aop:config使用注意)

    博客分类:
  • java
阅读更多

背景

公司在做监控迁移过程中,使用了aop:config进行配置。 配置例子:

<aop:config>
<aop:advisor advice-ref="rpc-interceptor" pointcut-ref="rpc-pointcut"/>
</aop:config>

定义了一些advisor列表,但在实际过程中却遇到一些莫名奇妙的问题,就是原本不应该被生成代理的对象出现了jdk,cglib代理。

 

分析

aop:config具体的解析类为//AopNamespaceHandler.java  ,通过跟踪分析,最终会创建一个autoProxyCreator。

 

//==========================
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());
	}

//==========================
ConfigBeanDefinitionParser.java

public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		configureAutoProxyCreator(parserContext, element);//构造auto proxy

		NodeList childNodes = element.getChildNodes();
		for (int i = 0; i < childNodes.getLength(); i++) {
			Node node = childNodes.item(i);
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				String localName = node.getLocalName();
				if (POINTCUT.equals(localName)) {
					parsePointcut((Element) node, parserContext);
				}
				else if (ADVISOR.equals(localName)) {
					parseAdvisor((Element) node, parserContext);
				}
				else if (ASPECT.equals(localName)) {
					parseAspect((Element) node, parserContext);
				}
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}

private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
	AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element); //调用AopNamespaceUtils创建
	}

//==========================
AopNamespaceUtils.java

public static void registerAspectJAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {

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

//==========================
AopConfigUtils.java

public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
		return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);    //创建了AspectJAwareAdvisorAutoProxyCreator
	}

 

最后创建的AutoProxyCreator为: AspectJAwareAdvisorAutoProxyCreator

 

用过DefaultAdvisorAutoProxyCreator的应该知道:

1. 它会自动扫描当前spring容器中的Advisor,自动将当前容器中的bean进行代理

2. 至于Advisor是否会对bean生效,主要是取决于对应的pointcut的定义

3. 我们定义的MethodInterceptor,BeforeAdvice等一些Advice事件,本身不是一个完整的Advisor,所以不会被AutoProxyCreator使用。(都是在运行时被wrap成一个Advisor。具体的可以看一下ProxyFactoryBean的代码)

//===========================
//ProxyFactoryBean.java

private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); //定义了wrap器

//===========================
//DefaultAdvisorAdapterRegistry.java

public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}


	public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
		if (adviceObject instanceof Advisor) {
			return (Advisor) adviceObject;
		}
		if (!(adviceObject instanceof Advice)) {
			throw new UnknownAdviceTypeException(adviceObject);
		}
		Advice advice = (Advice) adviceObject;
		if (advice instanceof MethodInterceptor) {
			// So well-known it doesn't even need an adapter.
			return new DefaultPointcutAdvisor(advice);
		}
		for (int i = 0; i < this.adapters.size(); i++) {
			// Check that it is supported.
			AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
			if (adapter.supportsAdvice(advice)) {
				return new DefaultPointcutAdvisor(advice);
			}
		}
		throw new UnknownAdviceTypeException(advice);
	}

AutoProxyCreator机制

在autoProxyCreator javadoc中有明确的定义:


 

因为默认在AbstractAdvisorAutoProxyCreator定义了findCandidateAdvisors的行为: (扫描spring容器中所有的Advisor)

 

//===========================
//AbstractAdvisorAutoProxyCreator.java
protected List findCandidateAdvisors() {
		return this.advisorRetrievalHelper.findAdvisorBeans();
	}

//===========================
//BeanFactoryAdvisorRetrievalHelper.java
public List findAdvisorBeans() {
		// Determine list of advisor bean names, if not cached already.
		String[] advisorNames = null;
		synchronized (this) {
			advisorNames = this.cachedAdvisorBeanNames;
			if (advisorNames == null) {
				// Do not initialize FactoryBeans here: We need to leave all regular beans
				// uninitialized to let the auto-proxy creator apply to them!
				advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);  //从容器中获取Advisor的定义
				this.cachedAdvisorBeanNames = advisorNames;
			}
		}
		.......
		}
		return advisors;
	}

 

 

而在DefaultAdvisorAutoProxyCreator和AspectJAwareAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator等,都是有扫描所有Advisor的动作。

 

结论

结论其实很明显了

  1. 因为使用了aop:config,创建了一个AspectJAwareAdvisorAutoProxyCreator
  2. AspectJAwareAdvisorAutoProxyCreator会扫描所有的Advisor,检查下pointCut是否匹配
  3. 我们定义的Advisor中的pointcut,spring的规范是要求:[classname] + [method]. 而我们因为先前都使用了ProxyFactoryBean,都是绑定在一个class使用,所以classname使用了通配符,所以最后的配置为: [.*] + method
  4. 正因为使用了 [.*] + method的配置,导致该Advisor定义应用到了所有bean,这也就出现了问题。

解决

其实需求很简单,就是想针对指定的匹配的beanName,应用指定的Advisor。

所以最后可以选择BeanNameAutoProxyCreator,如果觉得需要支持pattern的匹配,可以选择自己扩展。

 

 

最后

几点思考: 

1.  AutoProxyCreator 和 手工使用ProxyFactoryBean配置Advisor,尽量别混合使用

2.  深刻了解下spring中Advisor的设计,pointcut和advice。 pointcut是基于 [classname] + [method] 匹配

3.  避免多次的AutoProxyCreator处理,重复的cglib的代理会出现啥情况,相信大家都知道。针对这样的需求,自己实现了一个融合多个AutoProxyCreator的cglib代理的实现,可以参考: spring的auto-proxy自动代理(融合机制实现)

 

最后,整个问题的排查过程,主要是我的一个同事给力的比较多,因为对aop:config不熟悉,我只是提供一些spring aop使用的经验

分享到:
评论
3 楼 agapple 2011-05-31  
fastwei 写道
请慎用aop:config,太夸张,标题太大了吧。我觉得有适用场景的问题。我认为你的标题改为如何用好aop:config更好。哈哈。慎用意味着让别人不用,会起到误导的作用,毕竟文中的问题是你们项目的特定场景,别人的项目未必会遇到。


恩,其实想表达的观点是:
类似DefaultAdvisorAutoProxyCreator(自动代理)和自定义的ProxyFactoryBean配置Advisor(手工配置)别混用,除非你对其有比较深刻的理解。
水还是比较深的,平时多看看源码,了解下spring的一些设计理念
2 楼 fastwei 2011-05-31  
请慎用aop:config,太夸张,标题太大了吧。我觉得有适用场景的问题。我认为你的标题改为如何用好aop:config更好。哈哈。慎用意味着让别人不用,会起到误导的作用,毕竟文中的问题是你们项目的特定场景,别人的项目未必会遇到。
1 楼 pocketduck 2011-05-31  
楼主是有心人

相关推荐

    spring aop注解方式、xml方式示例

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种强大的方式来实现横切关注点,如日志、事务管理、性能监控等,而无需侵入业务代码。下面将详细介绍Spring AOP的注解方式和XML配置方式。 ### ...

    day39 07-Spring的AOP:自动代理

    当我们在配置文件中启用&lt;aop:aspectj-autoproxy/&gt;或在代码中使用@EnableAspectJAutoProxy注解时,Spring会自动为包含切面注解(@Aspect)的bean创建代理。 切面通常由以下几个部分组成: 1. **通知(Advice)**:这...

    Spring中的AOP不生效

    ### Spring中的AOP不生效的原因及解决方法 在Java开发中,面向切面编程(Aspect Oriented Programming,简称AOP)是一种重要的编程思想和技术手段,主要用于处理横切关注点问题,如日志记录、性能统计、安全控制、...

    Spring AOP 的实现例子(基于XML配置实现)

    `&lt;aop:config&gt;`是Spring的原生AOP配置,而`&lt;aop:aspectj-autoproxy&gt;`则允许我们使用AspectJ的注解进行AOP配置。 接下来,定义切面(Aspect)。在Spring AOP中,切面是包含一组通知(Advice)的类,这些通知会在特定...

    Spring Aop四个依赖的Jar包

    要使用Spring AOP,通常需要引入以下几个核心的Jar包: 1. **aspectj-1.7.3.jar**:这是AspectJ库的核心部分,提供了AOP语言支持,包括AspectJ编译器和运行时库。AspectJ是Java平台上的一个开源项目,它扩展了Java...

    spring_aop_cglib的实现方式

    在Spring配置中,如果我们希望使用CGLIB代理,可以在`&lt;aop:config&gt;`或`&lt;aop:aspectj-autoproxy&gt;`元素下添加`&lt;aop:proxy&gt;`子元素,并设置`proxy-target-class="true"`。例如: ```xml &lt;aop:config&gt; &lt;aop:aspect id=...

    spring-aop标签和配置文件两种方式实例

    本实例将探讨如何在Spring中使用XML配置文件和AOP标签来实现这一功能,特别是针对Spring 2.5及以上版本。 首先,我们来看XML配置文件方式。在Spring中,AOP可以通过`&lt;aop:config&gt;`标签来配置。下面是一个基本的AOP...

    spring_aop_xml.rar_java aop_xml aop

    在实际的`spring_aop_xml`压缩包中,应该包含了相关的XML配置文件、服务接口及其实现类、通知类等,通过这些文件可以更好地理解和学习如何在XML中配置和使用Spring AOP。通过深入学习和实践,你将能熟练掌握这一强大...

    spring 2.0使用AOP实例(基于XML的配置方式)

    在XML配置中,我们首先需要启用AOP代理,通过`&lt;aop:aspectj-autoproxy&gt;`元素开启。然后,我们可以定义一个切面,如: ```xml &lt;aop:config&gt; &lt;aop:aspect id="loggingAspect" ref="loggingService"&gt; &lt;!-- 定义切点 ...

    SpringAOP依赖包

    通过上述方式,我们可以轻松地在Spring应用中集成并使用AOP,实现对系统横切关注点的统一管理。同时,AOP的使用还可以提高代码的复用性和模块化,降低系统的复杂度。在实际项目中,结合事务管理、日志记录、性能监控...

    Spring AOP需要的jar

    本文将详细介绍在使用Spring AOP时所需的两个关键jar包:aopalliance-1.0.jar和aspectjweaver-1.8.9.jar。 首先,让我们了解一下什么是Spring AOP。AOP(Aspect-Oriented Programming,面向切面编程)是一种编程...

    SpringAop两种配置demo

    首先,在Spring的配置文件中启用AOP支持,需要添加`&lt;aop:config&gt;`标签: ```xml &lt;aop:config&gt; &lt;!-- 这里可以配置切点、通知等 --&gt; &lt;/aop:config&gt; ``` #### 2. 定义切点(Pointcut) 切点是AOP的核心,它定义了...

    spring aop xml实现

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它允许程序员在不修改源代码的情况下,通过在关键点(切点)插入拦截器(通知)来增强功能,实现横切关注点,如日志、事务管理等。XML配置是Spring AOP早期...

    spring AOP myeclipse 完整代码

    1. **创建Bean**:在Spring的配置文件(如`applicationContext.xml`)中定义你需要使用AOP的Bean。例如,你可能有一个名为`UserService`的服务类,它包含了业务逻辑,需要被AOP代理。 ```xml ``` 2. **定义切面**...

    用xml配置的方式进行SpringAOP开发

    可以使用`&lt;aop:config&gt;`或`&lt;aop:aspectj-autoproxy&gt;`标签来实现: ```xml &lt;aop:config&gt; &lt;aop:advisor id="loggingAdviceRef" advice-ref="loggingAdvisor"/&gt; &lt;/aop:config&gt; ``` 或者 ```xml &lt;aop:aspectj-autoproxy...

    Spring_AOP_XML配置

    **Spring AOP XML配置**是Spring框架中一种重要的面向切面编程(Aspect-Oriented Programming,简称AOP)实现方式,允许开发者定义“横切关注点”,如日志、事务管理等,这些关注点可以独立于业务代码进行,提高代码...

    spring的aop配置

    - 配置切面:使用`&lt;aop:aspect&gt;`标签创建切面,定义通知和切入点的关系。 2. 基于注解的配置 - 在需要增强的方法上直接使用注解,如`@Before`、`@After`、`@Around`等定义通知,使用`@Pointcut`定义切入点。例如...

    spring-aop-xml.rar(资源免积分/C币)

    1. **配置Spring容器**:首先,需要在Spring配置文件中启用AOP代理,通常通过`&lt;aop:aspectj-autoproxy&gt;`元素来实现。 2. **定义切面**:创建一个带有`@Aspect`注解的Java类,其中包含各种通知方法。例如,一个简单...

    SpringAop学习笔记以及实现Demo

    - 在Spring配置中启用注解驱动的AOP,`&lt;aop:aspectj-autoproxy&gt;`。 3. **基于XML配置的AOP**: - 定义切面类,无需特殊注解。 - 在XML配置文件中定义`&lt;aop:config&gt;`,并创建`&lt;aop:pointcut&gt;`定义切入点。 - ...

    Spring AOP

    这需要在`&lt;aop:config&gt;`元素下使用`&lt;aop:aspect&gt;`来创建切面,并使用`&lt;aop:before&gt;`, `&lt;aop:after&gt;`, `&lt;aop:after-returning&gt;`, `&lt;aop:after-throwing&gt;` 和 `&lt;aop:around&gt;`来定义通知。 例如: ```xml &lt;aop:config...

Global site tag (gtag.js) - Google Analytics