【Spring AOP】【二】Spring AOP源码解析-XML方式加载解析过程

1  前言

这篇我们看一下,我们的AOP代码是怎么被Spring加载的进去的,那么分两种一种是XML配置的,一种就是我们常用的注解,我们从源码先看下XML方式的都是怎么被加载解析的。

2  代码准备


<context:component-scan base-package="com.virtuous.demo.spring.cycle"/>
<bean id="A" class="com.virtuous.demo.spring.cycle.A" />
<!--通知-->
<bean id="logAspect" class="com.virtuous.demo.spring.LogAspect"/>
<!--aop配置-->
<aop:config proxy-target-class="true">
    <aop:aspect ref="logAspect" order="1">
        <aop:pointcut id="logAspectPointcut" expression="execution( * com.virtuous.demo.spring.cycle.A.*(..))"/>
        <aop:before method="before" pointcut-ref="logAspectPointcut"/>
        <aop:after method="after" pointcut-ref="logAspectPointcut"/>
        <aop:around method="around" pointcut-ref="logAspectPointcut"/>
        <aop:after-returning method="afterReturning" pointcut-ref="logAspectPointcut"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="logAspectPointcut"/>
     </aop:aspect>
</aop:config>
/**
 * @author kuku
 */
public class LogAspect {

    public void before(JoinPoint point) {
        System.out.println("before");
    }

    public void after(JoinPoint point) {
        System.out.println("after");
    }

    public void afterReturning(JoinPoint point) {
        System.out.println("afterReturning");
    }

    public void afterThrowing(JoinPoint point) {
        System.out.println("afterThrowing");
    }

    public void around(ProceedingJoinPoint joinPoint)  throws Throwable {
        System.out.println("around before");
        joinPoint.proceed();
        System.out.println("around after");
    }
}
@Component
public class A {

    public void say() {
        System.out.println("111");
    }

}
// 测试类
public class ApplicationContextTest {
    @Test
    public void testFactoryBean() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring/spring-alias.xml");
        A a = applicationContext.getBean("a", A.class);
        a.say();
    }
}

3  源码分析

(1)Spring对于AOP在XML的配置采用的是自定义标签解析的形式,入口在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法中。每种自定义标签都有自己的Handler来处理。

// 解析XML
protected
void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 解析默认的名称空间 比如bean 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 { delegate.parseCustomElement(ele); } } } } else { // 解析自定义的名称空间 比如AOP的解析 delegate.parseCustomElement(root); } }
// 解析自定义标签
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    // 1、获取namespaceUri
    String namespaceUri = getNamespaceURI(ele);
    if (namespaceUri == null) {
        return null;
    }
    // 2、根据namespaceUri得到命名空间解析器
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    }
    // 3、使用命名空间解析器解析自定义标签
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

(2)针对AOP的解析,对应的Handler为AopNamespaceHandler

public class AopNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        // In 2.0 XSD as well as in 2.5+ XSDs
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

        // Only in 2.0 XSD: moved to context namespace in 2.5+
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }

}

(3)ConfigBeanDefinitionParser解析aop:config

public BeanDefinition parse(Element element, ParserContext parserContext) {
    CompositeComponentDefinition compositeDef =
            new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
    parserContext.pushContainingComponent(compositeDef);
    // 注册AspectJAutoProxyCreator
    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:advisor
        else if (ADVISOR.equals(localName)) {
            parseAdvisor(elt, parserContext);
        }
        // 解析aop:config
        else if (ASPECT.equals(localName)) {
            parseAspect(elt, parserContext);
        }
    }
    parserContext.popAndRegisterContainingComponent();
    return null;
}

(4)passeAspect,并获取下边的通知,并进行一一创建

private void parseAspect(Element aspectElement, ParserContext parserContext) {
    // 解析 id
    String aspectId = aspectElement.getAttribute(ID);
    // 解析ref
    String aspectName = aspectElement.getAttribute(REF);
    try {
        this.parseState.push(new AspectEntry(aspectId, aspectName));
        List<BeanDefinition> beanDefinitions = new ArrayList<>();
        List<BeanReference> beanReferences = new ArrayList<>();
        List<Element> 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
        // 解析子元素advice 比如
        NodeList nodeList = aspectElement.getChildNodes();
        boolean adviceFoundAlready = false;
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            // 判断是不是通知的标签 也就是是不是那5个通知 before after around after-returning after-throwing
            if (isAdviceNode(node, parserContext)) {
                if (!adviceFoundAlready) {
                    adviceFoundAlready = true;
                    if (!StringUtils.hasText(aspectName)) {
                        parserContext.getReaderContext().error(
                                "<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
                                aspectElement, this.parseState.snapshot());
                        return;
                    }
                    beanReferences.add(new RuntimeBeanReference(aspectName));
                }
                // 给5个通知创建对应的BeanDefinition
                AbstractBeanDefinition advisorDefinition = parseAdvice(
                        aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
                beanDefinitions.add(advisorDefinition);
            }
        }
        AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
                aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
        parserContext.pushContainingComponent(aspectComponentDefinition);
     // 解析切点 也是作为BeanDefinition List
<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT); for (Element pointcutElement : pointcuts) { parsePointcut(pointcutElement, parserContext); } parserContext.popAndRegisterContainingComponent(); } finally { this.parseState.pop(); } }
// 创建通知的BeanDefinition
private AbstractBeanDefinition createAdviceDefinition(
        Element adviceElement, ParserContext parserContext, String aspectName, int order,
        RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
        List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
    RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
    adviceDefinition.setSource(parserContext.extractSource(adviceElement));
    adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
    adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
    if (adviceElement.hasAttribute(RETURNING)) {
        adviceDefinition.getPropertyValues().add(
                RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
    }
    if (adviceElement.hasAttribute(THROWING)) {
        adviceDefinition.getPropertyValues().add(
                THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
    }
    if (adviceElement.hasAttribute(ARG_NAMES)) {
        adviceDefinition.getPropertyValues().add(
                ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
    }
    ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
    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);
    }
    cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
    return adviceDefinition;
}

切点的创建和通知差不多这里就不展示了,这样我们的AOP的配置就都生成了BeanDefinition,我们来总结一下执行流程:

  1. XML中的AOP相关的名称空间,由AopNamespaceHandler来负则解析
  2. config标签的内容由ConfigBeanDefinitionParser来解析内容
  3. 解析aspect标签的内容
  4. 遍历每个通知以及切点进行创建对应的BeanDefinition并注册到Bean工厂(有一点需要注意的就是创建通知的时候会解析标签上的pointCut并关联,这样切点和通知有了联系)

4  小结

我们本文讲解了XML方式配置的AOP是如何被加载进Spring中的,主要就是对我们的配置进行解析,封装成BeanDefinition放进Bean工厂,下文我们讲解注解方式的解析过程。

 

posted @ 2023-02-19 21:29  酷酷-  阅读(104)  评论(0编辑  收藏  举报