Log4J
When using Spring framework, we must use Apache Common Logging to print log in console or binary files. As we known, Spring log configuration can change in the web.xml when it works on server. But in these series of articles, we try to analyze the Spring core techniques without works on server. So there should have another way to configure log without web.xml because we want to see the debug informations.
Spring gives Log4jConfigurer.initLogging() method (has been deprecated in Spring 4.2.1) to initialize log4j from the given file location. Note that we always use Maven to build project, but maven might mash src and resources folders to one. Thus we should use classpath: ,without slash, to represent resources folder in the project.
Now Apache Commons Logging (JCL) provides thin-wrapper Log implementations for other logging tools, including Log4J. But Log4jConfigurer.initLogging() calls methods from log4j 1.x. It means that we should add log4j 1.x dependency to make sure that our log configuration file works smoothly.
Here is the configure file log4j.properties, and just opening debug function in org.springframework.beans because we want to know the IOC process:
1 | log4j.rootCategory=INFO, stdout |
As last article, we run the same code but add our log configuration before:
1 | Log4jConfigurer.initLogging("classpath:log4j.properties"); |
Now we can follow the log information to know the IOC clearly.
ClassPathXmlApplicationContext
1 | 17:23:15,265 INFO main ClassPathXmlApplicationContext org.springframework.context.support.AbstractApplicationContext.prepareRefresh:581 - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@42dafa95: startup date [Fri Oct 21 17:23:15 EDT 2016]; root of context hierarchy |
First we step into ClassPathXmlApplicationContext constructor and it calls refresh(). This method is declared in the interface ConfigurableApplicationContext, implements in AbstractApplicationContext, finally called in ClassPathXmlApplicationContext which is a subclass of AbstractApplicationContext.
This method can load or refresh the persistent representation of the configuration, which might an XML file, properties file, or relational database schema. As this is a startup method, it should destroy already created singletons if it fails, to avoid dangling resources. In other words, after invocation of that method, either all or no singletons at all should be instantiated.
As we see in the document, refresh() is used to load and refresh persistent representation configuration. It should be done first of all.
XmlBeanDefinitionReader
1 | 17:23:15,555 INFO main XmlBeanDefinitionReader org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions:317 - Loading XML bean definitions from class path resource [services.xml] |
The refresh() method calls obtainFreshBeanFactory() in which can tell the subclass to refresh the internal bean factory. So how to let a class to call the method (in this case it is refreshBeanFactory()) in its subclass? It is a very classical design pattern.
First, the AbstractApplicationContext declares:
1 | protected abstract void refreshBeanFactory() |
This method is implemented in AbstractRefreshableApplicationContext which is a subclass of AbstractApplicationContext:
1 |
|
Therefore the father class can call the method which implemented in its subclass.
Here is XmlBeanDefinitionReader:
Bean definition reader for XML bean definitions. Delegates the actual XML document reading to an implementation of the
BeanDefinitionDocumentReaderinterface. Typically applied to aDefaultListableBeanFactoryor aGenericApplicationContext.
This class loads a DOM document and applies the
BeanDefinitionDocumentReaderto it.
The document reader will register each bean definition with the given bean factory, talking to the latter’s implementation of the
BeanDefinitionRegistryinterface.
loadBeanDefinition() method is used to load bean definitions from the specified XML file.
DefaultDocumentLoader
XmlBeanDefinitionReader calls doLoadDocument() and it has local variable DocumentLoader documentLoader which calls loadDocument().
1 | 19:38:23,603 DEBUG main DefaultDocumentLoader org.springframework.beans.factory.xml.DefaultDocumentLoader.loadDocument:73 - Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl] |
Here is DefaultDocumentLoader:
Spring’s default {@link DocumentLoader} implementation. Simply loads
Documentdocuments using the standard JAXP-configured XML parser. If you want to change theDocumentBuilderthat is used to load documents, then one strategy is to define a corresponding Java system property when starting your JVM. For example, to use the OracleDocumentBuilder, might start your application like as follows:
java -Djavax.xml.parsers.DocumentBuilderFactory=oracle.xml.jaxp.JXDocumentBuilderFactory MyMainClass
DefaultBeanDefinitionDocumentReader
Here is DefaultBeanDefinitionDocumentReader which is used to read bean definitions:
Default implementation of the
BeanDefinitionDocumentReaderinterface that reads bean definitions according to the “spring-beans” DTD and XSD format (Spring’s default XML bean definition format).
The structure, elements, and attribute names of the required XML document are hard-coded in this class. (Of course a transform could be run if necessary to produce this format).
<beans>does not need to be the root element of the XML document: this class will parse all bean definition elements in the XML file, regardless of the actual root element.
And registerBeanDefinitions(doc, readerContext):
Read bean definitions from the given DOM document and register them with the registry in the given reader context.
docthe DOM documentreaderContextthe current context of the reader (includes the target registry and the resource being parsed)
DefaultListableBeanFactory
Default implementation of the
ListableBeanFactoryandBeanDefinitionRegistryinterfaces: a full-fledged bean factory based on bean definition objects.
Typical usage is registering all bean definitions first (possibly read from a bean definition file), before accessing beans. Bean definition lookup is therefore an inexpensive operation in a local bean definition table, operating on pre-built bean definition metadata objects.
Can be used as a standalone bean factory, or as a superclass for custom bean factories. Note that readers for specific bean definition formats are typically implemented separately rather than as bean factory subclasses: see for example
PropertiesBeanDefinitionReaderandXmlBeanDefinitionReader.
For an alternative implementation of the
ListableBeanFactoryinterface, have a look atStaticListableBeanFactory, which manages existing bean instances rather than creating new ones based on bean definitions.
It implements the bean register method registerBeanDefinition() which is declared in interface BeanDefinitionRegistry.
Register a new bean definition with this registry. Must support
RootBeanDefinitionandChildBeanDefinition.
Now we look at DefaultListableBeanFactory and find that it has some private variables.
1 | /** Map of bean definition objects, keyed by bean name */ |
Note that beanDefinitionMap is a java.util.concurrent.ConcurrentHashMap and beanDefinitionNames is a volatile variable.
1 | this.beanDefinitionMap.put(beanName, beanDefinition); |
TODO: java.util.concurrent.ConcurrentHashMap and volatile variable.