[spring 41 / 43] - Analysis of interface architecture related to ApplicationContext

Quote the original text:
https://www.cmsblogs.com/article/1391385051513622528
[dieke Spring 41/43] - Analysis of interface architecture related to ApplicationContext

text

In the previous 30 blogs, the analysis is based on the BeanFactory container. The BeanFactory container is a little simple. It is not suitable for our production environment. In the production environment, we usually choose ApplicationContext. Compared with most people, it is the regular army. Compared with the miscellaneous army of BeanFactory, it has the following differences:

  • Inherits MessageSource and provides international standard access policies.
  • Inherits ApplicationEventPublisher and provides a powerful event mechanism.
  • The extended ResourceLoader can be used to load multiple resources and flexibly access different resources.
  • Support for Web applications.

ApplicationContext

The following figure is the class diagram of ApplicationContext structure

  • BeanFactory: Spring manages the top-level interface of beans. We can think of it as a simple version of spring container. ApplicationContext inherits two subclasses of BeanFactory: hierarchical BeanFactory and ListableBeanFactory. Hierarchical BeanFactory is a BeanFactory with hierarchical relationship and has the attribute parentBeanFactory. ListableBeanFactory implements the enumeration method, which can list all bean objects in the current BeanFactory without obtaining them one by one according to name.
  • ApplicationEventPublisher: an interface used to encapsulate the event publishing function and send event messages to the event Listener.
  • ResourceLoader: the top-level interface for Spring to load resources, which is used to load Resource files from a source. ApplicationContext inherits the subclass ResourcePatternResolver of ResourceLoader, which is a policy interface that resolves location into a Resource object.
  • MessageSource: the policy interface for parsing message, which does not support internationalization and other functions.
  • EnvironmentCapable: the interface used to get the Environment.

Sub interface of ApplicationContext

ApplicationContext has two direct subclasses: WebApplicationContext and ConfigurableApplicationContext.

WebApplicationContext

    public interface WebApplicationContext extends ApplicationContext {
        ServletContext getServletContext();
    }

The interface has only one getServletContext(), which is used to provide context information to the servlet.

ConfigurableApplicationContext

    public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
     // Set unique ID for ApplicationContext
     void setId(String id);
    
     // Set parent for ApplicationContext
     // The parent class should not be modified: if the created object is not available, it should be set outside the constructor
     void setParent(@Nullable ApplicationContext parent);
    
     // Setting Environment
     void setEnvironment(ConfigurableEnvironment environment);
    
     // Get Environment
     @Override
     ConfigurableEnvironment getEnvironment();
    
     // Add beanfactoryprocessor
     void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
    
     // Add ApplicationListener
     void addApplicationListener(ApplicationListener<?> listener);
    
     // Add ProtocolResolver
     void addProtocolResolver(ProtocolResolver resolver);
    
     // Load or refresh configuration
     // This is a very important method
     void refresh() throws BeansException, IllegalStateException;
    
     // Register shutdown hook
     void registerShutdownHook();
    
     // Close ApplicationContext
     @Override
     void close();
    
     // Is ApplicationContext active
     boolean isActive();
    
     // Gets the BeanFactory of the current context
     ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
    }

From the above code, we can see that the methods provided by the ConfigurableApplicationContext interface configure the ApplicationContext, such as setEnvironment(), addbeanfactoryprocessor, and it also inherits the following two interfaces:

  • Lifecycle: management of context lifecycle. It provides start() and stop() methods to start and pause components.
  • Closeable: an interface provided by the standard JDK, which is used to finally close components and release resources.

The WebApplicationContext interface and the ConfigurableApplicationContext interface have a common subclass interface ConfigurableWebApplicationContext, which combines the two interfaces and provides a configurable, manageable and closable WebApplicationContext. At the same time, the interface also adds setServletContext(), setServletConfig() and other methods, Used to assemble WebApplicationContext.

    public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {
     void setServletContext(@Nullable ServletContext servletContext);
    
     void setServletConfig(@Nullable ServletConfig servletConfig);
    
     ServletConfig getServletConfig();
    
     void setNamespace(@Nullable String namespace);
    
     String getNamespace();
    
     void setConfigLocation(String configLocation);
    
     void setConfigLocations(String... configLocations);
    
     String[] getConfigLocations();
    }

The above three interfaces can form a relatively complete Spring container. The whole Spring container system involves many interfaces, So let's make up a specific implementation class to see the implementation of ApplicationContext (in fact, in the previous series of articles, Xiaobian has analyzed the principles of most of the interfaces involved). Of course, it is impossible to involve every method, but Xiaobian will post the most important implementation methods for analysis. There are many implementation classes of ApplicationContext, so we use ClassPathXmlApplicationContext to analyze ApplicationContext.

ClassPathXmlApplicationContext

ClassPathXmlApplicationContext is a class we use a lot in learning Spring. It is the first Spring container that many people contact, including Xiaobian. I think many people still remember the following code.

    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    StudentService studentService = (StudentService)ac.getBean("studentService");

The following figure shows the structure of ClassPathXmlApplicationContext:
The main class hierarchy relationships are as follows:

    org.springframework.context.support.AbstractApplicationContext
          org.springframework.context.support.AbstractRefreshableApplicationContext
                org.springframework.context.support.AbstractRefreshableConfigApplicationContext
                      org.springframework.context.support.AbstractXmlApplicationContext
                            org.springframework.context.support.ClassPathXmlApplicationContext

This design is a typical application of the template method pattern. AbstractApplicationContext implements the ConfigurableApplicationContext interface, and its subclass abstractrefreshableconfigurationapplicationcontext implements the BeanNameAware and InitializingBean interfaces. Therefore, the top-level interfaces designed by ClassPathXmlApplicationContext include:

    BeanFactory: Spring container Bean Management of
    MessageSource: Administration message ,Realize internationalization and other functions
    ApplicationEventPublisher: Event release
    ResourcePatternResolver: Resource loading
    EnvironmentCapable: system Environment(profile + Properties) relevant
    Lifecycle: Management lifecycle
    Closeable: Close and release resources
    InitializingBean: Custom initialization
    BeanNameAware: set up beanName of Aware Interface

Let's analyze these interfaces one by one.

MessageSource

MessageSource defines the policy method getMessage() for obtaining messages. In the ApplicationContext system, this method implements AbstractApplicationContext. In AbstractApplicationContext, it holds a MessageSource instance and implements the implementation of getMessage() to this instance, as follows:

     private MessageSource messageSource;
    
     // Implement getMessage()
     public String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
        // Delegate to messageSource implementation
      return getMessageSource().getMessage(code, args, defaultMessage, locale);
     }
    
     private MessageSource getMessageSource() throws IllegalStateException {
      if (this.messageSource == null) {
       throw new IllegalStateException("MessageSource not initialized - " +
         "call 'refresh' before accessing messages via the context: " + this);
      }
      return this.messageSource;
     }

The real implementation is in AbstractMessageSource, as follows:

     public final String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {
      String msg = getMessageInternal(code, args, locale);
      if (msg != null) {
       return msg;
      }
      if (defaultMessage == null) {
       return getDefaultMessage(code);
      }
      return renderDefaultMessage(defaultMessage, args, locale);
     }

The specific implementation will not be analyzed here. Interested partners can conduct in-depth research by themselves.

ApplicationEventPublisher

The interface used to encapsulate the event publishing function and send event messages to the event Listener.

This interface provides a publishEvent() to notify all listeners registered in this application. This method is implemented in AbstractApplicationContext.

     @Override
     public void publishEvent(Object event) {
      publishEvent(event, null);
     }
    
     protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
      Assert.notNull(event, "Event must not be null");
      if (logger.isTraceEnabled()) {
       logger.trace("Publishing event in " + getDisplayName() + ": " + event);
      }
    
      ApplicationEvent applicationEvent;
      if (event instanceof ApplicationEvent) {
       applicationEvent = (ApplicationEvent) event;
      }
      else {
       applicationEvent = new PayloadApplicationEvent<>(this, event);
       if (eventType == null) {
        eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
       }
      }
    
      if (this.earlyApplicationEvents != null) {
       this.earlyApplicationEvents.add(applicationEvent);
      }
      else {
       getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
      }
    
      if (this.parent != null) {
       if (this.parent instanceof AbstractApplicationContext) {
        ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
       }
       else {
        this.parent.publishEvent(event);
       }
      }
     }

If the specified event is not an ApplicationEvent, it is wrapped in PayloadApplicationEvent. If there is a parent ApplicationContext, you should also publish the event to the parent ApplicationContext.

ResourcePatternResolver

The ResourcePatternResolver interface inherits the ResourceLoader interface and is a policy interface that resolves location to a Resource object. The getResources() provided by him is implemented in AbstractApplicationContext, where he holds an instance object of ResourcePatternResolver.

As follows:

     public Resource[] getResources(String locationPattern) throws IOException {
      return this.resourcePatternResolver.getResources(locationPattern);
     }

If you are familiar with Spring's ResourceLoader, you will find that it is ultimately implemented in pathmatching ResourcePatternResolver, which is the implementer of the ResourcePatternResolver interface.

EnvironmentCapable

Provides the Environment component of the current system Environment. A getEnvironment() is provided to return the Environment instance object, which is implemented in AbstractApplicationContext.

     public ConfigurableEnvironment getEnvironment() {
      if (this.environment == null) {
       this.environment = createEnvironment();
      }
      return this.environment;
     }

If the held environment instance object is empty, createEnvironment() is called to create one.

     protected ConfigurableEnvironment createEnvironment() {
      return new StandardEnvironment();
     }

Standard Environment is an Environment suitable for non WEB applications.

Lifecycle

An interface for managing the claim cycle.

In AbstractApplicationContext, there is an instance object of lifecycleProcessor type, lifecycleProcessor. The implementation of Lifecycle interface in AbstractApplicationContext is delegated to the implementation of lifecycleProcessor. As follows:

     @Override
     public void start() {
      getLifecycleProcessor().start();
      publishEvent(new ContextStartedEvent(this));
     }
    
     @Override
     public void stop() {
      getLifecycleProcessor().stop();
      publishEvent(new ContextStoppedEvent(this));
     }
    
     @Override
     public boolean isRunning() {
      return (this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning());
     }

When starting and stopping, the ContextStartedEvent and ContextStoppedEvent events will be published respectively.

Closeable

The Closeable interface is used to close and release resources. It provides close() to release the resources held by the object. In the ApplicationContext system, it is implemented by AbstractApplicationContext, which is used to close the ApplicationContext and destroy all bean s. In addition, if a JVM shutdown hook is registered, it should also be removed. As follows:

     public void close() {
      synchronized (this.startupShutdownMonitor) {
       doClose();
       // If we registered a JVM shutdown hook, we don't need it anymore now:
       // We've already explicitly closed the context.
       if (this.shutdownHook != null) {
        try {
         Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
        }
        catch (IllegalStateException ex) {
         // ignore - VM is already shutting down
        }
       }
      }
     }

Call doClose() to publish the contextclosedevevent event, destroy all beans (singletons), and close the BeanFactory as follows:

         protected void doClose() {
             // Omit some codes
           try {
            // Publish shutdown event.
            publishEvent(new ContextClosedEvent(this));
           }
           catch (Throwable ex) {
            logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
           }
        
                 // Omit some codes
           destroyBeans();
           closeBeanFactory();
           onClose();
        
           this.active.set(false);
          }
         }
        

InitializingBean

InitializingBean provides a way for beans to initialize methods, and the afterpropertieset () provided by it is used to perform initialization actions. In the ApplicationContext system, this method is implemented by AbstractRefreshableConfigApplicationContext, as follows:

     public void afterPropertiesSet() {
      if (!isActive()) {
       refresh();
      }
     }

Execute refresh(), which is executed in AbstractApplicationContext and executes the initialization process of the whole Spring container. This method will be analyzed and described in detail in the next article.

BeanNameAware

Set the interface of bean name. Interface is implemented in AbstractRefreshableConfigApplicationContext.

     public void setBeanName(String name) {
      if (!this.setIdCalled) {
       super.setId(name);
       setDisplayName("ApplicationContext '" + name + "'");
      }
     }

Because of the length problem and most of the interfaces have been described in detail in the previous articles, this paper mainly focuses on the ApplicationContext of the Spring Framework and briefly describes the implementation of its structure and function. I have to say here that Spring is really an excellent framework with good structural design and interface abstraction. Each interface has a single function and is highly abstract from specific functions to each module, And almost every set of interfaces provides a default implementation (defaultXXX). For the ApplicationContext system, it inherits many core interfaces in Spring and can provide a relatively complete Spring container for the client. The interface ConfigurableApplicationContext extends the ApplicationContext interface again and provides the life cycle management function. The abstract class ApplicationContext provides a large support for the whole set of interfaces The default implementation of the part encapsulates the "not easy to change" part, delegates the "easy to change" function to other classes through "combination", and uses the template method mode to open the implementation of some methods to be implemented by subclasses, so as to realize the design principle of "opening to extension and closing to modification".

Finally, let's enjoy the style of the following figure:

Keywords: Java Spring architecture

Added by tomdelonge on Sun, 19 Dec 2021 19:31:34 +0200