Spring technology insider-Implementation of IOC containers of Spring Framework (1)
I. SpringIOC container Overview
IOC container and dependency inversion mode
In an object-oriented system, objects encapsulate data and process data. The dependency between objects is often reflected in the dependency on data and methods. These dependencies can be completed by injecting the object dependency to the IOC container of the framework. It can decouple the code and improve the Code testability.
There are many ways to implement dependency control inversion. In Spring, the IOC container is the carrier to implement this mode. It can directly inject data into the object when an object is generated or initialized, you can also inject method calls by injecting object references into the object data domain. Such dependency injection can be recursive, and objects are input layer by layer. It simplifies the management of object dependencies and greatly simplifies the complexity of object-oriented systems.
About how to reverse the control of dependency, transfer control from specific business objects to the platform or framework. It is an effective and feasible solution to reduce the complexity of object-oriented system design and improve the testability of object-oriented systems.
After the application control is reversed, when an object is created, the external entities of all objects in a control system pass the reference of the objects on which it depends to him, the dependency is injected into the object. Therefore, the inversion of control refers to how an object obtains the reference of the object on which it depends. Here, the reversal refers to the reversal of the responsibility.
By using the IOC container, the management of object dependencies is reversed and transferred to the IOC container. The dependency between objects is managed by the IOC container, and the IOC container injects the object. To put it simply, because the establishment and maintenance of many object dependencies does not need to be highly correlated with the system running status, the operations that need to be executed in object-oriented programming, such as creating objects and assigning values to object references, can be handed over to containers for unification. In this way, the components with the same functions scattered in different codes become part of the container, that is, part of the infrastructure of the object-oriented system.
The dependency between objects is also relatively stable and will not change with the running state of the application. These features make these objects very suitable for IOC container management, although they exist in the application system, the application system does not take responsibility for managing these objects, but instead delivers the responsibility to the container through Dependency inversion. With these background, it is not difficult to understand the principles of Spring IOC containers. Spring has its own unique ideas, implementation skills, and rich product features for specific implementation of the Principles.
SpringIOC application scenarios
In Java EE Enterprise Application Development, the IOC design mode is a powerful tool to decouple the complex relationship between components. The Spring IOC module is an implementation of this mode.
From the perspective of basic services, the services provided by Spring and the services provided by EJB containers are not very different, but only in how to obtain services, there is a big difference between the two designs: in Spring, Spring IOC provides a basic JavaBean container to manage dependencies in the IOC mode, through dependency injection and AOP, the basic functions such as transaction management and lifecycle management are enhanced for POJO objects such as JavaBean. For EJB, A simple EJB component needs to Write Remote/local interfaces, Home interfaces, and Bean implementation classes, and the EJB operation cannot be separated from the EJB container, other EJB components also need to be searched through methods such as JNDI, resulting in dependency on EJB containers and technical specifications. That is to say, Spring restores the EJB component to a POJO object or a JavaBean object, reducing the application development dependency on the traditional J2EE technical specifications.
When using the IOC container, you can reverse the direction of resource acquisition so that the IOC container can actively manage these dependencies and inject these dependencies into components, this makes the dependency adaptation and management more flexible. In specific implementation, interface injection (type 1 IOC), setter injection (type 2 IOC), and constructor injection (type 3 IOC) are the main injection methods. In Spring's IOC design, setter injection and constructor injection are the main methods. In contrast, setter injection is a common injection method when Spring is used, and to prevent injection exceptions, the SpringIOC container also provides a check for specific dependencies.
Ii. Design and Implementation of IOC container series: BeanFactory and ApplicationContext
There are two main container series in SpringIOC container design. One is the simple container series that implements the BeanFactory interface. This series of containers only implement the most basic functions of containers; the other is ApplicationContext, which exists as the container's advanced form. The application context adds many framework-oriented features on the basis of simple containers and makes many adaptation to the application environment. Next, we will briefly analyze the design and implementation of the SpringIOC container.
Spring IOC container Series
For IOC container users, BeanFactory and ApplicationContext that we often come into use can be seen as specific container representations. If we go deep into the implementation of Spring, it actually represents a series of container products with different functions. The function size of the Knowledge container has its own characteristics.
Just like a product that requires product specifications, as an IOC container, you also need to specify basic functional specifications for its specific implementation. This functional specification is designed as an interface class BeanFactory, it embodies the most basic functional specifications Spring has set for users to use the IOC container. Take a bucket sold by a department store as an example. If the IOC container is regarded as a bucket, the BeanFactory defines the basic functions that can be used as a bucket, for example, at least water and a pick-up.
Design of Spring IOC container
We have learned more about the IOC container series. In Spring, how is this IOC container designed? Let's take a look at the IOC container Interface Design:
Three interface systems:
1. The BeanFactory path from the BeanFactory interface to HierarchicalBeanFactory is a major BeanFactory design path.
2. interface design with the application context interface as the core
3. Take BeanFactory and ApplicationContext as the core.
1. BeanFactory application scenarios
BeanFactory provides the most basic IOC container functions. We can see the definition of these functions in BeanFactory interface.
In Spring code implementation, BeanFactory is only an interface class and does not provide the specific implementation of the container, the DefaultListableBeanFactory, XMLBeanFactory, ApplicationContext, and so on can all be seen as the specific implementation of a function as shown in the container system, that is, the specific container product in the container system. Let's take a look at how BeanFactory defines the basic interface of the IOC container.
When using the container, you can use the Escape Character "&" to get the FactoryBean itself, which is used to distinguish between the objects produced by FactoryBean obtained through the container and the FactoryBean itself. For example, if
MyJndiObject is a FactoryBean, so we use & myJndiObject to get FactoryBean, instead of the object generated by FactoryBean myJNDIObject.
The BeanFactory interface designs the getBean method, which is the main method for using the IOC container API. Through this method, you can obtain the Bean managed in the container, bean is indexed by specifying a name. If you need to check the Bean type when obtaining the Bean, The BeanFactory interface defines the getBean method with parameters. The use of this method and the getBean method type without parameters are as follows, the difference is that the Bean search type is added.
You can use the getBean in the BeanFactory interface method to use the Bean name. Therefore, if the Bean to be obtained is of the prototype type, you can also generate the corresponding parameters of the specified constructor for this prototype Bean. With the BeanFactory definition, you can perform the following operations:
Using the containsBean interface method, you can determine whether the container contains the Bean with the specified name. Using the isSingleton class interface, you can query whether the Bean with the specified name is a Singleton Bean. For the SingleTon attribute, you can specify it in BeanDefinition. The interface method isTypeMatch is used to query whether the Class type of the Bean with the specified name is a specific Class type. You can use the getType method to query the Class type of the Bean with the specified name. You can use the getAliases Class to query all the aliases of the Bean with the specified name.
2. BeanFactory container design principle
The BeanFactory interface provides specifications for using IOC containers. The implementation of XMLBeanFactory is used as an example to describe the design principle of IOC containers.
As you can see, XMLBeanFactory, which is implemented at the bottom of the simple IOC container series, only provides the most basic IOC container functions. If you want to expand your container products, we need to see if Spring has provided thread or similar container implementations for our reference.
Read the source code of XMLBeanFactory carefully. XMLBeanFactory inherits from the DefaultListableBeanFactory class. It actually contains the important functions of the basic IOC container and is also a basic product in the container series used in many places. In Spring
DefaultListableBeanFactoryAs a fully functional IOC container by default, XmlBeanFactory adds new features in its inheritance. It is a BeanFactory related to XML, that is to say, it is an IOC container that can read BeanDefinition defined in XML files.
How do these functions implement XML reading? The processing of XML file definition information is not directly completed by XMLBeanFactory. In XMLBeanFactory,
XmlBeanDefinitionReaderObject. With this Reader object, BeanDefinition defined in XML has a processing place. We can see that the XML format information is actually processed
XMLBeanDefinitionReader.
When constructing the XMLBeanFactory IOC container, You need to define the BeanDefinition information source, which needs to be encapsulated into the Resource class in Spring for presentation. Resource is a class used by Spring to encapsulate I/O operations. For example:
ClassPathResources res = new ClassPathResources("beans.xml");
In this way, the specific ClassPathResource is used to construct the required Resource, and then the Resource is passed to the XMLBeanFactory constructor as the constructor. In this way, the IOC container can easily locate the desired BeanDefinition information to initialize the Bean and inject dependencies into the container.
Let's take a look at the source code of XMLBeanFactory:
/** * Convenience extension of {@link DefaultListableBeanFactory} that reads bean definitions * from an XML document. Delegates to {@link XmlBeanDefinitionReader} underneath; effectively * equivalent to using an XmlBeanDefinitionReader with a DefaultListableBeanFactory. * *
The structure, element 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" doesn't need to be the root element of the XML * document: This class will parse all bean definition elements in the XML file. * *
This class registers each bean definition with the {@link DefaultListableBeanFactory} * superclass, and relies on the latter's implementation of the {@link BeanFactory} interface. * It supports singletons, prototypes, and references to either of these kinds of bean. * See {@code "spring-beans-3.x.xsd"} (or historically, {@code "spring-beans-2.0.dtd"}) for * details on options and configuration style. * *
For advanced needs, consider using a {@link DefaultListableBeanFactory} with * an {@link XmlBeanDefinitionReader}. The latter allows for reading from multiple XML * resources and is highly configurable in its actual XML parsing behavior. * * @author Rod Johnson * @author Juergen Hoeller * @author Chris Beams * @since 15 April 2001 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory * @see XmlBeanDefinitionReader * @deprecated as of Spring 3.1 in favor of {@link DefaultListableBeanFactory} and * {@link XmlBeanDefinitionReader} */ @Deprecated @SuppressWarnings({"serial", "all"}) public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); /** * Create a new XmlBeanFactory with the given resource, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } /** * Create a new XmlBeanFactory with the given input stream, * which must be parsable using DOM. * @param resource XML resource to load bean definitions from * @param parentBeanFactory parent bean factory * @throws BeansException in case of loading or parsing errors */ public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
In other IOC containers, such as ApplicationContext, the basic principle of implementation is the same as that of XMLBeanFactory. It also obtains basic IOC container functions by holding or extending defalistlistablebeanfactory.
By referring to the implementation of XmlBeanFactory, we use DefaultListableBeanFactory programmatically, from which we can see some basic processes used by IOC containers. For example:
ClassPathResource res = new ClassPathResource("beans.xml");DefaultListableBeanFactory factory = new DefultListableBeanFactory();XmlBeanDefinitionReader reader = new XmlBeanDeifinitionReader(factory);reader.loadBeanDefinition(res);
In this way, we can use the DefaultListableBeanFactory IOC container through the factory object. The procedure is as follows:
Creates an abstract resource of the IOC configuration file, which contains the definition information of BeanDefinition. Create a BeanFactory. Here, DefaultListableBeanFactory is used to create a reader for loading BeanDefinition. Here, XMLBeanDefinitionReader is used to load BeanDefinition in the Xml file format. A callback configuration is used to read the configuration information from the defined resource location, the specific parsing process is completed by the XMLBeanDefinitionReader class.
3. Application scenarios of ApplicationContext
In Spring, the system has provided many predefined container implementations for users, without us having to do anything. In addition to providing the basic functions of the containers described earlier, the commonly used ApplicationContext does not provide the following additional services. Therefore, ApplicationContext is an IOC container with high-level morphological significance. It adds many additional functions based on BeanFactory.
Different information sources are supported. We can see that ApplicationContext extends the MessageSource interface and supports internationalization.
Access resources. It is reflected in the support of ResourceLoader and Resource. We can get Bean-defined resources from different places.
Supports application events, inherits the ApplicationEvenPublisher interface, and introduces the event mechanism in the context. The combination of events and Bean lifecycles facilitates Bean management.
Provide additional services in ApplicationContext. Makes IOC containers more functional and framework-oriented.
4. Design Principle of ApplicationContext container
In the ApplicationContext container, we take the implementation of FileSystemXMLApplicationContext as an example.
In the design of FileSystemXMLApplicationContext, we can see that the main functions of the ApplicationContext application context have been implemented in the FileSystemXMLApplicationContext base class AbstractXMLApplicationContext. In FileSystemXMLApplicationContext, as a specific application context, you only need to implement two functions related to its own design.
One function is to directly use FileSystemXMLApplicationContext to instantiate the application context and start the refresh process of the IOC container. The source code of FileSystemXMLApplicationContext is as follows:
/** * Create a new FileSystemXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * @param configLocations array of file paths * @param refresh whether to automatically refresh the context, * loading all bean definitions and creating all singletons. * Alternatively, call refresh manually after further configuring the context. * @param parent the parent context * @throws BeansException if context creation failed * @see #refresh() */ public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
This refresh process involves complex column operations started by the IOC container. At the same time, these operations are similar for different container implementations, so they are encapsulated in the base class. Therefore, what we see in the FileSystemXml design is a simple call.
Another feature is FileSystemXMLApplicationContext, which is related to how to load Xml Bean-defined resources from the file system. Through this process, you can read BeanDefinition in XML format in the file system to prepare, because different application context implementations correspond to different ways of reading BeanDefinition. The implementation code in FileSystemXMLApplicationContext is as follows:
/** * Resolve resource paths as file system paths. *
Note: Even if a given path starts with a slash, it will get * interpreted as relative to the current VM working directory. * This is consistent with the semantics in a Servlet container. * @param path path to the resource * @return Resource handle * @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath */ @Override protected Resource getResourceByPath(String path) { if (path != null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path);
You can see that you can call this method to locate the FileSystemResource.
To be continued ......