精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-05-30
最后修改:2011-05-31
背景公司在做监控迁移过程中,使用了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的动作。
结论结论其实很明显了
解决其实需求很简单,就是想针对指定的匹配的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使用的经验 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-05-31
楼主是有心人
|
|
返回顶楼 | |
发表时间:2011-05-31
最后修改:2011-05-31
请慎用aop:config,太夸张,标题太大了吧。我觉得有适用场景的问题。我认为你的标题改为如何用好aop:config更好。哈哈。慎用意味着让别人不用,会起到误导的作用,毕竟文中的问题是你们项目的特定场景,别人的项目未必会遇到。
|
|
返回顶楼 | |
发表时间:2011-05-31
fastwei 写道 请慎用aop:config,太夸张,标题太大了吧。我觉得有适用场景的问题。我认为你的标题改为如何用好aop:config更好。哈哈。慎用意味着让别人不用,会起到误导的作用,毕竟文中的问题是你们项目的特定场景,别人的项目未必会遇到。
恩,其实想表达的观点是: 类似DefaultAdvisorAutoProxyCreator(自动代理)和自定义的ProxyFactoryBean配置Advisor(手工配置)别混用,除非你对其有比较深刻的理解。 水还是比较深的,平时多看看源码,了解下spring的一些设计理念 |
|
返回顶楼 | |
浏览 2772 次