`
michael.softtech
  • 浏览: 206392 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

spring源码分析之——spring aop原理

阅读更多

aop是spring中非常有趣的一个功能。如果应用得当会大有用处。现在从源码角度分析一下

 Spring aop的实现原理。

 

还是从上篇中提到的

<aop:config>

<aop:advisor>....</aop:advisor>

....

</aop:config>

 

这些配置信息的解析入手。spring中aop namespace的handler是AopNamespaceHandler。

其初始化代码如下(init方法调用的时机在上一篇分析事务时已经分析过了):

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

 可以看到,aop:config标签的解析类是:ConfigBeanDefinitionParser(解析类的调用时机在上一篇分析事务时也介绍过),其parse方法如下:

 

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

		configureAutoProxyCreator(parserContext, element);

		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 = parserContext.getDelegate().getLocalName(node);
				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;
	}

   它的功能大致有两块:1 . 注册一个AspectJAwareAdvisorAutoProxyCreator类型的bean。 2. 解析主标签下面的

   advisor标签,并且注册advisor.

   下面逐一分析一下这两个方面。

 

   1. AspectJAwareAdvisorAutoProxyCreator 类型bean 的注册。

       稍微从

configureAutoProxyCreator(parserContext, element);

    这一句跟踪一下很容易就发现最后到了AopConfigUtils的如下方法:

 

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;
		}
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

    注意参数里面的cls是AspectJAwareAdvisorAutoProxyCreator.class,这个是在前面把调用委托过来的时候直接写死的。这段代码注册了一个名为AUTO_PROXY_CREATOR_BEAN_NAME(org.springframework.aop.config.internalAutoProxyCreator)的bean。bean的类型是AspectJAwareAdvisorAutoProxyCreator。

     AspectJAwareAdvisorAutoProxyCreator到底有什么玄机呢?看了一下继承关系,赫然发现实现了InstantiationAwareBeanPostProcessor接口!这个接口是BeanPostProcessor的一个子类,在bean初始化的时候

调用。此接口的实现在AbstractAutoProxyCreator中(其他与本次分析无关的方法的实现在此没有列出):

 

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
		Object cacheKey = getCacheKey(beanClass, beanName);

		if (!this.targetSourcedBeans.contains(cacheKey)) {
			if (this.advisedBeans.contains(cacheKey) || this.nonAdvisedBeans.contains(cacheKey)) {
				return null;
			}
			if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
				this.nonAdvisedBeans.add(cacheKey);
				return null;
			}
		}

		// Create proxy here if we have a custom TargetSource.
		// Suppresses unnecessary default instantiation of the target bean:
		// The TargetSource will handle target instances in a custom fashion.
		TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
		if (targetSource != null) {
			this.targetSourcedBeans.add(beanName);
			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		return null;
	}

 

   这段代码的作用就是每当bean初始化前,检查是否需要生成代理对象。如果需要,就生成代理。

 

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);

   上面这段代码负责找到与当前bean相关联的Advisor(s).感兴趣的朋友可以继续追踪一下代码了解细节。大致思路就是

先找到所有实现了Advisor接口的bean,然后根据配置文件中的advisor配置从中挑出能cut到当前bean的advisor.

 

    接下来就是生成proxy了。代码如下:

 

protected Object createProxy(
			Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

		ProxyFactory proxyFactory = new ProxyFactory();
		// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
		proxyFactory.copyFrom(this);

		if (!shouldProxyTargetClass(beanClass, beanName)) {
			// Must allow for introductions; can't just set interfaces to
			// the target's interfaces only.
			Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
			for (Class<?> targetInterface : targetInterfaces) {
				proxyFactory.addInterface(targetInterface);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		for (Advisor advisor : advisors) {
			proxyFactory.addAdvisor(advisor);
		}

		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		return proxyFactory.getProxy(this.proxyClassLoader);
	}

     通过读这段代码,也就明白了当在配置bean的时候proxyTargetClass属性时如果需要生成代理使用cglib的原因了。

    因为如果配置了这个属性,那么生成代理的时候会掠过对bean接口的解析,从而只能使用cglib代理。

 

     OK.第一个方面分析基本完成了。

    下面分析一下文章开头提到的第二个方面:

    aop:advisor的解析。

    这个解析是在ConfigBeanDefinitionParse里面完成的:

 

private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
		AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
		String id = advisorElement.getAttribute(ID);

		try {
			this.parseState.push(new AdvisorEntry(id));
			String advisorBeanName = id;
			if (StringUtils.hasText(advisorBeanName)) {
				parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
			}
			else {
				advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
			}

			Object pointcut = parsePointcutProperty(advisorElement, parserContext);
			if (pointcut instanceof BeanDefinition) {
				advisorDef.getPropertyValues().add(POINTCUT, pointcut);
				parserContext.registerComponent(
						new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
			}
			else if (pointcut instanceof String) {
				advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
				parserContext.registerComponent(
						new AdvisorComponentDefinition(advisorBeanName, advisorDef));
			}
		}
		finally {
			this.parseState.pop();
		}
	}

    这段代码实际上就是生成了一个beanclass 为DefaultBeanFactoryPointcutAdvisor的advisor.

    我们在分析第一个方面的时候,曾经提到过bean初始化前会调用

postProcessBeforeInstantiation

    而这个方法会找到所有跟需要实例化的bean关联的advisor,然后产生proxy.

    OK.终于明白了Advisor是如何产生作用的了!

 

   Spring AOP的原理大致如下: 

   配置一个实现了InstantiationAwareBeanPostProcessor接口的bean。在每次bean初始化的时候找到所有advisor,根据pointcut 判断是不是需要为将实例化的bean生成代理,如果需要,就把advice编制在代理对象里面。

 

 

 

 

 

分享到:
评论
1 楼 jason- 2014-07-10  
我看AOP的源码也是你这个思路,但是在网上一搜,尼玛大多数的都说ProxyFactoryBean的getObject方法是AOP的入口,我就没想明白了,怎么开始用到ProxyFactoryBean的。  不知道你有何见解,有没有spring方面的群可以推荐下的,或者加我一起讨论啊?  445714070@qq.com

相关推荐

Global site tag (gtag.js) - Google Analytics