ConfigurationClassBeanDefinitionReader
1 How bean s are defined
Common ways to define beans are:
- Through xml, for example:
<bean id="dictionaryRelMap" class="java.util.HashMap"/>
- Use @Component and other annotations on Class through annotations, such as
@Component public class xxxServicer{ .... }
- By @Bean under the @Configuration class, for example
@Configuration public class xxxConfiguration{ @Bean public myBean myBean(){ return new myBean(); } }
Although the three ways of defining beans are different and the corresponding details are different, they are the same logically. The main process is as follows: The key problem is to find the way to define beans, then generate Bean Definition, register it in Spring context, and automatically create instances of beans by Spring.
2 BeanDefinition
BeanDefinition is an interface that describes a bean instance, such as SINGLETON or PROTOTYPE, what is the value of an attribute, what are the parameters of a constructor, and so on. Simply put, through a Bean Definition, we can complete a Bean instantiation. BeanDefinition and its main subclasses:
Here's a brief description of each subclass:
- RootBean Definition and ChildBean Definition: These two Bean Definitions are relative and have been replaced by GenericBean Definition since Spring 2.5 came out. Because it forces us to know the relationship between them when we write code.
- GenericBean Definition: Compared with RootBean Definition and ChildBean Definition, which must be hard-coded when defined, GenericBean Definition has the advantage of dynamically setting parent for GenericBean Definition.
- Annotated Bean Definition: Look at the name to know that it is used to read beans defined through annotations.
3 Define Bean s through xml files
Defining beans through xml was the earliest Spring way to define beans. Therefore, how to parse the xml tag into Bean Definition (), The entry is in the class of org.springframework.beans.factory.xml.XmlBeanDefinitionReader, but the actual work is in the class of org.springframework.beans.factory.xml.BeanDefinitionParserDelegate. There's a lot of code, but the actual logic is simple: parse the labels defined by Spring, such as < bean > < property >. Previously, I wrote an article about how to customize Spring tags, parse them and register them in Spring.—— Portal
4 Load Bean s with Spring-supported annotations such as @Component
If you want to use annotations such as @Component to define beans, a prerequisite is that there are & lt; context: component-scan/> or @ComponentScan annotations. But there is still a difference between the two ways:
4.1 <context:component-scan/>
Because & lt; context: component-scan/> is an xml tag, it is parsing xml and generating the class org. spring framework. context. annotation. ComponentScanBean Definition Parser, key code:
@Override public BeanDefinition parse(Element element, ParserContext parserContext) { //Get the base-package tag String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // The actual processing class is ClassPathBeanDefinitionScanner ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); //Scan all the classes under the base Package and register with Spring if there are @Component tags, etc. Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
4.2 @ComponentScan
Annotations correspond to generated classes that are org. spring framework. context. annotation. ComponentScanAnnotation Parser
In fact, the last thing actually worked was the ClassPathBean Definition Scanner. The ComponentScan AnnotationParser class is generated with the @Configuration annotation process (meaning @ComponentScan must be used with @Configuration). Processing @Configuration is actually org.spring framework.context.annotation.Configuration ClassPostProcessor. Does it feel a little twisted?
Simply put, when you find an @ComponentScan annotation when you process @Configuration, you generate a ComponentScan Annotation Parser to scan the @Component annotation.
4.3 ClassPathBeanDefinitionScanner
As mentioned above, both annotations and labels will eventually be handled by the ClassPathBean Definition Scanner class, which does 1. Scan all classes under the base Package, if there are @Component and other annotations, read the @Component related properties, generate Scanned GenericBean Definition, and register it in Spring.
5 By @Bean
As mentioned earlier, @ComponentScan is a part of the @Configuration process. Since the @Bean annotation must also be used with @Configuration, the processing of @Bean is also in @Configuration. In fact, it is handled by the class Configuration ClassBean Definition Reader. Key code:
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { //If you define yourself through the @Import annotation, you need to register yourself in Spring if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } //Here is the @Bean on the processing method. for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } //Processing @ImportResource, where parsing xml is the XmlBean Definition Reader for parsing xml mentioned above loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
To ensure org. spring framework. context. annotation. Configuration ClassPostProcessor
6 instantiate BeanDefinition
I've talked about how to convert different ways of defining beans into Bean Definition and add it to Spring (which is exactly kept in BeanFactory's Bean Definition Map). The example is in the final stage of Application Context, and the key code is in DefaultListable BeanFactory.
@Override public void preInstantiateSingletons() throws BeansException { for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName); boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() { @Override public Boolean run() { return ((SmartFactoryBean<?>) factory).isEagerInit(); } }, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } else { getBean(beanName); } } }
Through the code of the last instance of getBean, in AbstractAutowire Capable BeanFactory
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { //Processing xxAware interface if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // Call BeanPostProcessors#postProcessBeforeInitialization wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //Initialization, first determine whether it is Initializing Bean, invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // Call BeanPostProcessors#postProcessAfterInitialization wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
From the above initialization, you can see the invocation order of InitializeBean and BeanPostProcessors
7 Summary
In summary, Spring loads beans with the same idea. First, it reads relevant information to generate Bean Definition, and then initializes beans through Bean Definition. If you know the above routines, you can clearly customize Xml tags or custom annotations to inject beans into Spring.