Use of annotations
public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext( "edu.demo.spring.bean","edu.demo.spring.ext"); Boy boy = context.getBean(Lad.class); boy.sayLove(); }
The above is the sample code. The main steps are as follows:
To debug and analyze, you need to debug and analyze the break point in the registerBeanDefinition method under the DefaultListableBeanFactory class.
Annotation loading processing
The following figure shows the specific call stack information:
Step 1: AnnotationConfigApplicationContext
/** * Create an AnnotationConfigApplicationContext container, and then scan the components under the given package * Register these components as bean definition information and automatically refresh the container * @param basePackages Scan the package of component classes, and multiple can be defined */ public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }
/** * In this constructor, AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner objects are built * AnnotatedBeanDefinitionReader Used to interpret annotation information * ClassPathBeanDefinitionScanner Used to scan packets */ public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
In the AnnotationConfigApplicationContext class, the main work is to build AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner objects in the constructor.
Step 2: AnnotatedBeanDefinitionReader
/** * Create an AnnotatedBeanDefinitionReader from the given Bean definition registration interface * Then initialize the AnnotatedBeanDefinitionReader */ public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) { this(registry, getOrCreateEnvironment(registry)); } /** * Get Environment from BeanDefinitionRegistry * If not, a StandardEnvironment is returned */ private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); if (registry instanceof EnvironmentCapable) { return ((EnvironmentCapable) registry).getEnvironment(); } return new StandardEnvironment(); } public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { //Non null judgment Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }
In the AnnotatedBeanDefinitionReader class, the main thing to do is to initialize the AnnotatedBeanDefinitionReader object.
Step 3: AnnotationConfigUtils
/** * Register all processors related to annotation configuration and register them with BeanFactory * @param registry the registry to operate on */ public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) { registerAnnotationConfigProcessors(registry, null); } /** * Register all processors related to annotation configuration and register them with BeanFactory */ public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { ...... //Register various processors to be used as bean definitions, and then inject them into the bean factory for use as beans Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); //@Configuration annotation class post processor if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } //@Autowired annotation class post processor if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check whether JSR-250 is supported if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check whether JPA is supported if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; } private static BeanDefinitionHolder registerPostProcessor( BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) { definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(beanName, definition); return new BeanDefinitionHolder(definition, beanName); }
You can know from the method name that the main thing to do in this step is to register the annotation configured Processor and register it in the internal BeanFactory. What is the Processor used for here?
In the process of loading annotations, in addition to loading and registering their own beans, Spring also injects various beans into the factory to process various logic. For example, the relevant processing objects defined by annotation beans in the above code are injected into the factory through bean definitions.
Step 4:
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { this.beanFactory.registerBeanDefinition(beanName, beanDefinition); }
Register the Bean definition information of the processor in the BeanFactory.
From the above call stack, we don't register the Bean definition information defined by ourselves, but some processors. And I don't see it doing the job of scanning packets.
From the code in the first step, we can see that the second step is to execute the scan method and the third step is the refresh method only in the constructor of AnnotationConfigApplicationContext. The annotation method is to prepare various initialization in the constructor, while the XML method is to register the Bean definition in the refresh method.
registerAnnotationConfigProcessors, understood from the method name, is to register some annotation configured processors. Let's start with the ConfigurationClassPostProcessor to see what these processors are used for.
/** * {@link BeanFactoryPostProcessor} used for bootstrapping processing of * {@link Configuration @Configuration} classes. * * <p>Registered by default when using {@code <context:annotation-config/>} or * {@code <context:component-scan/>}. Otherwise, may be declared manually as * with any other BeanFactoryPostProcessor. * * <p>This post processor is priority-ordered as it is important that any * {@link Bean} methods declared in {@code @Configuration} classes have * their corresponding bean definitions registered before any other * {@link BeanFactoryPostProcessor} executes. * * @author Chris Beams * @author Juergen Hoeller * @author Phillip Webb * @since 3.0 */ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware { }
It can be seen from the annotation that this is the beanfactoryprocessor used to process the @ Configuration annotation in the startup phase. However, beanfactoryprocessor is not seen here, but there is an interface BeanDefinitionRegistryPostProcessor. Click in to see that its parent is beanfactoryprocessor.
The inheritance system of ConfigurationClassPostProcessor is as follows:
Let's take a look at BeanDefinitionRegistryPostProcessor:
/** * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for * the registration of further bean definitions <i>before</i> regular * BeanFactoryPostProcessor detection kicks in. In particular, * BeanDefinitionRegistryPostProcessor may register further bean definitions * which in turn define BeanFactoryPostProcessor instances. * * @author Juergen Hoeller * @since 3.0.1 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor */ public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { /** * Modify the application context's internal bean definition registry after its * standard initialization. All regular bean definitions will have been loaded, * but no beans will have been instantiated yet. This allows for adding further * bean definitions before the next post-processing phase kicks in. * @param registry the bean definition registry used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; }
BeanDefinitionRegistryPostProcessor extends beanfactoryprocessor and adds
Processing of BeanDefinitionRegistry location, that is, it can pre process the registered BeanDefinitionRegistry in advance
As can be seen from the above figure, during the construction of beans, the places pointed by the arrow can be extended, so it is not difficult to see the role of BeanDefinitionRegistryPostProcessor
Let's look at beanfactoryprocessor:
@FunctionalInterface public interface BeanFactoryPostProcessor { /** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for overriding or adding * properties even to eager-initializing beans. * @param beanFactory the bean factory used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
From the above annotation information, we can know that what we do here is to process the bean definition information in BeanFactory before all bean definitions are loaded but instantiated.
This is one of the extension points provided by Spring. Before beanfactory starts to create bean instances, it performs some processing on the bean definition information in beanfactory
Mount external profile:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > <property name="locations" value="classpath:application.properties"/> </bean>
The following is a simple implementation example:
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println(this + "BeanFactoryPostProcessor Work.................................."); } }
In the test class, add the scanning of the package of the above class:
ApplicationContext context = new AnnotationConfigApplicationContext( "edu.demo.spring");
The following is its call stack:
In the postprocessor registrationdelegate The mybeanfactoryprocessor created and executed by invokebeanfactoryprocessors.
The output is as follows:
Relationship between IOC container and beanfactoryprocessor
Annotation scanning process
Set a breakpoint at the position shown in the figure above, release it, and finally go to defaultlistablebeanfactory The registerbeandefinition breakpoint can get the call stack.
Step 1:
/** * Performs a scan operation within the specified package * <p>Note that {@link #refresh()} must be called in order for the context * to fully process the new classes. * @param basePackages the packages to scan for component classes * @see #register(Class...) * @see #refresh() */ @Override public void scan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); this.scanner.scan(basePackages); }
/** * Performs a scan operation within the specified package * Build and initialize the ClassPathBeanDefinitionScanner scanner object to start scanning * @param basePackages the packages to check for annotated classes * @return number of beans registered */ public int scan(String... basePackages) { //Gets the number of bean definitions scanned int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
Step 2:
/** * Perform a scan within the specified base packages, * returning the registered bean definitions. * <p>This method does <i>not</i> register an annotation config processor * but rather leaves this up to the caller. * @param basePackages the packages to check for annotated classes * @return set of beans registered if any for tooling registration purposes (never {@code null}) */ protected Set<BeanDefinitionHolder> doScan(String... basePackages) { }
Complete the scan of bean definitions under the package name, and return the registered bean definition collection.
Step 3:
// Complete the registration of the bean definition to the bean factory registerBeanDefinition(String, BeanDefinition):929, DefaultListableBeanFactory (org.springframework.beans.factory.support) registerBeanDefinition(String, BeanDefinition):323, GenericApplicationContext (org.springframework.context.support) registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry):164, BeanDefinitionReaderUtils (org.springframework.beans.factory.support) registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry):320, ClassPathBeanDefinitionScanner (org.springframework.context.annotation)
The key part is the doScan method. Let's see what it does:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); //Traverse the package to scan for (String basePackage : basePackages) { //Find all candidate components (configured bean s) under the specified package Set<BeanDefinition> candidates = findCandidateComponents(basePackage); //Scan all candidate components for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //Register bean definitions into BeanFactory registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
Enter the findCandidateComponents method:
/** * Scan the specified path to obtain candidate components * @param basePackage the package to check for annotated classes * @return a corresponding Set of autodetected bean definitions */ public Set<BeanDefinition> findCandidateComponents(String basePackage) { //If there is a component index and the index supports contained filters, the selected component is obtained from the component index if (this.componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { // Otherwise, scan the specified package under the class directory return scanCandidateComponents(basePackage); } }
Please refer to the official document for component index: https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/core.html#beans-scanning-index
If you want to use the component index, you need to add the following maven configuration:
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-indexer</artifactId> <version>5.1.8.RELEASE</version> <optional>true</optional> </dependency> </dependencies>
It is not used here, so the scanCandidateComponents method is entered:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) { //A collection of all candidate components Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { //Build the path expression of the scan package, similar to the tangent expression //classpath*:edu/demo/spring/ext/**/*.class String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // The ResourcePatternResolver scanned the package and got class file // Logic of scanning: look under the package class file. Here, you need to be able to specify packages flexibly, so you need to use pattern matching // The Ant Path pattern used by default matches, such as the specified package edu demo.**. service Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { // MetadataReader, metadata reader, similar to xml reader MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { // Finally, the annotation bean definition is loaded with scannedgenericbean definition through MetadataReader ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); //It must be an inheritable class, not an interface or closed class. The abstract class must have a Lookup annotation if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else { // Ignore, not a top-level concrete class if (debugEnabled) { logger.debug("Ignored because not a concrete top-level class: " + resource); } } } else { // Ignored, cannot match to filter if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; }
Enter the getResourcePatternResolver method:
private ResourcePatternResolver getResourcePatternResolver() { if (this.resourcePatternResolver == null) { this.resourcePatternResolver = new PathMatchingResourcePatternResolver(); } return this.resourcePatternResolver; }
PathMatchingResourcePatternResolver class: it can resolve the specified resource location path to one or more matching resources. It can be one-to-one resource assignment or one to many matching pattern, through ant path format, classpath *: prefix
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { // ...... // AntPathMatcher is used by default private PathMatcher pathMatcher = new AntPathMatcher(); // ...... }
Diagram of pattern matching:
Let's take a look at the ScannedGenericBeanDefinition:
public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition { private final AnnotationMetadata metadata; /** * Create a new ScannedGenericBeanDefinition for the class that the * given MetadataReader describes. * @param metadataReader the MetadataReader for the scanned target class */ public ScannedGenericBeanDefinition(MetadataReader metadataReader) { Assert.notNull(metadataReader, "MetadataReader must not be null"); this.metadata = metadataReader.getAnnotationMetadata(); setBeanClassName(this.metadata.getClassName()); setResource(metadataReader.getResource()); } @Override public final AnnotationMetadata getMetadata() { return this.metadata; } @Override @Nullable public MethodMetadata getFactoryMethodMetadata() { return null; } }
Inheriting from GenericBeanDefinition class and based on the AnnotatedBeanDefinition implementation of ASM ClassReader, it supports annotation metadata.
Why use AnnotatedBeanDefinition instead of GenericBeanDefinition here?
Let's take a look at the two methods provided by the AnnotatedBeanDefinition interface:
public interface AnnotatedBeanDefinition extends BeanDefinition { /** * Get metadata for annotations * Obtain the annotation metadata (as well as basic class metadata) * for this bean definition's bean class. * @return the annotation metadata object (never {@code null}) */ AnnotationMetadata getMetadata(); /** * Get metadata of factory method * Obtain metadata for this bean definition's factory method, if any. * @return the factory method metadata, or {@code null} if none * @since 4.1.1 */ @Nullable MethodMetadata getFactoryMethodMetadata(); }
Metadata information
Spring defines a set of schemes to describe the description interfaces of a class, annotations on a class, a method, etc., which defines some related operations.
For example:
- Class: what is the name of the class and what modifiers are used
- Annotation: the name of the annotation on the class, the meta annotation information of the annotation, the annotation information of the annotation, and the attributes in the annotation
MetadataReader interface:
/** * Simple facade for accessing class metadata, * as read by an ASM {@link org.springframework.asm.ClassReader}. * Read the meta information of the class through ASM's ClassReader to provide a simple appearance mode. * 1. Use ASM to realize this function; 2. This is a simple appearance mode implementation * * @author Juergen Hoeller * @since 2.5 */ public interface MetadataReader { /** * Return the resource reference for the class file. * Returns the resource file of the class */ Resource getResource(); /** * Read basic class metadata for the underlying class. */ ClassMetadata getClassMetadata(); /** * Read full annotation metadata for the underlying class, * including metadata for annotated methods. * Return metadata information of annotation */ AnnotationMetadata getAnnotationMetadata(); }
Implementation class: SimpleMetadataReader
final class SimpleMetadataReader implements MetadataReader { SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException { // Build annotation metadata read visitor SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader); // Create the ASM ClassReader object corresponding to the resource, and access and interpret the ClassReader object with the visitor getClassReader(resource).accept(visitor, PARSING_OPTIONS); // Annotated class scanned this.resource = resource; // Get annotation metadata information this.annotationMetadata = visitor.getMetadata(); } }
ASM is a low-level bytecode operation library. The official website address is: https://asm.ow2.io/
Class information and annotation information read through ASM bytecode operation Library in Spring. Their class relationship diagram is as follows:
Scan filter
/** * Determine whether the given class does not match any exclude filter * and does match at least one include filter. * @param metadataReader the ASM ClassReader for the class * @return whether the class qualifies as a candidate component */ protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }
<context:component-scan base-package="edu.demo.spring.web" > <context:exclude-filter type="annotation" expression="@Service"/> <context:include-filter type="annotation" expression="@Controller"/> </context:component-scan>
The filter can be configured in the xml file in the above way. exclude does not include these components, and include needs to include these components.
@How does the service @controller match? What is the filter like by default? See the following code:
protected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } }
Parent class of AnnotationTypeFilter AbstractTypeHierarchyTraversingFilter#match
(MetadataReader,MetadataReaderFactory) method, and finally to the AnnotationTypeFilter#matchSelf method:
@Override protected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); // Include the specified annotation or the annotation contains the specified annotation, such as the specified @ Componet annotation, which is included in the @ Service annotation return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); }
Custom implementation TypeFilter:
public class MyTypeFilter implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // Use the class information and annotation information in the metadata reader to filter and judge your logic return metadataReader.getClassMetadata().getClassName().equals(Lad.class.getName()); } }
Bean defined registration
/** * Register the given bean definition information into the bean factory * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with * @throws BeanDefinitionStoreException if registration failed */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. //Get beanName String beanName = definitionHolder.getBeanName(); //Register bean definition information through the bean factory registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. //Bind the beanName to the alias. If there is an alias, register the alias String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
The core part is in the DefaultListableBeanFactory#registerBeanDefinition method:
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //Judge whether the beanName and beanDefinition information are empty Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //If there is a bean definition inherited from AbstractBeanDefinition //Then verify the bean if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //Does the relevant bean definition already exist BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); //When the bean definition already exists if (existingDefinition != null) { //Throw an exception if override override is not allowed if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } //Run the process of rewriting and overwriting, print some log information, and inform the processed information else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } //Overwrite the original bean definition information this.beanDefinitionMap.put(beanName, beanDefinition); } //The bean definition of beanName does not exist else { //Is the current bean object being created if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) //During startup, synchronous processing is used to prevent iterative exceptions of collections in other places synchronized (this.beanDefinitionMap) { //Store using ConcurrentHashMap this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else { // Still in startup registration phase //The current bean has not started to be created yet. put it directly this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } //If the corresponding singleton bean exists, reset the bean if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } // You need to freeze the configuration and clear the type mapping cache else if (isConfigurationFrozen()) { clearByTypeCache(); } }
For alias processing, the alias is stored as a Key and the beanName as a Value
@Override public void registerAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); synchronized (this.aliasMap) { if (alias.equals(name)) { this.aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name"); } } else { String registeredName = this.aliasMap.get(alias); if (registeredName != null) { if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'"); } } checkForAliasCircle(name, alias); this.aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'"); } } } }