When we develop applications based on spring, we usually place the database configuration in the properties file.
Summary of the knowledge points involved in code analysis:
1.NamespaceHandler parses custom namespace in xml configuration file
2.ContextNamespaceHandler context-related parser, here defines the specific parser for parsing property-placeholder
3.BeanDefinitionParser parses the interface of bean definition
4.BeanFactoryPostProcessor After loading the bean definition, it can be modified.
5.PropertySourcesPlaceholderConfigurer handles placeholders in bean definition
Let's take a look at the specific usage first
Use of property
Configure properties file in xml file
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <context:property-placeholder location="classpath:foo.properties" /></beans>
In this way, the /src/main/resources/foo.properties file will be loaded by spring
If you want to use multiple configuration files, you can add the order field to sort
Use PropertySource to annotate configuration
Spring 3.1 has added the @PropertySource annotation to facilitate adding property files to the environment.
@Configuration@PropertySource("classpath:foo.properties")public class PropertiesWithJavaConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); }}Injection and use of properties
1. Use @Value annotation to obtain in java
@Value( "${jdbc.url}" )private String jdbcUrl;You can also add a default value
@Value( "${jdbc.url:aDefaultUrl}" )private String jdbcUrl;1. Get it in Spring's xml configuration file
<bean id="dataSource"> <property name="url" value="${jdbc.url}" /></bean>Source code analysis
Loading of properties configuration information
Spring will start container initialization work through AbstractApplicationContext#refresh at startup, and during this period, loadBeanDefinitions will be entrusted to parse the xml configuration file.
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }loadBeanDefinitions through layer-by-layer delegation, find the DefaultBeanDefinitionDocumentReader#parseBeanDefinition to parse the specific bean
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } Since this is not a standard class definition, it entrusts BeanDefinitionParserDelegate to parse
Find the corresponding processor through NamespaceHandler to find the ContextNamespaceHandler, and then find the PropertyPlaceholderBeanDefinitionParser parser analysis through id
@Override public void init() { // This is the parser we are looking for registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } PropertyPlaceholderBeanDefinitionParser is the focus of this round of code analysis.
Let's take a look at its parent class.
1.BeanDefinitionParser
Used by DefaultBeanDefinitionDocumentReader to parse personalized tags
Only a parse API that parses Element is defined here
public interface BeanDefinitionParser {BeanDefinition parse(Element element, ParserContext parserContext);} 2.AbstractBeanDefinitionParser
The default abstract implementation of the BeanDefinitionParser interface. The spring is good at it. It provides many convenient APIs here, and uses the template method design pattern to provide custom implementation hooks for subclasses.
Let's take a look at the specific processing logic when parse: call the hook parseInternal parse
3. AbstractSingleBeanDefinitionParser
Analyze, define the abstract parent class of a single BeanDefinition
In parseInternal, parse parentName, beanClass, source; and encapsulate using BeanDefinitionBuilder
4. AbstractPropertyLoadingBeanDefinitionParser
Analyze property-related properties, such as location, properties-ref, file-encoding, order, etc.
5.PropertyPlaceholderBeanDefinitionParser
There are not many things to deal with here, just set up inore-unresolvable and system-properties-mode
Loading properties file, instantiation of bean
Next, let's take a look at when this bean is instantiated. There are two types of instantiation of general classes. One is instantiated when the singleton system is started; the other is instantiated when the non-singleton (or singleton lazy loading) when the getBean is instantiated.
The trigger here is through BeanFcatoryPostProcessor.
BeanFactoryPostProcessor modifies bean definition before bean instantiation. For example, the placeholders in bean definition are solved here, and the properties we are using now are also solved here.
This is implemented through PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors.
Scan the BeanFactoryPostProcessor in the container, find the PropertySourcesPlaceholderConfigurer needed here, and instantiate it through the getBean of the container
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); }After the instantiation of PropertySourcesPlaceholderConfigurer is completed, it will be triggered directly and loaded information.
OrderComparator.sort(priorityOrderedPostProcessors); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
Let's take a look at the inheritance system of PropertySourcesPlaceholderConfigurer
1.BeanFactoryPostProcessor
Define an interface for modifying the properties of bean definition in a container. Its implementation class is instantiated before general classes are used and the properties of other classes are modified.
This is obviously different from BeanPostProcessor, which modifies bean instances.
2.PropertiesLoaderSupport
Abstract class that loads properties files.
The specific loading logic here is to entrust PropertiesLoaderUtils#fillProperties to implement
3.PropertyResourceConfigurer
The replacement of placeholders in bean definition is implemented by this abstract class.
Implement BeanFactoryPostProcessor#postProcessBeanFactory, iterate over the class definition in the container, and modify it
How to modify it is implemented by handing it to a subclass through the hook processProperties
4.PlaceholderConfigurerSupport
Use the visitor design pattern to update properties through BeanDefinitionVisitor and StringValueResolver
StringValueResolver is an interface that converts String type data. The API implementation that truly updates properties is actually in
PropertyPlaceholderHelper#parseStringValue
5.PropertySourcesPlaceholderConfigurer
Override postProcessorBeanFactory api definition parsing process
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.