Spring 4.3.x Analysis of xml configuration parsing process (9) - parsing the config tag of aop namespace

Keywords: Attribute Spring snapshot xml

Summary

spring created a simplified definition of AOP in xml files http://www.springframework.org/schema/aop Namespaces, which I call AOP namespaces for short. When spring encounters a non-default namespace in parsing the xml configuration file content, it looks for the processor corresponding to the namespace in all spring.handlers files in the META-INF directory of the system. We can find the processor of the AOP namespace in the META-INF directory of the spring-aop-x.x.x-RELEASE.jar package, such as the content of this file. Next.

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

The following is the source code for the AopNamespace Handler class.

public class AopNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        // Register parsers for config Tags
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        // Register parsers for aspectj-autoproxy Tags
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        // Register decorators for scoped-proxy Tags
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

        // Since Spirng 2.1, spring-configured tags have been defined in the context namespace
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }

}

AopNamespaceHandler inherits the NamespaceHandlerSupport class. For an introduction to this class, see another article - Resolving labels for custom namespaces . Let's look directly at how the ConfigBean Definition Parser parser parses the config tag.

Resolve < aop: config > tags

The parse method of the BeanDefinitionParser interface is directly implemented by the parser ConfigBeanDefinitionParser. The source code of this method is as follows.

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        CompositeComponentDefinition compositeDef =
                new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
        parserContext.pushContainingComponent(compositeDef);

        // Create AOP Automatic Agent Creator
        configureAutoProxyCreator(parserContext, element);

        //Traverse and parse subtags of <aop:config>.
        List<Element> childElts = DomUtils.getChildElements(element);
        for (Element elt: childElts) {
            String localName = parserContext.getDelegate().getLocalName(elt);
            if ("pointcut".equals(localName)) {
                // Resolve < aop: pointcut > tags
                parsePointcut(elt, parserContext);
            } else if ("advisor".equals(localName)) {
                // Resolve < aop: advisor > tags
                parseAdvisor(elt, parserContext);
            } else if ("aspect".equals(localName)) {
                // Resolve < aop: aspect > tags
                parseAspect(elt, parserContext);
            }
        }

        parserContext.popAndRegisterContainingComponent();
        return null;
    }

The parse method first calls the configureAutoProxyCreator method of the parser ConfigBeanDefinitionParser to register an automatic proxy builder AspectJAwareAdvisorAutoProxyCreator object in the container, then calls the parsePointcut method to parse the < aop: pointcut > tag, the parseAdvisor method to parse the < aop: advisor > tag, and the Aspectparse method to parse the < aop: aspect > tag.

1. Create an automatic agent builder object

Below is the source code of ConfigBean Definition Parser's configureAutoProxyCreator method.

    private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
        AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
    }

Let's continue to look at the registerAspectJAutoProxyCreatorIfNecessary method of the AOP namespace tool class AopNamespaceUtils, with the following source code.

    public static void registerAspectJAutoProxyCreatorIfNecessary(
            ParserContext parserContext, Element sourceElement) {
        // Register AspectJ Automatic Agent Builder
        BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
                parserContext.getRegistry(), parserContext.extractSource(sourceElement));
        // Resolve the proxy-target-class and expose-proxy attributes of <aop:config> Tags
        useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
        registerComponentIfNecessary(beanDefinition, parserContext);
    }

The registerAspectJAutoProxyCreatorIfNecessary method completes the registration of AspectJ automatic agent builder in two steps. The first step is to register the AspectJ automatic agent builder, and the second step is to parse the properties of the < aop: config > tag to set the attribute values of the automatic agent builder. These two steps are described below.

Step 1: Register AspectJ Automatic Agent Builder
Below is the source code for the registerAspectJAutoProxyCreator IfNecessary method of the AopConfigUtils tool class.

    public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
        return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
    }

Continue to see the registerOrEscalateApcAsRequired method of the AopConfigUtils tool class, as follows.

    private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
        // Defined as AUTO_PROXY_CREATOR_BEAN_NAME= "org.spring framework.aop.config.internalAutoProxyCreator"
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            // If an automatic proxy builder already exists in the container, compare the priorities of the two builders
            BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                int requiredPriority = findPriorityForClass(cls);
                // Save a high priority builder
                if (currentPriority < requiredPriority) {
                    apcDefinition.setBeanClassName(cls.getName());
                }
            }
            return null;
        }
        // If there is no automatic agent builder in the container
        // Create the BeanDefinition object corresponding to the builder 
        RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
        beanDefinition.setSource(source);
        beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

        // Register the BeanDefinition object of the proxy builder in the container
        registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
        return beanDefinition;
    }

Step 2: Parse <aop:config> attributes
After registering the proxy builder in the container, the useClassProxyingIfNecessary method of the AopNamespace Utils tool class is called to parse the two properties of <aop:config>. The source code of this method is as follows.

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
        if (sourceElement != null) {
            // Parsing proxy-target-class attributes
            boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute("proxy-target-class"));
            if (proxyTargetClass) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            // Resolving expose-proxy attributes
            boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute("expose-proxy"));
            if (exposeProxy) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }

The following is the method source code for AopConfigUtils to process these two attributes.
Where AUTO_PROXY_CREATOR_BEAN_NAME="org.spring framework.aop.config.internalAutoProxyCreator"

    public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
        }
    }

    public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
        if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
            definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
        }
    }

2. Resolve < aop: pointcut > tags

The ConfigBeanDefinitionParser parser calls its parsePointcut method to parse the < aop: pointcut > tag. The source code for this method is as follows.

    private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
        // Get the id attribute value
        String id = pointcutElement.getAttribute("id");
        // Get the expression attribute value
        String expression = pointcutElement.getAttribute("expression");

        AbstractBeanDefinition pointcutDefinition = null;

        try {
            this.parseState.push(new PointcutEntry(id));
            // Create a BeanDefinition of a Pointcut object based on the tangent expression
            pointcutDefinition = createPointcutDefinition(expression);
            pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

            String pointcutBeanName = id;
            if (StringUtils.hasText(pointcutBeanName)) {
                // When the id attribute value is not empty, use the bean name whose id value is Pointcut and register it in the container
                parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
            } else {
                // When the id attribute value is empty, use the bean name generator to create the bean name for the Pointcut and register it in the container
                pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
            }

            parserContext.registerComponent(
                    new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
        } finally {
            this.parseState.pop();
        }

        return pointcutDefinition;
    }

Now let's see how the createPointcutDefinition method of the ConfigBeanDefinitionParser parser actually creates a BeanDefinition with the following source code.

    protected AbstractBeanDefinition createPointcutDefinition(String expression) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
        // Specify the creation of an AspectJExpressionPointcut object with a scope of prototype
        beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        beanDefinition.setSynthetic(true);
        beanDefinition.getPropertyValues().add("expression", expression);
        return beanDefinition;
    }

The createPointcutDefinition method registers an AspectJExpressionPointcut object to the container.

3. Analyzing < aop: advisor > tags

The ConfigBeanDefinitionParser parser calls its parseAdvisor method to parse the < advisor > tag. The source code for this method is as follows.

    private void parseAdvisor(Element advisorElement, ParserContext parserContext) {

        // Create a BeanDefintion object corresponding to an Advisor object
        AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
        // Get the id attribute value
        String id = advisorElement.getAttribute(ID);

        try {
            this.parseState.push(new AdvisorEntry(id));
            String advisorBeanName = id;
            if (StringUtils.hasText(advisorBeanName)) {
                // When the id attribute value is not empty, use the bean name whose id value is Advisor and register it in the container
                parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
            } else {
                // When the id attribute value is empty, use the bean name generator to create the bean name for Advisor and register it in the container
                advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
            }

            // Get the Point cut of Advisor
            // Resolving pointcut and poincut-ref attributes
            Object pointcut = parsePointcutProperty(advisorElement, parserContext);
            if (pointcut instanceof BeanDefinition) {
                // Gets a Poincut bean created from a pointcut expression specified by the pointcut attribute
                advisorDef.getPropertyValues().add("pointcut", pointcut);
                parserContext.registerComponent(
                        new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
            } else if (pointcut instanceof String) {
                // Gets a Pointcut bean to which the pointcut-ref attribute value points.
                advisorDef.getPropertyValues().add("pointcut", new RuntimeBeanReference((String) pointcut));
                parserContext.registerComponent(
                        new AdvisorComponentDefinition(advisorBeanName, advisorDef));
            }
        } finally {
            this.parseState.pop();
        }
    }

The parseAdvisor method can be interpreted in two steps. The first step is to create and register the BeanDefinition object corresponding to the Advisor, which is created by calling the createAdvisorBeanDefinition method of ConfigBeanDefinitionParser, and the second step is to obtain the Pontcut of the Advisor by parsing the pointcut or pointcut-ref attribute, which is done by calling the ConfigBeanDefinitionPa. Rser's parsePointcutProperty method completes. Let's look at the code for each of these methods.
The first step is to create the Bean Definition corresponding to Advisor
The parseAdvisor method calls ConfigBeanDefinitionParser's createAdvisorBeanDefinition to create a BeanDefinition object to specify the disor object to be created, with the source code as follows.

    private AbstractBeanDefinition createAdvisorBeanDefinition(Element advisorElement, ParserContext parserContext) {

        // Specify injecting DefaultBeanFactoryPointcutAdvisor object into the container as Advisor
        RootBeanDefinition advisorDefinition = new RootBeanDefinition(DefaultBeanFactoryPointcutAdvisor.class);
        advisorDefinition.setSource(parserContext.extractSource(advisorElement));

        // Specify Advisor's Advice object
        // Get advice-ref attribute value
        String adviceRef = advisorElement.getAttribute("advice-ref");
        if (!StringUtils.hasText(adviceRef)) {
            parserContext.getReaderContext().error(
                    "'advice-ref' attribute contains empty value.", advisorElement, this.parseState.snapshot());
        }
        else {
            advisorDefinition.getPropertyValues().add(
                    "adviceBeanName", new RuntimeBeanNameReference(adviceRef));
        }

        // Gets the order value to specify the execution order of Advise
        if (advisorElement.hasAttribute("order")) {
            advisorDefinition.getPropertyValues().add(
                    "order", advisorElement.getAttribute("order"));
        }

        return advisorDefinition;
    }

The createAdvisorBeanDefinition method not only creates a BeanDefinition object related to Advisor, but also obtains the order and adice-ref attributes of the < aop: advisor > tag to set the corresponding attributes of Advisor.

Step 2: Get the Pointcut of Advisor
<aop: advisor> has five attributes: id, advice-ref, order, pointcut and pointcut-ref. The first three attributes only need to simply get the attribute value to OK. The parseAdvisor method also needs to call the parsePoint Property method of ConfigBeanDefinitionParser to parse the point and pointcut-ref attributes. Here is the source code of this method.

    private Object parsePointcutProperty(Element element, ParserContext parserContext) {
        // The poincut and pointcut-ref attributes cannot be defined simultaneously
        if (element.hasAttribute("pointcut") && element.hasAttribute("pointcut-ref")) {
            parserContext.getReaderContext().error(
                    "Cannot define both 'pointcut' and 'pointcut-ref' on <advisor> tag.",
                    element, this.parseState.snapshot());
            return null;
        } else if (element.hasAttribute("pointcut")) {
            String expression = element.getAttribute("pointcut");
            // Create a BeanDefinition object for Pointcut based on the tangent expression
            AbstractBeanDefinition pointcutDefinition = createPointcutDefinition(expression);
            pointcutDefinition.setSource(parserContext.extractSource(element));
            return pointcutDefinition;
        } else if (element.hasAttribute("pointcut-ref")) {
            // Get the pointcut-ref attribute value and return it
            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 <advisor> tag.",
                    element, this.parseState.snapshot());
            return null;
        }
    }

4. Resolve < aop: aspect > tags

The ConfigBeanDefinitionParser parser calls its parseAspect method to parse the < aop: aspect > tag. The source code for this method is as follows.

    private void parseAspect(Element aspectElement, ParserContext parserContext) {
        // Get the id attribute value
        String aspectId = aspectElement.getAttribute("id");
        // Get the ref attribute value
        String aspectName = aspectElement.getAttribute("ref");

        try {
            this.parseState.push(new AspectEntry(aspectId, aspectName));
            List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
            List<BeanReference> beanReferences = new ArrayList<BeanReference>();

            List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, "declare-parents");
            // Traverse and parse the < aop: declare-parents > tag
            // Defined as METHOD_INDEX=0
            for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
                Element declareParentsElement = declareParents.get(i);
                // Resolve < aop: declare-parents > tag
                beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
            }

            // Traverse and parse before, after, after-returning, after-throwing, and around Tags
            NodeList nodeList = aspectElement.getChildNodes();
            boolean adviceFoundAlready = false;
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node node = nodeList.item(i);
                // Current judgement
                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));
                    }
                    // Parse the tags associated with adice and create and register the corresponding BeanDefinition object
                    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);

            // Traverse and parse < aop: pointcut > tags
            List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
            for (Element pointcutElement : pointcuts) {
                parsePointcut(pointcutElement, parserContext);
            }

            parserContext.popAndRegisterContainingComponent();
        } finally {
            this.parseState.pop();
        }
    }

The parseAspect method parses the pointcut, declare-parents and five advice tags under the aspect tag. The analysis of the pointcut tag has already been seen before. Here we just need to look at the analysis of the latter two tags.

(1) Resolve the < aop: declare-parents > tag. parseAspect calls the parseDeclareParents method of the ConfigBeanDefinitionParser parser to process this tag, as follows.

    private AbstractBeanDefinition parseDeclareParents(Element declareParentsElement, ParserContext parserContext) {
        // Using BeanDefinitionBuilder Object to Construct a BeanDefinition Object
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DeclareParentsAdvisor.class);
        builder.addConstructorArgValue(declareParentsElement.getAttribute("implement-interface"));
        builder.addConstructorArgValue(declareParentsElement.getAttribute("types-matching"));

        String defaultImpl = declareParentsElement.getAttribute("default-impl");
        String delegateRef = declareParentsElement.getAttribute("delegate-ref");

        // default-impl and delegate-ref cannot be defined simultaneously
        if (StringUtils.hasText(defaultImpl) && !StringUtils.hasText(delegateRef)) {
            builder.addConstructorArgValue(defaultImpl);
        } else if (StringUtils.hasText(delegateRef) && !StringUtils.hasText(defaultImpl)) {
            builder.addConstructorArgReference(delegateRef);
        } else {
            parserContext.getReaderContext().error(
                    "Exactly one of the " + DEFAULT_IMPL + " or " + DELEGATE_REF + " attributes must be specified",
                    declareParentsElement, this.parseState.snapshot());
        }

        AbstractBeanDefinition definition = builder.getBeanDefinition();
        definition.setSource(parserContext.extractSource(declareParentsElement));
        // Register BeanDefinition objects with containers
        parserContext.getReaderContext().registerWithGeneratedName(definition);
        return definition;
    }

The parseDeclareParents method registers a DeclareParents Advisor object to the container.

(2) Resolve the advice tag. spring provides <aop:before>, <aop:after>, <aop:after-returning>, <aop:after-throwing>, <aop:around> five advice tags. parseAspect calls the parseAdvice method of ConfigBean Definition Parser parser to process these tags. The code is as follows.

    private AbstractBeanDefinition parseAdvice(
            String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
            List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {

        try {
            this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));

            // Create a 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 a factory for getting an instance of an aspect
            RootBeanDefinition aspectFactoryDef =
                    new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
            aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
            aspectFactoryDef.setSynthetic(true);

            // Create Advice
            AbstractBeanDefinition adviceDef = createAdviceDefinition(
                    adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
                    beanDefinitions, beanReferences);

            // Configure Advicor
            RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
            advisorDefinition.setSource(parserContext.extractSource(adviceElement));
            advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
            if (aspectElement.hasAttribute("order")) {
                advisorDefinition.getPropertyValues().add(
                        "order", aspectElement.getAttribute("order"));
            }

            // Register Advisor in Container
            parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);

            return advisorDefinition;
        } finally {
            this.parseState.pop();
        }
    }

The parseAdvice method first creates a BeanDefinition corresponding to the MethodLocatingFactoryBean for the specified aspect instance method, then creates a BeanDefinition corresponding to the SimpleBeanFactoryAwareAspectInstanceFactory for the specified aspect instance, and then calls the CreateAdviceDefinition method of the ConfigBeanDefinitionParser parser to create the BeanDefinition of the Advice. Finally, create and register a BeanDefition for Advisor. In these steps, we also need to see how the createAdviceDefinition method creates the BeanDefintion of Advice. Here is the source code for this method.

    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("aspectName", aspectName);
        adviceDefinition.getPropertyValues().add("declarationOrder", order);

        if (adviceElement.hasAttribute("returning")) {
            adviceDefinition.getPropertyValues().add(
                    "returningName", adviceElement.getAttribute("returning"));
        }
        if (adviceElement.hasAttribute("throwing")) {
            adviceDefinition.getPropertyValues().add(
                    "throwingName", adviceElement.getAttribute("throwing"));
        }
        if (adviceElement.hasAttribute("arg-names")) {
            adviceDefinition.getPropertyValues().add(
                    "argumentNames", adviceElement.getAttribute("arg-names"));
        }

        ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
        // Defined as METHOD_INDEX=0
        cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);

        // Resolving poincut and pointcut-ref attributes
        // Define POINTCUT_INDEX = 1
        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);
        }
        // Defined as ASPECT_INSTANCE_FACTORY_INDEX = 2
        cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);

        return adviceDefinition;
    }

The createAdviceDefinition method mainly parses the attribute values on the adive tag. However, before dealing with attributes, you need to determine which type of tag is in the advice tag in 5. Here is the source code for the getAdviceClass method.

    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".equals(elementName)) {
            return AspectJAfterReturningAdvice.class;
        } else if ("after-throwing".equals(elementName)) {
            return AspectJAfterThrowingAdvice.class;
        } else if ("around".equals(elementName)) {
            return AspectJAroundAdvice.class;
        } else {
            throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
        }
    }

So far, we have completed exploring which types of BeanDefiniton spring will create when it encounters config tags in aop namespaces.

summary

(1) Create BeanDefinition of the AspectJAware Advisor AutoProxyCreator object for the config tag.

If a "org. spring framework. aop. config. internalAutoProxyCreator" already exists in the spring container, the priority of the two creators is compared. The higher priority is saved and the lower one is removed from the container.

(2) Create BeanDefiniton of AspectJExpressionPointcut object for pointcut tag.

The pointcut tag must provide the expression attribute value.

(3) Create a BeanDefinition of the DefaultBeanFactoryPointcutAdvisor object for the advisor tag.

The advisor tag also requires a bean that implements the Adivce interface, specified by the adive-ref attribute, and a Pointcut bean, specified by the pointcut or pointcut-ref attribute.

(4) BeanDefintion is not created for the aspect tag. Its function is to provide the aspect method for the advice type tag.

(6) The corresponding BeanDefintion is created for the tag of advice type, as follows.

Create BeanDefintion of the AspectJMethodBeforeAdvice object for the before tag.
Create BeanDefinition of the AspectJAfterAdvice object for the after tag.
Create BeanDefinition of the AspectJAfterReturningAdvice object for the after-returning tag.
Create BeanDefinition of the AspectJAfterThrowingAdvice object for the after-throwing tag.
Create BeanDefinition of AspectJAroundAdvice object for around tag.

The creation of these Advice s also requires MethodLocatingFactoryBean objects and SimpleBeanFactoryAwareAspectInstanceFactory objects, so spring also needs to create BeanDefinition of MethodLocatingFactoryBean objects and SimpleBeanFactoryAwareAspectInstanceFactory objects.
Finally, the BeanDefinition of AspectJPointcutAdvisor object is created to manage the BeanDefinition of Advice.

(5) Create BeanDefinition of DeclareParents Advisor object for declare-parents tag

Posted by Hiro on Tue, 01 Jan 2019 03:03:08 -0800