从springAOP的调用过程解析aop源码之一——aop使用的疑惑

场景构建:使用springAOP实现调用所有方法前后打印日志的功能,比较spring容器中对象和自己创建的对象方法调用中的表现。
applicationContext.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    http://www.springframework.org/schema/aop
	http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
	http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	<context:component-scan
		base-package="com.shidebin.mongodb.springAop2"></context:component-scan>
	<aop:aspectj-autoproxy proxy-target-class="true" />
</beans>

AopConfiguration类:

package com.shidebin.mongodb.springAop2;

import java.beans.PropertyEditor;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;
@Aspect
@Component
public class AopConfiguration {

	Logger logger = null;  
	//修饰词任意 com.shidebin包及其子包下的方法参数任意的方法
	@Before("execution(* com.shidebin..*.*(..))")
	void beforeAdvice(JoinPoint point) {
		Object[] args = point.getArgs();
		Map<String,Object> argMap = new HashMap<String,Object>();
		for(int i = 0; i<args.length;i++) {
			argMap.put("arg"+i, args[i]);
		}
		Signature signature = point.getSignature();
		logger = Logger.getLogger(signature.getDeclaringType());
		//方法名
		String name = signature.getName();
		String jsonString = JSONObject.toJSONString(argMap);
		logger.info("调用方法:<"+name+">\n方法参数为:\n"+jsonString);
	}
	//修饰词public 任意包下的方法参数任意的方法
	@After("execution(public *  *(..))")
	void AftereAdvice(JoinPoint point) {
		//方法名
		Signature signature = point.getSignature();
		logger = Logger.getLogger(signature.getDeclaringType());
		String name = signature.getName();
		logger.info("方法:<"+name+">调用完成");
	}
	//修饰词public 任意包下的方法参数任意的方法
	@AfterReturning(value = "execution(public * *(..))",returning="result")
	void AfterReturnAdvice(JoinPoint point, Object result) {
		// 方法名
		Signature signature = point.getSignature();
		logger = Logger.getLogger(signature.getDeclaringType());
		String name = signature.getName();
		logger.info("调用方法:<" + name + ">的返回值为:"+JSONObject.toJSONString(result));
	}
	//修饰词任意 任意包下的方法参数任意的方法
	@AfterThrowing(value = "execution(**  *(..))",throwing="e")
	void afterThrowingAdvice(JoinPoint point,Exception e) {
		Object[] args = point.getArgs();
		Map<String,Object> argMap = new HashMap<String,Object>();
		for(int i = 0; i<args.length;i++) {
			argMap.put("arg"+i, args[i]);
		}
		Signature signature = point.getSignature();
		logger = Logger.getLogger(signature.getDeclaringType());
		//方法名
		String name = signature.getName();
		String jsonString = JSONObject.toJSONString(argMap);
		logger.info("调用方法:<"+name+">\n方法参数为:\n"+jsonString+"发生异常,异常如下:\n");
		e.printStackTrace();
	}
	//修饰词public 任意包下的方法参数任意的方法
	@Around("execution(public * *(..))")
	Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {
		Object[] args = point.getArgs();
		Map<String,Object> argMap = new HashMap<String,Object>();
		for(int i = 0; i<args.length;i++) {
			argMap.put("arg"+i, args[i]);
		}
		Signature signature = point.getSignature();
		logger = Logger.getLogger(signature.getDeclaringType());
		//方法名
		String name = signature.getName();
		String jsonString = JSONObject.toJSONString(argMap);
		logger.info("调用方法:<"+name+">\n方法参数为:\n"+jsonString);
		Object proceed = point.proceed();
		logger.info("方法:<"+name+">调用完成");
		return proceed;
	}
}

test类:

package com.shidebin.mongodb.springAop;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.shidebin.mongodb.springAop2.Computer;
import com.shidebin.mongodb.springAop2.User;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class AopAnnotationTest {

	@Autowired
	public Computer computer;
	@Autowired
	public ApplicationContext app;
	@Test
	public void test() {
		User user = new User();
		user.setUsername("shidebin");
		user.setCountry("china");
		user.setAge(29);
		computer.work(user,"win7");
		System.out.println("Autowired computer running");
		Computer computer2 = new Computer();
		computer2.work(user,"win7");
		System.out.println("cunstomer computer running");
	}
}

测试表现:

2019-02-27 16:42:58 INFO [com.shidebin.mongodb.springAop2.Computer] 
 调用方法:<work>
方法参数为:
{"arg1":"win7","arg0":{"age":29,"country":"china","lenght":0.0,"username":"shidebin"}}
  2019-02-27 16:42:58 INFO [com.shidebin.mongodb.springAop2.Computer] 
 调用方法:<work>
方法参数为:
{"arg1":"win7","arg0":{"age":29,"country":"china","lenght":0.0,"username":"shidebin"}}
  shidebin正在使用win7
2019-02-27 16:42:58 INFO [com.shidebin.mongodb.springAop2.Computer] 
 方法:<work>调用完成
  2019-02-27 16:42:58 INFO [com.shidebin.mongodb.springAop2.Computer] 
 方法:<work>调用完成
  2019-02-27 16:42:58 INFO [com.shidebin.mongodb.springAop2.Computer] 
 调用方法:<work>的返回值为:null
  Autowired computer running
shidebin正在使用win7
cunstomer computer running

可以看出:spring管理的Computer对象才具有aop功能,在方法调用前后才能打印日志,而自己新建的Computer是不具有这种功能,浅层次的理解就是aop功能是spring容器提供的,我们自己建的对象并不受spring管理,肯定不具有aop功能啦。进一步理解,spring通过@autowired自动注入的Computer对象为什么会具有aop功能呢,我们通过computer.work(user,“win7”);方法调用貌似没做任何处理却能在方法调用前后能打印出日志。显然spring管理的computer对象是个代理对象,在真正执行work方法前后做了一些处理。因为我们在执行computer.work(user,“win7”);方法前spring仅给我们管理了需要的computer对象并通过@autowired注入给我们使用。所以aop功能应该是在bean创建或getBean过程中。回顾我们之前的文章不难发现是在获取bean时实现的aop功能。
1.怎么发现的aop功能是在获取bean时实现的呢?
观察applicationContext.xml我们知道,要支持aop功能xml必须如此配置:
从springAOP的调用过程解析aop源码之一——aop使用的疑惑
1.1 http://www.springframework.org/schema/aop用途何在?
通过aop的jar我们不难发现这里配置了这个:
从springAOP的调用过程解析aop源码之一——aop使用的疑惑
此时我们可以猜测这个地址是用来找到AopNamespaceHandler,我们来看一下这个类:

public class AopNamespaceHandler extends NamespaceHandlerSupport {

	/**
	 * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
	 * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
	 * and '{@code scoped-proxy}' tags.
	 */
	@Override
	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());
	}

}

在这里我们非常熟悉的字符aspectj-autoproxy,这不是我们在xml配置的标签吗,
这是要干嘛呢?
我们来看看这个init方法在哪被调用了,发现只有在DefaultNamespaceHandlerResolver中resolve方法调了:

public NamespaceHandler resolve(String namespaceUri) {
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				namespaceHandler.init();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "] not found", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "]: problem with handler class file or dependent class", err);
			}
		}
	}

这里大概讲一下功能,详解再之后进行。通过读取spring.handlers文件生成map,然后通过namespaceUri找到NamespaceHandler执行init方法并返回NamespaceHandler实例
我们接着看resolve方法被哪个调用了:
返现有三个地方调用了此方法,根据我们对bean加载源码的分析,此时应该是BeanDefinitionParserDelegate下的parseCustomElement方法:

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

此方法我们很熟悉,在bean加载时已经看到过了,意思是通过 http://www.springframework.org/schema/aop找到AopNamespaceHandler处理器,然后对元素进行解析。
从AopNamespaceHandler处理器知道在init方法注册了多个处理器。
那么如何找到对应的处理器呢?

public BeanDefinition parse(Element element, ParserContext parserContext) {
		return findParserForElement(element, parserContext).parse(element, parserContext);
	}

	/**
	 * Locates the {@link BeanDefinitionParser} from the register implementations using
	 * the local name of the supplied {@link Element}.
	 */
	private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		String localName = parserContext.getDelegate().getLocalName(element);
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;
	}

原来在AopNamespaceHandler的init方法执行时,用标签名作为key,处理器作为value,然后在此时再通过标签名找到对应的处理AspectJAutoProxyBeanDefinitionParser并调用其中的parse方法:

public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		extendBeanDefinition(element, parserContext);
		return null;
	}
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			ParserContext parserContext, Element sourceElement) {

		BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		registerComponentIfNecessary(beanDefinition, parserContext);
	}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

可以看到生成了AnnotationAwareAspectJAutoProxyCreator.class的beanDefinition,并把proxy-target-class属性保存。
此时<aop:aspectj-autoproxy proxy-target-class=“true” />的封装我们就理解了,实际上就是解析成了AnnotationAwareAspectJAutoProxyCreator类的bean对象。通过观察AnnotationAwareAspectJAutoProxyCreator的继承关系发现是SmartInstantiationAwareBeanPostProcessor的实现,此时我们就高兴了,因为SmartInstantiationAwareBeanPostProcessor我们很熟悉,在获取bean实例时有讲过,在获取真正的bean实例前都会先从bean后处理器获取代理实例,如果有会直接返回实例。
1.2http://www.springframework.org/schema/aop/spring-aop-3.2.xsd的作用?
打开这个文件我们会发现我们熟悉的aop标签都在这,实际上这个文件就是用来自定义标签的。这个文件定义了aop标签的规范。
最后回答我们的问题:怎么发现的aop功能是在获取bean时实现的呢?
1.通过xsd中的标签定义用dom解析xml文件生成document。
2.通过对document中的element的namespaceuri找到AopNamespaceHandler,通过aspectj-autoproxy标签找到AspectJAutoProxyBeanDefinitionParser解析器,通过AspectJAutoProxyBeanDefinitionParser解析器得到AnnotationAwareAspectJAutoProxyCreator.class的beanDefinition
3.在获取bean实例时调用AnnotationAwareAspectJAutoProxyCreator中的postProcessAfterInitialization方法返回代理实例