`
agapple
  • 浏览: 1583409 次
  • 性别: 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文件来进行,大概有四种方式: 1. 配置ProxyFactoryBean,显式地设置advisors, advice, target等(基于代理的AOP ) ...4. 通过&lt;aop: aspectj-autoproxy&gt;来配置,使用AspectJ的注解来标识通知及切入点

    Spring2.5使用AOP需要的aspectJ

    Spring2.5开发 如果我们用到了AOP的话 一定需要aspectjrt.jar + aspectjweaver.jar 两个jar文件 这里面提供了 导入这两个就可以进行AOP开发了

    11spring4_aop3.rar

    xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ...

    spring aop代理列子

    package com.gc.autoproxy下为:aop方式自动代理 package com.gc.cglib下为:aop方式cglib代理 package com.gc.dynproxy下为:aop方式动态代理 package com.gc.javaproxy下为:java代理机制实现 package ...

    spring_aop4.rar_Home Home_jar 转换_spring AOP jar

    spring对AOP的支持 1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK...

    spring框架手动提交事务,jdbctample

    &lt;aop:aspectj-autoproxy proxy-target-class="true"&gt;&lt;/aop:aspectj-autoproxy&gt; xml中要有这句,可以解决子类类报错的问题。 或者去掉这句话,同时去掉public class AccountServiceImpl implements AccountService {...

    spring源码--AOP流程--笔记.docx

    aop分析笔记 个人总结所得 org.springframework.aop.framework.autoproxy

    矩阵::key:Nepxion Matrix是一个与Spring AutoProxy,Spring Registrar和Spring Import Selector集成的AOP框架,具有高通用性,鲁棒性,灵活性和可用性。面向注解的业务场景,包括代理,拦截,开关

    Nepxion矩阵 Nepxion Matrix是一种集成的Spring AutoProxy,Spring Registrar和Spring Import Selector三种机制的AOP框架,具有很高的通用性,健壮性,可选和易用性请联系我微信,钉钉,公众号和文档简介Spring自动...

    autoproxy-gfwlist:autoproxy-gfwlist的镜像

    autoproxy-gfwlist 的镜 ( )。 如果您无法加载 ,请使用以下命令: 或trunk分支中的一个: 不喜欢这个自述文件? 切换到,这与官方的svn repo完全相同。

    autoproxy-plus:现代化的AutoProxy,更快,更易于使用

    AutoProxy现代化,更快,更易于使用。 从。 Mozilla的附加评论对我来说太慢了。 第一次全面审核仍在排队中。 因此,我提供了这种替代安装方式。 在Mozilla的附加站点上。

    Spring.html

    注意:使用注解的方式,最终通知和后置通知顺序换了,建议使用环绕通知 注解 配置 声明式事务管理 PlatFormTransactionManager:平台事务管理器:定义了commit/rollback Mybatis/jdbc:...

    spring 的自动代理

    spring实现的自动代理配置!用到的关键类org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.4.2. Spring AOP中使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.7. 编程方式创建@AspectJ代理 6.8. 在Spring应用中使用AspectJ 6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 6.8.1.1....

    Spring 2.0 开发参考手册

    6.4.2. Spring AOP中使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.7. 编程方式创建@AspectJ代理 6.8. 在Spring应用中使用AspectJ 6.8.1. 在Spring中使用AspectJ来为domain object进行依赖注入 ...

    Spring中文帮助文档

    6.4.2. Spring AOP中使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.6.1. 理解AOP代理 6.7. 以编程方式创建@AspectJ代理 6.8. 在Spring应用中使用AspectJ 6.8.1. 在Spring中使用AspectJ进行domain ...

    Spring API

    6.4.2. Spring AOP中使用@AspectJ还是XML? 6.5. 混合切面类型 6.6. 代理机制 6.6.1. 理解AOP代理 6.7. 以编程方式创建@AspectJ代理 6.8. 在Spring应用中使用AspectJ 6.8.1. 在Spring中使用AspectJ进行domain ...

    spring chm文档

    6. 使用Spring进行面向切面编程(AOP) 6.1. 简介 6.1.1. AOP概念 6.1.2. Spring AOP的功能和目标 6.1.3. Spring的AOP代理 6.2. @AspectJ支持 6.2.1. 启用@AspectJ支持 6.2.2. 声明一个切面 6.2.3. 声明一个...

    spring_MVC源码

    03. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 04. xmlns:p=...

    Autoproxy10自动代理

    AutoProxy 能自动识别实际网络位置,并切换到相应的文件更改代理服务器设置,默认打印机和默认主页。  特性:  - 自动识别实际网络  - 自动启动网络配置(代理/打印机/初始页)  - 维护网络资料  - 这每个...

    AutoProxy修改版

    AutoProxy修改版,解决火狐自带的AutoProxy不能添加规则的问题

Global site tag (gtag.js) - Google Analytics