本文主要分析通过<aop:config>这种标签来使用spring AOP的过程。Spring版本是4.1.7。在我看来Spring AOP主要分四个步骤,加载解析AOP标签,生成代理对象,拦截器的调用。下面就是通过这四个方面来分析Spring AOP的原理。
一.容器初始化,解析AOP标签。
1.先看一下我跟源码时关于AOP的配置
<aop:config>
<aop:aspect ref="print">
<aop:pointcut expression="execution(* aop.*.do*(..))" id="pointcut" />
<aop:after method="printLog" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
2.spring 在容器初始化时会通过一系列的BeanDefinitionParser将我们定义的内容解析成Spring内部的使用的数据结构BeanDefinition。spring 容器初始化的过程,不在多说,主要说明一下AOP标签解析的过程。AOP标签解析的入口
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
//解析除了Bean标签之外的其它标签
//AOP标签解析的入口
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
在spring中bean标签是default,其它的任何标签都是custom。我们接着跟代码
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));
}
获取NamespaceHandler的过程主要就是通过命名空间
http://www.springframework.org/schema/aop,找到定义标签的Handler文件,在spring-aop的jar包里META—INF里的spring.handlers,内容如下:
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
Spring对不同的命名空间都有相对应的NamespaceHandler。从上面的内容可以看出处理AOP命名空间的是AopNamespaceHandler,我们看一下AopNamespaceHandler里的内容。
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@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());
}
}
从中可以看出NameSpaceHandler主要是来注册用来解析标签的BeanDefitionParser,解析<aop:config>标签的是ConfigBeanDefitionParser,看一下主要的解析过程
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
//解析<aop:config>标签
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element);
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
//解析<aop:pointcut>标签
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
//解析<aop:padvisor>标签
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
//解析<aop:aspect>标签
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
在解析<aop:config>的子标签时都是生成BeanDefinition,在BeanFactory中有个重要属性beanDefinitionMap,里面保存着解析的结果,大家调试的时候可以看下里面除了你定义的bean之外,还有Aop标签的解析结果。在解析<aop:config>是有个重要的步骤,如下
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
向BeanFactory中AspectJAwareAdvisorAutoProxyCreator,这个其实就是个BeanPostProcessor,对spring了解的可以知道,这是spring对外暴露的扩展点,里面定义了俩个方法,postProcessBeforeInstantiation在bean初始化之前,postProcessAfterInitialization在Bean初始化之后
用。
AspectJAwareAdvisorAutoProxyCreator
这个东西就是我们要找的东西,因为有了它我们才会生成代理对象。
二.生成代理对象。
1.在通过getBean()触发依赖注入之后,spring先生成对象的实例,然后进行初始化,在完成初始化之后会调用BeanPostProcessor的
postProcessAfterInitialization()方法,这里就是生成代理对象的入口。上面我们说了spring在解析Aop标签时会向容器注入AspectJAwareAdvisorAutoProxyCreator,其postProcessAfterInitialization的方法在父类AbstractAutoProxyCreator里,我们看一下其方法的实现:
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
在看一下
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
//1.判断是否有advisor和当前bean的class匹配
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//2.来生成代理对象
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
其中1的主要逻辑在AopUtils中,我们看一下主要的过程
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
主要过程就是获取所有配置的advisor,然后循序校验,先判断类名是不是匹配,如果匹配则判断是不是有方法匹配。
生成代理对象有俩中策略,一种是JDK的动态代理,还有一种是Cglib,由于我并不是很了解Cglib我们看一下JDK动态的代理。
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
很熟悉的东西。
三.拦截器的调用。
先看一下
CglibAopProxy里的i
ntercept()方法
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Class<?> targetClass = null;
Object target = null;
try {
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//获取目标类
target = getTarget();
if (target != null) {
targetClass = target.getClass();
}
//1.获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
//没有匹配的拦截器则直接执行目标方法
retVal = methodProxy.invoke(target, args);
}
else {
//2.执行匹配的拦截器链
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
先看一下如何获取匹配的拦截器,
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
//根据配置文件获取advisor集合,然后循环遍历
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
//class是否匹配
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
//方法名是否匹配
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) {
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
根据要执行的实例的类名,方法名循环遍历配置的advisor来判断。
我们最后在看一下是怎么执行的
public Object proceed() throws Throwable {
// 判断是否到了链尾,如果是则执行目标方法,调用结束。
if (this.currentInterceptorIndex ==
this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 取出当前的Advisor;
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 判断是否需要动态判断参数,如果需要则执行如下操作;
// 参数验证通过则执行Advisor,否则跳过该Advisor;
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
// 如果不需要动态判断参数,则执行该Advisor,因为在之前已经验证通过了;
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
在这里就分析完了。