New understanding of Spring IOC

Analysis of spring IOC

Re IOC and DI

  • IOC(Inversion of Control) inversion of control: the so-called inversion of control is to reverse the code that we need to create and depend on the objects in our code to the container for help. Therefore, we need to create a container and a description to let the container know the relationship between the objects to be created. The most specific expression of this description is the configuration file we see.

  • DI(Dependency Injection) dependency injection: it means that an object passively accepts a dependency class rather than looking for it on its own initiative. In other words, it means that an object does not look up the class it depends on from the container, but actively injects the class it depends on when the container instantiates the object.

The above is the popular understanding of ioc and DI. We can also use our existing knowledge to think about the realization of these two points. In fact, both of them mainly rely on and reflect mechanism to realize these functions. So in order to raise some key issues, we will follow the key issues to see the specific process.

  • How to express the relationship between objects in spring
    In our configuration file or javaconfig, we can also withdraw the cash in the corresponding way

  • Where is the file or information describing the relationship between objects
    It may exist in classpat, fileSystem, url or context,

  • For different storage locations and file formats, the descriptions are different. How to achieve unified parsing and declaration
    We can think of transforming these external information according to the model and maintaining a unified model object internally

  • How to analyze these information differently
    According to their own characteristics, the corresponding policies are specified for analysis

Core class of IOC container

1. BeanFactory

BeanFactory is the top-level container interface in spring. We can see that the essence of the container in spring is the factory. It has many implementation classes. Here we show the main core class diagram:
Make a simple explanation for the above figure:

  • BeanFactory is the top-level container interface, which consists of three sub interfaces: hierarchalbeanfactory, AutowireCapableBeanFactory and ListableBeanFactory

  • In the inheritance relationship, we can see that interfaces and abstract classes are the main, multi-level encapsulation, and the final implementation classes such as DefaultListableBeanFactory, as well as AbstractApplicationContext In spring, these interfaces have their own specific use scenarios. In each scenario, the creation of different objects is transferred to the transformation process with corresponding control restrictions, strong domain division, single responsibility and strong scalability

public interface BeanFactory {
	/**
	 * It is mainly brave to distinguish beanFactory from factorybean, which is the factory or container that generates objects inside spring,
     * When we get the object through getBean, we get the proxy object of the real object. If we want to get the proxy object of the generated object
     * Factory needs to add this prefix
	 */
	String FACTORY_BEAN_PREFIX = "&";
	/**
	 * Return the real column of an instance through beanName
	 */
	Object getBean(String name) throws BeansException;
	/**
	 * Obtain the objects in the container through the BeanName and class types, and limit the verification at multiple levels
	 */
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
    /**
	 * Specifying the parameter list of the corresponding constructor or factory method through BeanName
	 */
	Object getBean(String name, Object... args) throws BeansException;
	<T> T getBean(Class<T> requiredType) throws BeansException;
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
    
    /*
     * Verify if it exists in IOC
     */
	boolean containsBean(String name);
    /*
     * The verification is single instance or prototype mode
     */
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    /* 
     * Determine whether the type of bean s in IOC is the type of tytomatch
     */
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    //Gets the type of the specified bean
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	@Nullable
	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
    // Get the alias of the bean
	String[] getAliases(String name);
}
  • BeanFactory defines the behavior of containers in spring, but it will not implement these. The top-level interface makes highly abstract processing. The creation and operation of concrete containers are all handed over to subclasses for implementation. Therefore, we need to know how IOC works from the implementation subclasses of ico in spring, such as when we read the xml configuration mode ClasspathXmlApplicationContext or annotationconfigpplicationcontext used in annotations. In these specific implementation classes, there is a specific process of container initialization

  • In the above class diagram, the ApplicationContext class is a very important interface, which is a high-level interface provided by spring, and also the container we will contact most in the future

2. BeanDefinition

beandefinition is a series of definition models for object relationship and object creation in spring. Its essence is actually a Map collection. Its class diagram can be seen as follows:

3. BeanDefinitionReader

When we create the initialization container, that is, the bean factory, we will create the corresponding bean definitionreader object according to the factory. The reader object is a resource parser, and the parsing process is complex. In the later parsing, we will see the specific implementation

4. ResourceLoader

The package org.springframework.core.io.ResourceLoader is the top-level interface used by spring to load unified resources. It defines behaviors and implements specific subclasses. We can see the class diagram

The core implementation of ResourceLoader is shown in the class diagram, and the container also implements this interface in spring. It will be specially explained in the later operation of uniform resource loading

5. Resource

It belongs to org.springframework.core.io.Resource, which is the top-level interface of resource loading policy implementation in spring. Each implementation class of this class is an access policy to a resource. Class diagram:

Preliminary understanding of Web IOC container

We are very familiar with a core controller DispatcherServlet in springMvc. This class does a centralized distribution and web container initial function. First, let's take a look at the class diagram

  • We can see that dispatcher Servlet inherits HttpServlet, and we are familiar with that HttpServlet belongs to Servlet, so it must have an init() initialization method. We can see that init method is rewritten in HttpServletBean
/**
	 * Override init method to initialize ServletContext
	 * Map config parameters onto bean properties of this servlet, and
	 * invoke subclass initialization.
	 * @throws ServletException if bean properties are invalid (or required
	 * properties are missing), or if subclass initialization fails.
	 */
	@Override
	public final void init() throws ServletException {
		// Set bean properties from init parameters.
		//Read initialization parameters such as init param in web.xml
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}
		// Let subclasses do whatever initialization they like.
		//Initialization container is implemented by subclass FrameworkServlet
		initServletBean();
	}
  • We can see that the specific container instance method FrameworkServlet in init method is implemented. Let's take a look at the specific implementation of initialServletBean
/**
	 * Overridden method of {@link HttpServletBean}, invoked after any bean properties
	 * have been set. Creates this servlet's WebApplicationContext.
	 * Building a web context container
	 */
	@Override
	protected final void initServletBean() throws ServletException {
		getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
		if (logger.isInfoEnabled()) {
			logger.info("Initializing Servlet '" + getServletName() + "'");
		}
		long startTime = System.currentTimeMillis();
		try {
			//Container initialization
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
		catch (ServletException | RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			throw ex;
		}
		if (logger.isDebugEnabled()) {
			String value = this.enableLoggingRequestDetails ?
					"shown which may lead to unsafe logging of potentially sensitive data" :
					"masked to prevent unsafe logging of potentially sensitive data";
			logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
					"': request parameters and headers will be " + value);
		}
		if (logger.isInfoEnabled()) {
			logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
		}
	}
  • We can see that initWebApplicationContext() is exactly the method of container initialization. We will continue to follow up. Now we are looking at container initialization. Others will be left for a while. Later we will talk about spring MVC in the system
/**
	 *  Initialize web container WebApplicationContext
	 * Initialize and publish the WebApplicationContext for this servlet.
	 * <p>Delegates to {@link #createWebApplicationContext} for actual creation
	 * of the context. Can be overridden in subclasses.
	 * @return the WebApplicationContext instance
	 * @see #FrameworkServlet(WebApplicationContext)
	 * @see #setContextClass
	 * @see #setContextConfigLocation
	 */
	protected WebApplicationContext initWebApplicationContext() {
		//Get the parent container WebApplicationContext from the ServletContext root container
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		//Declare child containers
		WebApplicationContext wac = null;
		//Build the relationship between parent and child containers. Here, judge whether the current container exists,
		// Set the parent container rootcontext as a child container if it exists
		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent -> set
						// the root application context (if any; may be null) as the parent
						cwac.setParent(rootContext);
					}
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		//Determine whether there is a reference to the sub container, search in the ServletContext root container, and assign the value if found
		if (wac == null) {
			// No context instance was injected at construction time -> see if one
			// has been registered in the servlet context. If one exists, it is assumed
			// that the parent context (if any) has already been set and that the
			// user has performed any initialization such as setting the context id
			wac = findWebApplicationContext();
		}
		//If the above search is not found, the container is assigned here to build a container, but the container is not initialized, just a reference
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one
			wac = createWebApplicationContext(rootContext);
		}
		//Here, the onRefresh method is triggered for true container initialization
		if (!this.refreshEventReceived) {
			// Either the context is not a ConfigurableApplicationContext with refresh
			// support or the context injected at construction time had already been
			// refreshed -> trigger initial onRefresh manually here.
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}
		return wac;
	}
  • We see the method onRefresh() of the real initialization container, follow up to find the implementation class in the dispatcher servlet, and call the initStrategies() method to continue to enter
/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}
	/**
	 * Initialize the container to initialize the 9 major components of spring MVC
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		//Multi file upload component
		initMultipartResolver(context);
		//Internationalized components, i.e. local language environment
		initLocaleResolver(context);
		//Initialize theme template processor
		initThemeResolver(context);
		//Initialize HandMapping mapping
		initHandlerMappings(context);
		//Initializing the HandlerAdapters parameter adapter
		initHandlerAdapters(context);
		//Initialize a interception component
		initHandlerExceptionResolvers(context);
		//Initialize the view preprocessing parser,
		initRequestToViewNameTranslator(context);
		//Initialize view resolver
		initViewResolvers(context);
		//Initialize FlashMap
		initFlashMapManager(context);
	}

IOC container initialization

There are many ways to initialize the IOC container, such as configuration file or Javaconfig. For example, ClassPathXmlApplicationContext

  • The main processes in IOC can be summarized as three basic processes: localization, loading and registration. Our common containers are ApplicationContext, and resourceloader is the base class of all resource loading. We can find that all IOC containers inherit BeanFactory, which also shows that all containers are essentially a bean factory
  • We can get the container through the following code
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD);

In this case, how to build the loading container in the container creation? Let's take a look at it

//Constructor called
	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

Here is a constructor called, which is the true execution process. We find the internal execution force refresh() method

public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		//Call the constructor of the parent class to set the resource loading
		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
  • You can see for yourself that classes like annotationconfiguapplicationcontext, FileSystemXmlApplicationContext, and XmlWebApplicationContext all call the refresh() method, which is implemented by their parent class AbstractApplicationContext. Here, the decorator pattern policy pattern is applied

This article is published by AnonyStar and can be reproduced, but the original source shall be declared.
Admire "the art of elegant coding", firmly believe that practice makes perfect, and strive to change life
Welcome to wechat public account: coder for more quality articles
More articles on my blog: IT short code

Tags: Java Spring xml Attribute

Posted on Tue, 05 May 2020 14:07:49 -0700 by shadow1200