博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring源码之AOP
阅读量:2350 次
发布时间:2019-05-10

本文共 15453 字,大约阅读时间需要 51 分钟。

前言

之前说过了IOC,现在自然要来到AOP了,先来看下配置文件实现的AOP的相关源码解析。

显然还是会用到BeanDefinitionParser接口的实现类。

这里是用ConfigBeanDefinitionParser实现类来完成对\节点的解析。

ConfigBeanDefinitionParser

@Override    public BeanDefinition parse(Element element, ParserContext parserContext) {        CompositeComponentDefinition compositeDef =                new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));        parserContext.pushContainingComponent(compositeDef);//向Spring容器注册了一个BeanName为org.springframework.aop.config.internalAutoProxyCreator的Bean定义,可以自定义也可以使用Spring提供的(根据优先级来)      //深入这个方法你会发现熟悉的proxy-target-class属性 以及一个不是很常用的exposeProxy属性    /*    向Spring容器注册了一个BeanName为org.springframework.aop.config.internalAutoProxyCreator的Bean定义,可以自定义也可以使用Spring提供的(根据优先级来)Spring默认提供的是org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator,这个类是AOP的核心类,留在下篇讲解在这个方法里面也会根据配置proxy-target-class和expose-proxy,设置是否使用CGLIB进行代理以及是否暴露最终的代理。    */      configureAutoProxyCreator(parserContext, element);        List
childElts = DomUtils.getChildElements(element); for (Element elt: childElts) { String localName = parserContext.getDelegate().getLocalName(elt); if (POINTCUT.equals(localName)) { parsePointcut(elt, parserContext); } else if (ADVISOR.equals(localName)) { parseAdvisor(elt, parserContext); } else if (ASPECT.equals(localName)) { parseAspect(elt, parserContext); } } parserContext.popAndRegisterContainingComponent(); return null; }

我们直接来看解析\的方法。

1.调用 configureAutoProxyCreator(parserContext, element);在向Spring容器里注册一个名为

org.springframework.aop.config.internalAutoProxyCreator的Bean定义,类型默认是

org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator,

具体实现先不细说。

2.接着往下看,我们的配置文件里面config节点下是aspect,所以我们这里直接看parseAspect(elt, parserContext);这个方法就帮我们解析了aspect节点下的所有配置,是我们今天要关注的重点。

parseAspect(elt, parserContext)

parseAspect

private void parseAspect(Element aspectElement, ParserContext parserContext) {  //获取ID和指定的beanName        String aspectId = aspectElement.getAttribute(ID);        String aspectName = aspectElement.getAttribute(REF);        try {          //往paresState这个栈里面推入一个当前aspcet节点的Entry            this.parseState.push(new AspectEntry(aspectId, aspectName));          //新建一个Bean定义List和BeanReference的List,在将Bean定义转化为Bean的时候会用到            List
beanDefinitions = new ArrayList
(); List
beanReferences = new ArrayList
();//如果该aspect节点有declare-parents属性的话,得到其属性对应的值,然后对其进行处理。 List
declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS); for (int i = METHOD_INDEX; i < declareParents.size(); i++) { Element declareParentsElement = declareParents.get(i); beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext)); } // We have to parse "advice" and all the advice kinds in one loop, to get the // ordering semantics right. //对aspect下的子节点进行处理。 NodeList nodeList = aspectElement.getChildNodes(); boolean adviceFoundAlready = false; //****开始对子节点遍历 for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); /* 即这个for循环只用来处理
标签下的
这五个标签的。 */ if (isAdviceNode(node, parserContext)) { if (!adviceFoundAlready) { adviceFoundAlready = true; if (!StringUtils.hasText(aspectName)) { parserContext.getReaderContext().error( "
tag needs aspect bean reference via 'ref' attribute when declaring advices.", aspectElement, this.parseState.snapshot()); return; } //要注入一个包含aspectName的beanReference,在aspectComponentDefinition 转化为Bean的时候会用到 beanReferences.add(new RuntimeBeanReference(aspectName)); } AbstractBeanDefinition advisorDefinition = parseAdvice( aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences); //将advisorDefinition加入到beanDefinitions beanDefinitions.add(advisorDefinition); } } //新建一个aspectComponentDefinition,注入了beanDefinitions beanReferences AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition( aspectElement, aspectId, beanDefinitions, beanReferences, parserContext); parserContext.pushContainingComponent(aspectComponentDefinition); List
pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT); for (Element pointcutElement : pointcuts) { parsePointcut(pointcutElement, parserContext); } parserContext.popAndRegisterContainingComponent(); } finally { this.parseState.pop(); } }

我们直接从对aspect子节点遍历开始看起。

isAdviceNode(node, parserContext)的作用是筛选出了要处理的子节点的名字。

private boolean isAdviceNode(Node aNode, ParserContext parserContext) {        if (!(aNode instanceof Element)) {            return false;        }        else {            String name = parserContext.getDelegate().getLocalName(aNode);            return (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) ||                    AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name));        }    }

可以很清楚的看出,代表只会处理aop:aspect>标签下的\、\、\、\、\这五个标签。

接着看

AbstractBeanDefinition advisorDefinition = parseAdvice(                            aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);

这里正式处理子节点了。

private AbstractBeanDefinition parseAdvice(            String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,            List
beanDefinitions, List
beanReferences) { try { //将当前的节点的信息放入到parseState栈中 this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement))); // // create the method factory bean RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class); methodDefinition.getPropertyValues().add("targetBeanName", aspectName); methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method")); methodDefinition.setSynthetic(true); // create instance factory definition RootBeanDefinition aspectFactoryDef = new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class); aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName); aspectFactoryDef.setSynthetic(true); // register the pointcut AbstractBeanDefinition adviceDef = createAdviceDefinition( adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef, beanDefinitions, beanReferences); // configure the advisor RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class); advisorDefinition.setSource(parserContext.extractSource(adviceElement));//看方法名就知道,当Adviser Bean定义通过Spring转化为AspectJPointcutAdvisor实例的时候, //advise Bean定义对应的AbstractAspectJAdvice实例将作为前者的构造方法的形参注入。 advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef); //如果节点有order属性,要注入order属性 if (aspectElement.hasAttribute(ORDER_PROPERTY)) { advisorDefinition.getPropertyValues().add( ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY)); } // register the final advisor //注册到Spring容器中,即BeanFactory中 parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); return advisorDefinition; } finally { //出栈 this.parseState.pop(); } }

1,创建一个Method工厂Bean定义,里面包含的信息有aspectname以及相应节点的method属性值。

2.创建一个aspectFactory工厂Bean定义,里面包含的信息有asepectname.

3.创建一个Advise Bean定义,下面会重点讲解。

4.创建一个Advisor Bean定义,将AdviseBean注入到其中,然后返回。看方法名就知道,当Adviser Bean定义通过Spring转化为AspectJPointcutAdvisor实例的时候, advise Bean定义对应的AbstractAspectJAdvice实例将作为前者的构造方法的形参注入。

我们重点来看下是如何创建Advise Bean定义的。

createAdviceDefinition

private AbstractBeanDefinition createAdviceDefinition(            Element adviceElement, ParserContext parserContext, String aspectName, int order,            RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,            List
beanDefinitions, List
beanReferences) {//新建Advice Bean定义 RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext)); adviceDefinition.setSource(parserContext.extractSource(adviceElement)); //注入 aspectName和declarationOrder属性 adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName); adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);//如果该节点有returning属性,将其注入该Bean定义的returningName属性 if (adviceElement.hasAttribute(RETURNING)) { adviceDefinition.getPropertyValues().add( RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING)); } //如果该节点有throwing属性,那么将其注入到该Bean定义的throwingName属性 if (adviceElement.hasAttribute(THROWING)) { adviceDefinition.getPropertyValues().add( THROWING_PROPERTY, adviceElement.getAttribute(THROWING)); } //如果该节点有arg-names属性,那么将其注入到该Bean定义的argumentNames属性 if (adviceElement.hasAttribute(ARG_NAMES)) { adviceDefinition.getPropertyValues().add( ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES)); } //很明显这里是为了该Bean定义中的Bean收集构造方法参数信息 ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();//第一个参数 Method cav.addIndexedArgumentValue(METHOD_INDEX, methodDef); //第二个参数 pointcut Object pointcut = parsePointcutProperty(adviceElement, parserContext); if (pointcut instanceof BeanDefinition) { cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut); beanDefinitions.add((BeanDefinition) pointcut); } else if (pointcut instanceof String) { RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut); cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef); beanReferences.add(pointcutRef); } //第三个参数,asepctFactory cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef); return adviceDefinition; }

1.通过节点的标签名字来得到要新建的AdviseBean定义的构造方法的入参。

getAdviceClass

private Class
getAdviceClass(Element adviceElement, ParserContext parserContext) { String elementName = parserContext.getDelegate().getLocalName(adviceElement); if (BEFORE.equals(elementName)) { return AspectJMethodBeforeAdvice.class; } else if (AFTER.equals(elementName)) { return AspectJAfterAdvice.class; } else if (AFTER_RETURNING_ELEMENT.equals(elementName)) { return AspectJAfterReturningAdvice.class; } else if (AFTER_THROWING_ELEMENT.equals(elementName)) { return AspectJAfterThrowingAdvice.class; } else if (AROUND.equals(elementName)) { return AspectJAroundAdvice.class; } else { throw new IllegalArgumentException("Unknown advice kind [" + elementName + "]."); } }

我们可以看到五个不同类型的节点都有自己对应的AbstractAdvise实现类。

2.注入一系列的属性

3.注入对应的Bean的构造方法参数。

我们这里主要看注入第二个参数的过程。

我们截取这一段来分析一下:

Object pointcut = parsePointcutProperty(adviceElement, parserContext);        if (pointcut instanceof BeanDefinition) {            cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);            beanDefinitions.add((BeanDefinition) pointcut);        }        else if (pointcut instanceof String) {            RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);            cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);            beanReferences.add(pointcutRef);        }

1.先调用parsePointcutProperty得到我们要注入的pointcut。

private Object parsePointcutProperty(Element element, ParserContext parserContext) {        if (element.hasAttribute(POINTCUT) && element.hasAttribute(POINTCUT_REF)) {            parserContext.getReaderContext().error(                    "Cannot define both 'pointcut' and 'pointcut-ref' on 
tag.", element, this.parseState.snapshot()); return null; } else if (element.hasAttribute(POINTCUT)) { // Create a pointcut for the anonymous pc and register it. String expression = element.getAttribute(POINTCUT); AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression); pointcutDefinition.setSource(parserContext.extractSource(element)); return pointcutDefinition; } else if (element.hasAttribute(POINTCUT_REF)) { String pointcutRef = element.getAttribute(POINTCUT_REF); if (!StringUtils.hasText(pointcutRef)) { parserContext.getReaderContext().error( "'pointcut-ref' attribute contains empty value.", element, this.parseState.snapshot()); return null; } return pointcutRef; } else { parserContext.getReaderContext().error( "Must define one of 'pointcut' or 'pointcut-ref' on
tag.", element, this.parseState.snapshot()); return null; } }

方法有点长,但是很好理解:

1.如果当前节点没有pointcut属性和pointcut-ref属性,那么直接返回null。

2.如果当前节点有pointcut属性,则调用createPointcutDefinition创建一个Bean定义,里面对应的bean是一个AspectJExpressionPointcut实例。

protected AbstractBeanDefinition createPointcutDefinition(String expression) {        RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);        beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);        beanDefinition.setSynthetic(true);        beanDefinition.getPropertyValues().add(EXPRESSION, expression);        return beanDefinition;    }

我们可以看到我们把pointcut属性对应的值注入到了AspectJExpressionPointcut的expresssion属性中。

而且要注意这个Bean是原型bean,而我们Spring默认的bean都是单例的。所以当节点中的属性是pointcut时,我们是要给出expression表达式,而对应的Bean是Spring默认给出的。

3.如果节点有pointcut-ref属性,我们直接返回属性值,也就是一个String对象。

2.当得到要注入的pointcut后,根据他的类型来选择不同的操作,如果是pointcut属性,即注入的是一个Bean定义时,我们除了要做必要的注入到构造方法里之外,我们需要把他注入到beanDefinitions这个Bean定义的list中。如果是pointcut-ref,即是String类型,那么额外的操作则变成了把他注入到beanReferences这个BeanReference类型的List,BeanReference的作用这里先稍微说一句,因为有些Bean是需要依赖于其他bean的,所以就需要BeanReference,而具体什么时候起作用以及怎么起作用,我们下次再细说。

我们回到parseAspect方法,我们现在可以吧目光投向parseAdvice后的代码。

我们会将得到的advisorDefinition加入到beanDefinitions。

然后新建一个AspectComponentDefinition实例,里面注入了beanDefinitions和beanReferences。

最后,我们需要对aspect下面的pointcut子节点进行处理。

List
pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT); for (Element pointcutElement : pointcuts) { parsePointcut(pointcutElement, parserContext); } parserContext.popAndRegisterContainingComponent();

先得到所有的poincut节点,然后调用parsePointcut注册pointcut的信息,最后会向Spring容器注册一个PointcutComponentDefinition组件。

最后将所有aspect相关信息组成的aspectComponentDefinition注入到Spring容器中。

然后回到parse方法,注入CompositeComponentDefinition到Spring容器中。

转载地址:http://cdmvb.baihongyu.com/

你可能感兴趣的文章
Go语言基础入门--if,for,range,switch
查看>>
Go语言基础入门--函数,错误处理
查看>>
VIM 学习系列之基本命令,常用命令
查看>>
轻松搭建安全、轻量、极速、简约的博客Eiblog
查看>>
Golang包管理工具Glide,你值得拥有
查看>>
Glide命令,如何使用glide,glide.lock
查看>>
耗时整整一天,整理出的超详细清晰的vim,vimrc配置
查看>>
Git 学习笔记、相关命令、问答
查看>>
HTTPS 免费证书,免费 ssl 证书,FreeSSL.cn 申请多种免费证书
查看>>
SSH Config 那些你所知道和不知道的事
查看>>
10 分钟理解什么是 OAuth 2.0 协议
查看>>
docker volume 容器卷的那些事(一)
查看>>
docker volume 容器卷的那些事(二)
查看>>
首日报名爆满超300 向C++大师Lippman提问征集
查看>>
如何降低白噪声对网站用户体验的影响?
查看>>
TUP Masters第七期:C++大师Lippman论编程新范式Hugo
查看>>
[TUP第30期]直击移动应用开发难点 探讨跨平台最佳解决方案
查看>>
Avangate SaaS模式开启全球软件新营销之门
查看>>
如何降低白噪声对网站用户体验的影响?
查看>>
17173总经理赵佳:媒体移动化需抓住三大要素
查看>>