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:
ConfigurableListableBeanFactory
How to load and instantiate beansResourceEditorRegistrar
How to register the Attribute Editor and how the Attribute Editor resolves to an objectPathMatchingResourcePatternResolver
How to resolve and load the resources specified by locationpatternPropertySourcesPropertyResolver
How to resolve pathsXmlBeanDefinitionReader
How to parse bean definitionsAnnotatedBeanDefinitionReader
How do I register beansClassPathBeanDefinitionScanner
How 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.DemoBean
Class defines a propertydate
yesDate
Type, 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(PropertyEditorRegistry
Just an interface,PropertyEditorRegistrySupport
Is 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
ResourceArrayPropertyEditor
Multiple 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
?
Match one character*
Match 0 or more characters**
Match 0 or more directories
for example
/trip/api/*x
matching/trip/api/x
、/trip/api/ax
、/trip/api/abx
, but does not match/trip/abc/x
/trip/a/a?x
matching/trip/a/abx
, but does not match/trip/a/ax
、/trip/a/abcx
/**/api/alie
matching/trip/api/alie
、/trip/dax/api/alie
, but does not match/trip/a/api
/**/*.htmlm
Match all to.htmlm
Ending 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@PropertySource
Annotation 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 fromxml
Parsing 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:
- SimpleConstructorNamespaceHandler
- SimplePropertyNamespaceHandler
- UtilNamespaceHandler
- ContextNamespaceHandler
- JeeNamespaceHandler
- LangNamespaceHandler
- TaskNamespaceHandler
- CacheNamespaceHandler
- MvcNamespaceHandler
- AopNamespaceHandler
- JdbcNamespaceHandler
- TxNamespaceHandler
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:
- ConfigurationClassPostProcessor
- AutowiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- PersistenceAnnotationBeanPostProcessor
- EventListenerMethodProcessor
- DefaultEventListenerFactory
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)