Spring BeanPostProcessor two: CommonAnnotationBeanPostProcessor (01)

In the last blog, I shared the four methods in the InstantAwareBeanPostProcessor interface, and introduced them in detail, leaving a problem at the end of the article, that is the postProcessProperties method. When it comes to this method, it is used for property filling, and The CommonAnnotationBeanPostProcessor class is introduced.

I. Overview

The CommonAnnotationBeanPostProcessor class is an extremely important class in spring. It is responsible for parsing the three annotations @Resource, @WebServiceRef, and @EJB. Since these three attributes are to be parsed, there must be these three annotations in the CommonAnnotationBeanPostProcessor class. This code,

static {
        try {
            @SuppressWarnings("unchecked")
            Class<? extends Annotation> clazz = (Class<? extends Annotation>)
                    ClassUtils.forName("javax.xml.ws.WebServiceRef", CommonAnnotationBeanPostProcessor.class.getClassLoader());
            webServiceRefClass = clazz;
        }
        catch (ClassNotFoundException ex) {
            webServiceRefClass = null ;
        }

        try {
            @SuppressWarnings("unchecked")
            Class<? extends Annotation> clazz = (Class<? extends Annotation>)
                    ClassUtils.forName("javax.ejb.EJB", CommonAnnotationBeanPostProcessor.class.getClassLoader());
            ejbRefClass = clazz;
        }
        catch (ClassNotFoundException ex) {
            ejbRefClass = null ;
        }
        // Add @Resource annotation
        resourceAnnotationTypes.add (Resource. class );
         if (webServiceRefClass! = null ) { 
// Add @WebServiceRef annotation resourceAnnotationTypes.add(webServiceRefClass); }
if (ejbRefClass! = null ) {
// Add @EJB annotation resourceAnnotationTypes.add(ejbRefClass); } }

The above is a static code block, we know when the static code block will be executed, that is, when the class is loaded, that is, when the CommonAnnotationBeanPostProcessor class is loaded, the above static code block will be executed, we look at the static code block The logic in can be seen by adding three annotations to resourceAnnotationTypes, which is the interface represented by the annotations to be parsed by this class.

In the CommonAnnoatationBeanPostProcessor class, there is also an important attribute, injectionMetaDataCache, which stores the field or method information parsed by CommonAnnoatationBeanPostProcessor in the class, that is, the meta information of the field or method marked with three attributes.

Second, detailed

1. Method overview

Let's look at the methods in the CommonAnnoatationBeanPostProcessor class,

The above is the more important method in CommonAnnoatationBeanPostProcessor. There are two more familiar methods. The postProcessBeforeInstantiation and postProcessAfterInstantiation methods have been analyzed before.

@Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        return null;
    }

The above is the postProcessBeforeInstantiation method. The return value of this method is null, that is, a proxy object will not be generated before the bean is instantiated.

@Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) {
        return true;
    }

The above is the postProcessAfterInstantiation method, the return value of this method is true, which means that this class will not prevent the injection of attributes.

The postProcessProperties method in CommonAnnoatationBeanPostProcessor,

@Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
        try {
            metadata.inject(bean, beanName, pvs);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
        }
        return pvs;
    }

First look at this method, today this method is not the protagonist. The protagonist is the following method,

@Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
        InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
        metadata.checkConfigMembers(beanDefinition);
    }

This method will first call the postProcessMergedBeanDefinition method of the parent class, and then call the findResourceMetadata method.

The parent class of CommonAnnoatationBeanPostProcessor is InitDestoryAnnoatationBeanPostProcessor. In the InitDestoryAnnoatationBeanPostProcessor class, the postProcessMergedBeanDefinition method mainly completes the analysis and initialization (@PostConstruct) and destruction (@PreDestory) related annotations and caches into lifecycleMetadataCache.

The findResourceMetadata method of CommonAnnoatationBeanPostProcessor is mainly to parse @Resource, @WebServiceRef, @EJB three annotations and cache it to injectionMetadataCache. Today I will focus on analyzing the postProcessMergedBeanDefinition method of CommonAnnoatationBeanPostProcessor.

2, postProcessMergedBeanDefinition method

When this method is called, from the spring source code can be found as follows, in the doCreateBean method, only part of the code is posted below,

synchronized (mbd.postProcessingLock) {
             if (! mbd.postProcessed) {
                 try {
                     // 2, call beanPostProcessor is the bean post processor MergedBeanDefinitionPostProcessor, execute its postProcessMergedBeanDefinition method 
                    applyMergedBeanDefinitionPostProcessors (mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

The applyMergedBeanDefinitionPostProcessor method is called in the above code, the applyMergedBeanDefinitionPostProcessor method is as follows,

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof MergedBeanDefinitionPostProcessor) {
                MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
                bdp.postProcessMergedBeanDefinition (mbd, beanType, beanName);
            }
        }
    }

The beanPostProcessor in the beanFactory will be looped here, and there will definitely be a CommonAnnoatationBeanPostProcessor post processor, then its postProcessMergedBeanDefinition method will be called.

The postProcessMergedBeanDefinition method is as follows,

@Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
        InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
        metadata.checkConfigMembers(beanDefinition);
    }

As mentioned above, the parent class is not analyzed first, so the key analysis is findResourceMetadata and checkConfigMembers. findResourceMetadata is as follows

private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {
        // Fall back to class name as cache key, for backwards compatibility with custom callers.
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        // Quick check on the concurrent map first, with minimal locking.
        InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            synchronized (this.injectionMetadataCache) {
                metadata = this.injectionMetadataCache.get(cacheKey);
                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                    if (metadata != null) {
                        metadata.clear(pvs);
                    } 
// Important code, put the returned metadata object into the injectionMetadatCache cache, and the cache key is beanName for subsequent methods to take it out of the cache metadata
= buildResourceMetadata(clazz); this.injectionMetadataCache.put(cacheKey, metadata); } } } return metadata; }

Let's look at the buildResourceMetadata method,

private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
        if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
            return InjectionMetadata.EMPTY;
        }

        List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
        Class<?> targetClass = clazz;

        do {
            final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
             // Determine whether there is an annotation on the attribute
            ReflectionUtils.doWithLocalFields(targetClass, field -> {
                if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
                    }
                    currElements.add(new WebServiceRefElement(field, field, null));
                }
                else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        throw new IllegalStateException("@EJB annotation is not supported on static fields");
                    }
                    currElements.add(new EjbRefElement(field, field, null));
                }
                else if (field.isAnnotationPresent(Resource.class)) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        throw new IllegalStateException("@Resource annotation is not supported on static fields");
                    }
                    if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
                        currElements.add(new ResourceElement(field, field, null));
                    }
                }
            });
             // Determine whether there is an annotation on the method
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
                if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
                        }
                        if (method.getParameterCount() != 1) {
                            throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
                        }
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
                    }
                    else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            throw new IllegalStateException("@EJB annotation is not supported on static methods");
                        }
                        if (method.getParameterCount() != 1) {
                            throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
                        }
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                        currElements.add(new EjbRefElement(method, bridgedMethod, pd));
                    }
                    else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
                        if (Modifier.isStatic(method.getModifiers())) {
                            throw new IllegalStateException("@Resource annotation is not supported on static methods");
                        }
                        Class<?>[] paramTypes = method.getParameterTypes();
                        if (paramTypes.length != 1) {
                            throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
                        }
                        if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
                            PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                            currElements.add(new ResourceElement(method, bridgedMethod, pd));
                        }
                    }
                }
            });

            elements.addAll(0, currElements);
            targetClass = targetClass.getSuperclass();
        }
        while (targetClass != null && targetClass != Object.class);

        return InjectionMetadata.forElements(elements, clazz);
    }

This method is longer, but it mainly completes two things, judging whether there are @Resource, @WebServiceRef, @EJB annotations from the attributes and methods, and if it exists, it is placed in injectionMetadataCache. It can also be seen from here that these three annotations can be used in methods and properties.

At this point, the postProcssMergedBeanDefinition method of the CommonAnnoatationBeanPostProcessor class has been analyzed, and its role (excluding the role of its parent class) is annotated from the three annotations @Resource, @WebServiceRef, and @EJB in the resolution class, and put into the injectionMetadataCache cache for the postProcessProperties method use.

3. Use scenarios

From the above analysis, the role of the postProcessMergedBeanDefinition method of CommonAnnoatationBeanProcessor has been known. It is to parse @Resource, @WebServiceRef, @EJB and cache metadata information. The following will analyze how to use the information cached in injectionMetadataCache, which is the logic of the postProcessPerties method.

 

Originality is not easy, and corrections are welcome.

Guess you like

Origin www.cnblogs.com/teach/p/12688608.html