Spring source code analysis 5: bean configuration, definition and registration

Time:2022-5-8

Spring source code analysis 5: bean configuration, definition and registration

staySpring source code analysis II: context component (webapplicationcontext)There are some points to be resolved:

  • ConfigurableListableBeanFactoryHow to load and instantiate beans
  • ResourceEditorRegistrarHow to register the Attribute Editor and how the Attribute Editor resolves to an object
  • PathMatchingResourcePatternResolverHow to resolve and load the resources specified by locationpattern
  • PropertySourcesPropertyResolverHow to resolve paths
  • XmlBeanDefinitionReaderHow to parse bean definitions
  • AnnotatedBeanDefinitionReaderHow do I register beans
  • ClassPathBeanDefinitionScannerHow are packages scanned

The first of these has beenSpring source code analysis 3: bean registration, parsing and instantiation mechanismIn this section, let’s take a look at the following items

1. ResourceEditorRegistrar

ResourceEditorRegistrar
The main function of is to fill in the attributes after the beandefinition is converted to beanwrapper when the bean is instantiated

For example, XML defines a bean like this

<bean id="demoBean" class="com.example.DemoBean">
  <property name="date" value="2021-10-15" />
</bean>

staycom.example.DemoBeanClass defines a propertydateyesDateType, but what spring reads from XML is a string, which requires spring to configure an attribute editor to convert the string into an object.

public class ResourceEditorRegistrar implements PropertyEditorRegistrar {
    //Register custom editor
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        //Registered entity, temporarily omitted, to be analyzed later
        //Here, first resolve the propertyeditorregistry
    }
}

Let’s have a look firstPropertyEditorRegistrySupport
How do I handle various types of attribute editors(PropertyEditorRegistryJust an interface,PropertyEditorRegistrySupportIs its default implementation)

1.1. PropertyEditorRegistrySupport

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
    //Default editor
    private Map<Class<?>, PropertyEditor> defaultEditors;
    //Override default editor
    private Map<Class<?>, PropertyEditor> overriddenDefaultEditors;
    //Custom editor
    private Map<Class<?>, PropertyEditor> customEditors;
    //Path custom editor
    private Map<String, CustomEditorHolder> customEditorsForPath;
    //Custom editor缓存
    private Map<Class<?>, PropertyEditor> customEditorCache;
}

1.1.1. PropertyEditorRegistrySupport.getDefaultEditor

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
    //Gets the default editor for requiredtype
    public PropertyEditor getDefaultEditor(Class<?> requiredType) {
        // ...  Code omission

        if (this.overriddenDefaultEditors != null) {
            //If there is a custom override of the default editor, return to the custom default editor
            PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
            if (editor != null) {
                return editor;
            }
        }
        //If defaulteditors have not been created, create
        if (this.defaultEditors == null) {
            createDefaultEditors();
        }
        return this.defaultEditors.get(requiredType);
    }

    //Create a default editor, including all basic and common types
    private void createDefaultEditors() {
        this.defaultEditors = new HashMap<>(64);

        this.defaultEditors.put(Charset.class, new CharsetEditor());
        this.defaultEditors.put(Class.class, new ClassEditor());
        this.defaultEditors.put(Class[].class, new ClassArrayEditor());
        this.defaultEditors.put(Currency.class, new CurrencyEditor());
        this.defaultEditors.put(File.class, new FileEditor());
        this.defaultEditors.put(InputStream.class, new InputStreamEditor());

        this.defaultEditors.put(InputSource.class, new InputSourceEditor());

        this.defaultEditors.put(Locale.class, new LocaleEditor());
        this.defaultEditors.put(Path.class, new PathEditor());
        this.defaultEditors.put(Pattern.class, new PatternEditor());
        this.defaultEditors.put(Properties.class, new PropertiesEditor());
        this.defaultEditors.put(Reader.class, new ReaderEditor());
        this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
        this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
        this.defaultEditors.put(URI.class, new URIEditor());
        this.defaultEditors.put(URL.class, new URLEditor());
        this.defaultEditors.put(UUID.class, new UUIDEditor());
        this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());

        this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
        this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
        this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
        this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
        this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

        this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
        this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

        this.defaultEditors.put(char.class, new CharacterEditor(false));
        this.defaultEditors.put(Character.class, new CharacterEditor(true));

        this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
        this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

        this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
        this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
        this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
        this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
        this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
        this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
        this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
        this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
        this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
        this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
        this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
        this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
        this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
        this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

        StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
        this.defaultEditors.put(String[].class, sae);
        this.defaultEditors.put(short[].class, sae);
        this.defaultEditors.put(int[].class, sae);
        this.defaultEditors.put(long[].class, sae);
    }
}

These default editors are relatively simple, all inbeans/propertyeditors
Under this package, only one is not under this package and needs to be parsed:ResourceArrayPropertyEditor

ResourceArrayPropertyEditorMultiple resources are resolved, such as multiple resources represented by the wildcard *

public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
    //Convert string value to target value
    @Override
    public void setAsText(String text) {
        //Resolve placeholder ${} in path
        String pattern = resolvePath(text).trim();
        try {
            //Use resourcepatternresolver Getresources parsing
            setValue(this.resourcePatternResolver.getResources(pattern));
        }
        catch (IOException ex) {
            // ...  Code omission
        }
    }

    //Sets the value of the conversion
    @Override
    public void setValue(Object value) throws IllegalArgumentException {
        //If it is a collection, continue processing
        //If it is an array but has not been converted to a resource, continue processing
        if (value instanceof Collection || (value instanceof Object[] && !(value instanceof Resource[]))) {
            Collection<?> input = (value instanceof Collection ? (Collection<?>) value : Arrays.asList((Object[]) value));
            Set<Resource> merged = new LinkedHashSet<>();
            for (Object element : input) {
                if (element instanceof String) {
                    //Resolve placeholder ${} in path
                    String pattern = resolvePath((String) element).trim();
                    try {
                        //Convert to resource and add
                        Resource[] resources = this.resourcePatternResolver.getResources(pattern);
                        Collections.addAll(merged, resources);
                    }
                    catch (IOException ex) {
                        // ...  Code omission
                    }
                }
                else if (element instanceof Resource) {
                    //If it is a resource, add
                    merged.add((Resource) element);
                }
                else {
                    // ...  Code omission
                }
            }
            super.setValue(merged.toArray(new Resource[0]));
        }
        else {
            super.setValue(value);
        }
    }
}

1.1.2. PropertyEditorRegistrySupport.registerCustomEditor

public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
    //Register custom editor
    @Override
    public void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor) {
        // ...  Code omission

        //If there is a PropertyPath, register in customeditorsforpath
        if (propertyPath != null) {
            if (this.customEditorsForPath == null) {
                this.customEditorsForPath = new LinkedHashMap<>(16);
            }
            this.customEditorsForPath.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
        }
        //Otherwise, register in customeditors
        else {
            if (this.customEditors == null) {
                this.customEditors = new LinkedHashMap<>(16);
            }
            this.customEditors.put(requiredType, propertyEditor);
            this.customEditorCache = null;
        }
    }

    //Add override default editor for requiredtype
    public void overrideDefaultEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
        if (this.overriddenDefaultEditors == null) {
            this.overriddenDefaultEditors = new HashMap<>();
        }
        this.overriddenDefaultEditors.put(requiredType, propertyEditor);
    }
}

1.2. ResourceEditorRegistrar.registerCustomEditors

public class ResourceEditorRegistrar implements PropertyEditorRegistrar {
    //Register custom editor
    @Override
    public void registerCustomEditors(PropertyEditorRegistry registry) {
        //Use the current resourceloader to override the default resource loader of resource, InputStream, file, path, etc
        //Other treatment methods remain unchanged
        ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
        doRegisterEditor(registry, Resource.class, baseEditor);
        doRegisterEditor(registry, ContextResource.class, baseEditor);
        doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
        doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
        doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
        doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
        doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
        doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));

        ClassLoader classLoader = this.resourceLoader.getClassLoader();
        doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
        doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
        doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));

        if (this.resourceLoader instanceof ResourcePatternResolver) {
            doRegisterEditor(registry, Resource[].class,
                    new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
        }
    }

    //If you can, overwrite the default editor, otherwise register the custom editor
    private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
        if (registry instanceof PropertyEditorRegistrySupport) {
            ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
        }
        else {
            registry.registerCustomEditor(requiredType, editor);
        }
    }
}

2. PathMatchingResourcePatternResolver

PathMatchingResourcePatternResolver
It is a resource finder with ant pattern wildcards, which can be used to find resources under the classpath or in the file system

Ant pattern matching rules are as follows

  1. ?Match one character
  2. *Match 0 or more characters
  3. **Match 0 or more directories

for example

  • /trip/api/*xmatching/trip/api/x/trip/api/ax/trip/api/abx, but does not match/trip/abc/x
  • /trip/a/a?xmatching/trip/a/abx, but does not match/trip/a/ax/trip/a/abcx
  • /**/api/aliematching/trip/api/alie/trip/dax/api/alie, but does not match/trip/a/api
  • /**/*.htmlmMatch all to.htmlmEnding path
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
    //Ant mode wildcard is used by default
    private PathMatcher pathMatcher = new AntPathMatcher();

    //Resolve the resource specified by locationpattern
    @Override
    public Resource[] getResources(String locationPattern) throws IOException {
        //Start with "classpath *:"
        if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
            //Yes *? Wildcard of {} symbol
            if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
                //Find out if it contains *? Resources covered by {} symbol path
                return findPathMatchingResources(locationPattern);
            }
            else {
                //No wildcards, treat as pure characters
                return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
            }
        }
        else {
            //Start with "War:" or other path with:
            int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
                    locationPattern.indexOf(':') + 1);
            if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                //Find out if it contains *? Resources covered by {} symbol path
                return findPathMatchingResources(locationPattern);
            }
            else {
                //Otherwise, as a single name resource
                return new Resource[] {getResourceLoader().getResource(locationPattern)};
            }
        }
    }

}

2.1. PathMatchingResourcePatternResolver.findPathMatchingResources

public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
    //Find out if it contains *? Resources covered by {} symbol path
    protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
        //Return the root path. For example, the root path of "/ WEB-INF / *. XML" is "/ WEB-INF /"
        String rootDirPath = determineRootDir(locationPattern);
        //Remove sub paths of root path
        String subPattern = locationPattern.substring(rootDirPath.length());
        //Get all resources under the root path
        Resource[] rootDirResources = getResources(rootDirPath);
        //Result set
        Set<Resource> result = new LinkedHashSet<>(16);
        for (Resource rootDirResource : rootDirResources) {
            //Analyze the agreement with:
            URL rootDirUrl = rootDirResource.getURL();
            //Bundle protocol
            if (rootDirUrl.getProtocol().startsWith("bundle")) {
                //As urlresource
                rootDirResource = new UrlResource(rootDirUrl);
            }
            //VFS protocol
            if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
                result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
            }
            //Jar, war, zip, wsjar, vfszip protocol
            else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
                //Load resources from jar files
                result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
            }
            else {
                //Load resources from system files
                result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
            }
        }
        return result.toArray(new Resource[0]);
    }
}

2.2. PathMatchingResourcePatternResolver.findAllClassPathResources

public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
    //Find resources that are not covered by wildcard paths
    protected Resource[] findAllClassPathResources(String location) throws IOException {
        //Remove the beginning/
        String path = location;
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        Set<Resource> result = doFindAllClassPathResources(path);
        return result.toArray(new Resource[0]);
    }

    protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
        Set<Resource> result = new LinkedHashSet<>(16);
        ClassLoader cl = getClassLoader();
        //Load resources through classloader
        Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
        while (resourceUrls.hasMoreElements()) {
            URL url = resourceUrls.nextElement();
            //Convert URL to urlresource and add
            result.add(convertClassLoaderURL(url));
        }
        if (!StringUtils.hasLength(path)) {
            //Processing jar resources, system file resources
            addAllClassLoaderJarRoots(cl, result);
        }
        return result;
    }
}

3. PropertySourcesPropertyResolver

PropertySourcesPropertyResolver
The main function of is through@PropertySourceAnnotation injects attribute sources into@Configuration, @Component, ...Annotated class

public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
    //Character value to object converter. Defaultconversionservice is used by default
    private volatile ConfigurableConversionService conversionService;
    //The prefix of the placeholder. The default is${
    private String placeholderPrefix = SystemPropertyUtils.PLACEHOLDER_PREFIX;
    //The suffix of the placeholder. The default is}
    private String placeholderSuffix = SystemPropertyUtils.PLACEHOLDER_SUFFIX;
    //Separator of multiple values, default:
    private String valueSeparator = SystemPropertyUtils.VALUE_SEPARATOR;
}
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
    //Get a property value
    protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
        if (this.propertySources != null) {
            //Traverse from source
            for (PropertySource<?> propertySource : this.propertySources) {
                //Get value from source
                Object value = propertySource.getProperty(key);
                if (value != null) {
                    if (resolveNestedPlaceholders && value instanceof String) {
                        //Resolve placeholder
                        value = resolveNestedPlaceholders((String) value);
                    }

                    //Convert the current value to other types of objects, if necessary
                    return convertValueIfNecessary(value, targetValueType);
                }
            }
        }
        //Return null if no
        return null;
    }

    //Resolve placeholder
    protected String resolveNestedPlaceholders(String value) {
        // ...  Code omission

        return resolvePlaceholders(value);
    }

    //Resolve placeholder
    public String resolvePlaceholders(String text) {
        if (this.nonStrictHelper == null) {
            this.nonStrictHelper = createPlaceholderHelper(true);
        }
        //Replace placeholder
        return doResolvePlaceholders(text, this.nonStrictHelper);
    }

    //Convert the current value to other types of objects, if necessary
    protected <T> T convertValueIfNecessary(Object value, Class<T> targetType) {
        // ...  Code omission

        ConversionService conversionServiceToUse = this.conversionService;
        if (conversionServiceToUse == null) {
            // ...  Code omission

            //Get the default converter defaultconversionservice
            conversionServiceToUse = DefaultConversionService.getSharedInstance();
        }
        //Conversion value
        return conversionServiceToUse.convert(value, targetType);
    }
}

Let’s seeDefaultConversionService
How are objects converted

public class DefaultConversionService extends GenericConversionService {
    public DefaultConversionService() {
        //Add a default converter when initializing
        addDefaultConverters(this);
    }

    //Add default converter
    public static void addDefaultConverters(ConverterRegistry converterRegistry) {
        //Add scalar class converter
        addScalarConverters(converterRegistry);
        //Add collection class converter
        addCollectionConverters(converterRegistry);

        //Convert ByteBuffer or byte array to any other type
        converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
        converterRegistry.addConverter(new StringToTimeZoneConverter());
        converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
        converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

        converterRegistry.addConverter(new ObjectToObjectConverter());
        converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
        converterRegistry.addConverter(new FallbackObjectToStringConverter());
        converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
    }

    //Add collection class converter
    public static void addCollectionConverters(ConverterRegistry converterRegistry) {
        ConversionService conversionService = (ConversionService) converterRegistry;

        //Array to set
        converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
        //Set to array
        converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

        //Array to another array
        converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
        //Set to another set
        converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
        //Map to another map
        converterRegistry.addConverter(new MapToMapConverter(conversionService));

        converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
        converterRegistry.addConverter(new StringToArrayConverter(conversionService));

        converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
        converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));

        converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
        converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

        converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
        converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));

        //Convert stream to array or collection
        converterRegistry.addConverter(new StreamConverter(conversionService));
    }

    //Add scalar class converter
    private static void addScalarConverters(ConverterRegistry converterRegistry) {
        //The number of one JDK version is converted to the number of other JDK versions
        converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());

        converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
        converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());

        converterRegistry.addConverter(new StringToCharacterConverter());
        converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

        converterRegistry.addConverter(new NumberToCharacterConverter());
        converterRegistry.addConverterFactory(new CharacterToNumberFactory());

        converterRegistry.addConverter(new StringToBooleanConverter());
        converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

        converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
        converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));

        converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
        converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));

        converterRegistry.addConverter(new StringToLocaleConverter());
        converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

        converterRegistry.addConverter(new StringToCharsetConverter());
        converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

        converterRegistry.addConverter(new StringToCurrencyConverter());
        converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

        //String to properties object
        converterRegistry.addConverter(new StringToPropertiesConverter());
        //Properties object to string
        converterRegistry.addConverter(new PropertiesToStringConverter());

        converterRegistry.addConverter(new StringToUUIDConverter());
        converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
    }
}

These default editors are inconvert/support
Under the package, it’s not difficult. Take a more complex exampleArrayToCollectionConverter

final class ArrayToCollectionConverter implements ConditionalGenericConverter {
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        // ...  Code omission

        //Get array length first
        int length = Array.getLength(source);
        TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
        //Create collection
        Collection<Object> target = CollectionFactory.createCollection(targetType.getType(),
                (elementDesc != null ? elementDesc.getType() : null), length);

        //Element does not require conversion
        if (elementDesc == null) {
            for (int i = 0; i < length; i++) {
                Object sourceElement = Array.get(source, i);
                target.add(sourceElement);
            }
        }
        //Element needs conversion
        else {
            for (int i = 0; i < length; i++) {
                Object sourceElement = Array.get(source, i);
                //Add after calling the injected conversionservice conversion element
                Object targetElement = this.conversionService.convert(sourceElement,
                        sourceType.elementTypeDescriptor(sourceElement), elementDesc);
                target.add(targetElement);
            }
        }
        return target;
    }
}

4. XmlBeanDefinitionReader

XmlBeanDefinitionReader
The main function of is fromxmlParsing bean definitions in

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    //Load bean definitions from a source
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        //Get thread pool to load source
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

        if (!currentResources.add(encodedResource)) {
            //If the addition fails, an error is reported
        }

        try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            //Load bean definitions through resource flows
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        catch (IOException ex) {
            // ...  Code omission
        }
        finally {
            // ...  Code omission
        }
    }

    //Load bean definitions through resource flows
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            //Gets the document object of the XML file
            Document doc = doLoadDocument(inputSource, resource);
            int count = registerBeanDefinitions(doc, resource);

            // ...  Code omission

            return count;
        }
        catch (BeanDefinitionStoreException ex) {
            // ...  Code omission
        }
        // ...  Code omission
    }

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //Instantiate a beandefinitiondocumentreader object. Defaultbeandefinitiondocumentreader is used by default
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

        // ...  Code omission

        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

        // ...  Code omission
    }
}

In essence, it is calledDefaultBeanDefinitionDocumentReader.registerBeanDefinitions

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    //Register bean definitions
    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }

    //Start from the doc root element to find and register the bean definition
    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        //Instantiate a new bean definition parser
        this.delegate = createDelegate(getReaderContext(), root, parent);

        // ...  Code omission

        //Parse bean definition
        parseBeanDefinitions(root, this.delegate);

        // ...  Code omission
    }

    //Parse bean definition
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        //If the xmlns namespace defined by the bean is empty, or http://www.springframework.org/schema/beans
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            //Traverse child nodes
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    //If the xmlns namespace defined by the bean is empty, or http://www.springframework.org/schema/beans
                    //Do default parsing
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    //Otherwise, do custom parsing
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            //Resolve custom elements
            delegate.parseCustomElement(root);
        }
    }
}
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    //Do default parsing
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        //If it is a < import > element, import other resources
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        //If it is an < alias > element, register the alias
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        //If it is a < bean > element, register the bean definition
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        //If it is a < beans > element, resolve the child element
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            doRegisterBeanDefinitions(ele);
        }
    }

    //If it is a < bean > element, register the bean definition
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        //Resolve the name, alias and definition of the bean and wrap it as beandefinitionholder
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {

            // ...  Code omission

            try {
                //Call getreadercontext() Getregistry() registers the bean definition
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                // ...  Code omission
            }
            // ...  Code omission
        }
    }
}

Go to another level, or callBeanDefinitionParserDelegate

public class BeanDefinitionParserDelegate {
    //Resolve custom elements
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        //Get xmlns namespace
        String namespaceUri = getNamespaceURI(ele);
        //No xmlns namespace, null returned
        if (namespaceUri == null) {
            return null;
        }
        //Get the namespace processor. Defaultnamespacehandlerresolver is used by default
        //In defaultnamespacehandlerresolver, meta-inf / spring.exe is loaded by default Handlers specifies the processor
        //The core processors are the following
        // http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
        // http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
        // http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
        // http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
        // http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
        // http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
        // http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
        // http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
        // http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler
        // http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
        // http\://www.springframework.org/schema/jdbc=org.springframework.jdbc.config.JdbcNamespaceHandler
        // http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

        // ...  Code omission

        //Call processor resolution
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
}

List these processors and analyze them later:

public class BeanDefinitionParserDelegate {
    //Resolve bean defined elements
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        //Get ID attribute
        String id = ele.getAttribute(ID_ATTRIBUTE);
        //Get name attribute
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<>();
        if (StringUtils.hasLength(nameAttr)) {
            //Use,; Separate into multiple aliases
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        //The ID attribute is used as beanname by default
        String beanName = id;
        //If there is no ID attribute but a name attribute, take the first name as the beanname
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
        }

        // ...  Code omission

        //Truly parse bean definitions
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                //If there is no beanname, the default beanname is generated
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);

                        // ...  Code omission
                    }

                }
                catch (Exception ex) {
                    // ...  Code omission

                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

    //Deep parsing bean definition
    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

        //Class attribute
        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }

        //Parent attribute
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            //Create a basic definition object with class and parent
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            //Apply other attributes to the definition object, such as singleton, abstract, lazy init, autowire, dependencies on, etc
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            //Set description attribute
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            //Parsing meta elements
            parseMetaElements(ele, bd);
            //Parse lookup method element
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            //Resolve the replaced method element
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            //Parse constructor Arg element
            parseConstructorArgElements(ele, bd);
            //Resolve property element
            parsePropertyElements(ele, bd);
            //Parse qualifier element
            parseQualifierElements(ele, bd);

            // ...  Code omission

            return bd;
        }
        catch (ClassNotFoundException ex) {
            // ...  Code omission
        }
        // ...  Code omission

        return null;
    }
}

5. AnnotatedBeanDefinitionReader

AnnotatedBeanDefinitionReader
The main function of is to register beans through annotations

public class AnnotatedBeanDefinitionReader {
    public void registerBean(Class<?> beanClass) {
        doRegisterBean(beanClass, null, null, null, null);
    }

    public void registerBean(Class<?> beanClass, @Nullable String name) {
        doRegisterBean(beanClass, name, null, null, null);
    }

    public void registerBean(Class<?> beanClass, Class<? extends Annotation>... qualifiers) {
        doRegisterBean(beanClass, null, qualifiers, null, null);
    }

    public void registerBean(Class<?> beanClass, @Nullable String name,
            Class<? extends Annotation>... qualifiers) {

        doRegisterBean(beanClass, name, qualifiers, null, null);
    }

    public <T> void registerBean(Class<T> beanClass, @Nullable Supplier<T> supplier) {
        doRegisterBean(beanClass, null, null, supplier, null);
    }

    public <T> void registerBean(Class<T> beanClass, @Nullable String name, @Nullable Supplier<T> supplier) {
        doRegisterBean(beanClass, name, null, supplier, null);
    }

    public <T> void registerBean(Class<T> beanClass, @Nullable String name, @Nullable Supplier<T> supplier,
            BeanDefinitionCustomizer... customizers) {

        doRegisterBean(beanClass, name, null, supplier, customizers);
    }

    //Register bean
    private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
            @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
            @Nullable BeanDefinitionCustomizer[] customizers) {

        //Create a basic annotation bean definition
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);

        // ...  Code omission

        //Make sure beanname
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

        //Handle general annotations of meta information, such as lazy, primary, dependson, role and description
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        //Processing of qualifier annotation
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }

        //Custom processor
        if (customizers != null) {
            for (BeanDefinitionCustomizer customizer : customizers) {
                customizer.customize(abd);
            }
        }

        //Encapsulated as holder
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

        // ...  Code omission

        //Register in registry
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }
}

6. ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner
The main function of is to scan the specified package to get the bean

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    //Scan package
    public int scan(String... basePackages) {
        // ...  Code omission

        //Scanning
        doScan(basePackages);

        //Register annotation parsing processor
        if (this.includeAnnotationConfig) {
            //Mainly the following beans
            // org.springframework.context.annotation.internalConfigurationAnnotationProcessor => ConfigurationClassPostProcessor
            // org.springframework.context.annotation.internalAutowiredAnnotationProcessor => AutowiredAnnotationBeanPostProcessor
            // org.springframework.context.annotation.internalCommonAnnotationProcessor => CommonAnnotationBeanPostProcessor
            // org.springframework.context.annotation.internalPersistenceAnnotationProcessor => PersistenceAnnotationBeanPostProcessor
            // org.springframework.context.event.internalEventListenerProcessor => EventListenerMethodProcessor
            // org.springframework.context.event.internalEventListenerFactory => DefaultEventListenerFactory
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }

        // ...  Code omission
    }

    //Scanning
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //Initialize container
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();

        //Traversal package
        for (String basePackage : basePackages) {
            //Get the components in the package that can be processed
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

            //Traversal component
            for (BeanDefinition candidate : candidates) {
                //Generate bean name
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    //Inject some default values and determine whether other beans need to be assembled automatically
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    //Handle general annotations of meta information, such as lazy, primary, dependson, role and description
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                //If it does not exist, it can be added
                if (checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);

                    // ...  Code omission

                    beanDefinitions.add(definitionHolder);
                    //Register in registry
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }

    //Get the components in the package that can be processed
    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        // ...  Code omission

        return scanCandidateComponents(basePackage);
    }

    //Get the components in the package that can be processed
    private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<>();
        try {
            //Add "classpath *:" prefix and "* * / *. Class" suffix
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    basePackage + '/' + this.resourcePattern;
            //Get all classes under the package
            Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
            //Traverse resources
            for (Resource resource : resources) {
                //If the resource is readable, proceed
                if (resource.isReadable()) {
                    try {
                        //Get meta information reader
                        MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                        //Initialize a basic bean definition
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);

                        //Add
                        candidates.add(sbd);
                    }
                    catch (Throwable ex) {
                        // ...  Code omission
                    }
                }
                else {
                    // ...  Code omission
                }
            }
        }
        catch (IOException ex) {
            // ...  Code omission
        }
        return candidates;
    }
}

List these processors and analyze them later:

follow-up

More blogs, seehttps://github.com/senntyou/blogs

Author:Deep (@ senntyou)

Copyright notice: Free Reprint – non commercial – non derivative – keep signature(Creative sharing 3.0 License

Recommended Today

JS generate guid method

JS generate guid method https://blog.csdn.net/Alive_tree/article/details/87942348 Globally unique identification(GUID) is an algorithm generatedBinaryCount Reg128 bitsNumber ofidentifier , GUID is mainly used in networks or systems with multiple nodes and computers. Ideally, any computational geometry computer cluster will not generate two identical guids, and the total number of guids is2^128In theory, it is difficult to make two […]