场景构建:使用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必须如此配置:
1.1 http://www.springframework.org/schema/aop用途何在?
通过aop的jar我们不难发现这里配置了这个:
此时我们可以猜测这个地址是用来找到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方法返回代理实例