Application context context context of Spring key interface

catalog

1.ServletContextListener interface

2.ApplicationContextInitializer interface

2.1 integration mode of springboot

2.2 integration mode of spring MVC

3.ApplicationListener interface and supporting classes

3.1 ApplicationListener interface and supporting class ApplicationEvent

3.2 ApplicationEventMulticaster interface

1.ServletContextListener interface

The interface source code is as follows:

public interface ServletContextListener extends EventListener {
    public default void contextInitialized(ServletContextEvent sce) {
    }
    public default void contextDestroyed(ServletContextEvent sce) {
    }
}

Interface Description: this interface belongs to the class in the Servlet package in the JDK source code. When the program starts the Tomcat container in the way of Web server, the contextInitialized() method will be called after initializing the ServletContext. The old-fashioned conventional Spring entry was entered here.

The contextDestroyed() method is called when the ServletContext is destroyed.

2.ApplicationContextInitializer interface

The interface source code is as follows:

public interface ApplicationContextInitializer
        <C extends ConfigurableApplicationContext> {
   void initialize(C applicationContext);
}

Interface Description: the function of the interface is to perform some custom operations on ApplicationContext before the Spring container calls the refresh() method to refresh the context, such as manually adding a custom configuration file or a custom implementation class. The interface can use generics to determine a specific ApplicationContext implementation class. Next, let's see how the interface integrates into the Spring boot and Spring MVC framework.

2.1 integration mode of springboot

There are two ways to integrate spring boot: one is in the spring boot file spring.factories The other is in application.yml File.

For example, a CustomInitializer customized ApplicationContextInitializer implementation class is defined, and the code is as follows:

@Slf4j
public class CustomInitializer implements ApplicationContextInitializer {
    @Override
    public void initialize(ConfigurableApplicationContext 
            applicationContext) {
        log.info("invoke CustomInitializer initialize method.");
    }
}

The first one: adopted in spring.factories Add ApplicationContextInitializer configuration to. First, you need to add / meta-inf in the resources directory/ spring.factories File, which is the configuration file of springboot. As long as the path is correct, it can be read and loaded by springboot. The configuration is as follows:

org.springframework.context.ApplicationContextInitializer=\
  com.iboxpay.initializer.CustomInitializer

The configuration reading of the relevant source code is as follows (it is recommended to read the springboot source code first, and show that the source code will ignore unnecessary processes):

public class SpringApplication {
    private List<ApplicationContextInitializer<?>> initializers;
    public SpringApplication(ResourceLoader resourceLoader, 
            Class<?>... primarySources) {
       ...
       setInitializers((Collection) getSpringFactoriesInstances(
               ApplicationContextInitializer.class));
       ...
    }
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
       return getSpringFactoriesInstances(type, new Class<?>[] {});
    }
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, 
            Class<?>[] parameterTypes, Object... args) {
       ClassLoader classLoader = getClassLoader();
       Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader
               .loadFactoryNames(type, classLoader));
       List<T> instances = createSpringFactoriesInstances(type, 
               parameterTypes, classLoader, args, names);
       AnnotationAwareOrderComparator.sort(instances);
       return instances;
    }
    public ConfigurableApplicationContext run(String... args) {
        ...
        prepareContext(context, environment, listeners, 
                applicationArguments, printedBanner);
        ...
    }
    private void prepareContext(ConfigurableApplicationContext context, 
            ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, 
            ApplicationArguments applicationArguments, 
            Banner printedBanner) {
        ...
        applyInitializers(context);
        ...
    }
    protected void applyInitializers(ConfigurableApplicationContext 
            context) {
       for (ApplicationContextInitializer initializer : getInitializers()){
          Class<?> requiredType = GenericTypeResolver
                  .resolveTypeArgument(initializer.getClass(),
                      ApplicationContextInitializer.class);
          Assert.isInstanceOf(requiredType, context, 
                  "Unable to call initializer.");
          initializer.initialize(context);
       }
    }
}

The general process is as follows:

  1. Starting main function call of springboot SpringApplication.run() method;
  2. Initialize the SpringApplication class, and call the getSpringFactoriesInstances method in its constructor to get the spring.factories All ApplicationContextInitializer classes configured in the file, the specific reading process is omitted;
  3. After initializing the SpringApplication class, we call the run() method, which will be called to applyInitializers() in turn.
  4. Method applyInitializers() calls the spring.factories Read the initialize method of the ApplicationContextInitializer class in the file.

The second type: adopted in application.yml File configuration:

context:
  initializer:
    classes: com.iboxpay.initializer.CustomInitializer

The principle of this configuration method and spring.factoreis It's almost the same to configure the ApplicationContextInitializer class in the file. The DelegatingApplicationContextInitializer class configured in the file will be read earlier. This class will call applyinitializer() and then the initialization() method of DelegatingApplicationContextInitializer class in the process of this method. The Configured on application.yml initializer class in configuration. Its source code is as follows:

public class DelegatingApplicationContextInitializer
      implements ApplicationContextInitializer
              <ConfigurableApplicationContext>, Ordered {
    private static final String PROPERTY_NAME = 
            "context.initializer.classes";
    @Override
    public void initialize(ConfigurableApplicationContext context) {
       ConfigurableEnvironment environment = context.getEnvironment();
       List<Class<?>> initializerClasses = 
               getInitializerClasses(environment);
       if (!initializerClasses.isEmpty()) {
          applyInitializerClasses(context, initializerClasses);
       }
    }
    private List<Class<?>> getInitializerClasses(
            ConfigurableEnvironment env) {
       String classNames = env.getProperty(PROPERTY_NAME);
       List<Class<?>> classes = new ArrayList<>();
       if (StringUtils.hasLength(classNames)) {
          for (String className : 
                  StringUtils.tokenizeToStringArray(classNames, ",")) {
             classes.add(getInitializerClass(className));
          }
       }
       return classes;
    }
}

You can see that the method flow is very simple. Get the environment object directly in the initialize method, then pass the object into the getInitializerClasses method, and read the context.initializer.classes Property, which is where we application.yml The custom initializer class, CustomInitializer, configured in the. After reading the custom class, you can call it in the class to complete the embedding integration of the custom initializer class.

2.2 integration mode of spring MVC

Spring MVC is dependent on web.xml It is started by the configuration of the file, so if you want to configure the initializer class into the program context before the ApplicationContext is refreshed, you can only guess through web.xml File configuration. Now the instance Demo class is still the CustomInitializer class from the previous section.

In fact, spring MVC does web.xml There are two ways to implement configuration through files: one is to configure the initializer class as the globalInitializerClasses property, and the other is to configure the initialization parameter contextInitializerClasses in the core class DispatcherServlet of spring MVC. Next, let's talk about these two configuration methods (if you have seen the spring MVC startup source code):

First, use the globalInitializerClasses property to configure the web.xml The configuration in the file is as follows:

<context-param>
    <param-name>globalInitializerClasses</param-name>
    <param-value>com.iboxpay.initializer.CustomInitializer</param-value>
</context-param>

The loading source code at startup is as follows:

public abstract class FrameworkServlet 
        extends HttpServletBean implements ApplicationContextAware {
    private String contextInitializerClasses;
    public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = 
            "globalInitializerClasses";
    public void setContextInitializerClasses(String 
            contextInitializerClasses) {
       this.contextInitializerClasses = contextInitializerClasses;
    }
    @Override
    protected final void initServletBean() throws ServletException {
        ...
        this.webApplicationContext = initWebApplicationContext();
        ...
    }
    protected WebApplicationContext initWebApplicationContext() {
        ...
        if (wac == null) {
           wac = createWebApplicationContext(rootContext);
        }
        ...
    }
    protected WebApplicationContext createWebApplicationContext(
            WebApplicationContext parent) {
       return createWebApplicationContext((ApplicationContext) parent);
    }
    protected WebApplicationContext createWebApplicationContext(
            ApplicationContext parent) {
        ...
        configureAndRefreshWebApplicationContext(wac);
        ...
    }
    protected void configureAndRefreshWebApplicationContext(
            ConfigurableWebApplicationContext wac) {
        ...
        applyInitializers(wac);
        wac.refresh();
    }
    protected void applyInitializers(ConfigurableApplicationContext wac) {
       // obtain web.xml globalInitializerClasses configuration in file
       String globalClassNames = getServletContext()
               .getInitParameter(ContextLoader
                   .GLOBAL_INITIALIZER_CLASSES_PARAM);
       if (globalClassNames != null) {
          for (String className : 
                  StringUtils.tokenizeToStringArray(globalClassNames, 
                          INIT_PARAM_DELIMITERS)) {
             this.contextInitializers.add(loadInitializer(className, wac));
          }
       }
       // This is a member variable, which means that the variable is set during initialization
       if (this.contextInitializerClasses != null) {
          for (String className : 
                  StringUtils.tokenizeToStringArray(this
                          .contextInitializerClasses, 
                              INIT_PARAM_DELIMITERS)) {
             this.contextInitializers.add(loadInitializer(className, wac));
          }
       }
       AnnotationAwareOrderComparator.sort(this.contextInitializers);
       for (ApplicationContextInitializer<ConfigurableApplicationContext> 
               initializer : this.contextInitializers) {
          initializer.initialize(wac);
       }
    }
}

How to enter the initServletBean of the FrameworkServlet class is not explained too much. It can be seen that the familiar method name, applyInitializers(), is the final method to call the initializer class according to its general call chain. We all know that web.xml All the configuration in the file will be saved in the ServletContext, so you can get it by directly calling getServletContext().getInitParameter(), and then add the obtained class configuration instantiation to the contextInitializers member variable.

The second is configured in the initial variable of DispatcherServlet:

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring-mvc.xml</param-value>
    </init-param>
    <!-- Configure initialization variables, which will be called set method set go in -->
    <init-param>
        <param-name>contextInitializerClasses</param-name>
        <param-value>com.iboxpay.initializer.CustomInitializer</param-value>
    </init-param>
</servlet>

The second method call chain is the same as the other one. Only when initializing dispatcherservet, the contextInitializerClasses configuration call setContextInitializerClasses method will be set in, and its member variable contextInitializerClasses will be assigned directly, which is the same as the assignment of member variable contextConfigLocation. If there are multiple supported ',',; \t\n "these symbols are separated. Then, the String path is instantiated into a specific class, added to the contextInitializers variable, and finally the initialization () method is called one by one to complete the initialization class integration.

3.ApplicationListener interface and supporting classes

3.1 ApplicationListener interface and supporting class ApplicationEvent

The combined source code is as follows:

public interface ApplicationListener<E extends ApplicationEvent> 
        extends EventListener {
   void onApplicationEvent(E event);
}
public abstract class ApplicationEvent extends EventObject {
   private static final long serialVersionUID = 7099057708183571937L;
   private final long timestamp;
   public ApplicationEvent(Object source) {
      super(source);
      this.timestamp = System.currentTimeMillis();
   }
   public final long getTimestamp() {
      return this.timestamp;
   }
}

Interface Description: this combination is the event driven monitoring function provided by Spring container for developers. It should be familiar to see the Spring boot source code. In Spring boot application.yml The reading and parsing of files is done by using event listening.

The key code of the example is as follows:

public class ConfigFileApplicationListener 
        implements EnvironmentPostProcessor, SmartApplicationListener, 
        Ordered {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
       if (event instanceof ApplicationEnvironmentPreparedEvent) {
          onApplicationEnvironmentPreparedEvent(
                  (ApplicationEnvironmentPreparedEvent) event);
       }
       if (event instanceof ApplicationPreparedEvent) {
          onApplicationPreparedEvent(event);
       }
    }
}
public class ApplicationEnvironmentPreparedEvent 
        extends SpringApplicationEvent {
   private final ConfigurableEnvironment environment;
   public ApplicationEnvironmentPreparedEvent(SpringApplication application,
           String[] args, ConfigurableEnvironment environment) {
      super(application, args);
      this.environment = environment;
   }
   public ConfigurableEnvironment getEnvironment() {
      return this.environment;
   }
}

Example description: ConfigFileApplicationListener and ApplicationEnvironmentPreparedEvent are a set of listening driver Event function combinations. Declare a ConfigFileApplicationListener listener, and then pass in the corresponding Event declaration to perform corresponding operations according to the Event. The onApplicationEnvironmentPreparedEvent() method is read parsing application.yml File method.

3.2 ApplicationEventMulticaster interface

Of course, this is just a listener. If you need a group of listener groups to complete a function, you can also use another Spring interface: ApplicationEventMulticaster.

The source code of the ApplicationEventMulticaster interface is as follows:

public interface ApplicationEventMulticaster {
   void addApplicationListener(ApplicationListener<?> listener);
   void addApplicationListenerBean(String listenerBeanName);
   void removeApplicationListener(ApplicationListener<?> listener);
   void removeApplicationListenerBean(String listenerBeanName);
   void removeAllListeners();
   void multicastEvent(ApplicationEvent event);
   void multicastEvent(ApplicationEvent event, 
           @Nullable ResolvableType eventType);
}

Interface Description: this interface provides three functions: add listener, delete listener and call listener for incoming listening events. This is equivalent to that all listeners can be put into this implementation class. When it is necessary to perform the corresponding operation according to an event, only the incoming event is needed. The developer can customize in the implementation class of listener group to coordinate the collocation of each listener according to the event to complete a specific function.

For example, you can refer to the spring boot startup and operation listener eventpublishingrunnistener. The class information and method source code are as follows:

public class EventPublishingRunListener 
        implements SpringApplicationRunListener, Ordered {
    private final SimpleApplicationEventMulticaster initialMulticaster;
    @Override
    public void starting() {
       this.initialMulticaster.multicastEvent(
               new ApplicationStartingEvent(this.application, this.args));
    }
    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
       this.initialMulticaster
             .multicastEvent(
                 new ApplicationEnvironmentPreparedEvent(this.application, 
                     this.args, environment));
    }
    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
       this.initialMulticaster
             .multicastEvent(new ApplicationContextInitializedEvent(
                 this.application, this.args, context));
    }
    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
       ...
       this.initialMulticaster.multicastEvent(
               new ApplicationPreparedEvent(this.application, this.args, 
                       context));
    }
    @Override
    public void failed(ConfigurableApplicationContext context, 
            Throwable exception) {
       ApplicationFailedEvent event = new ApplicationFailedEvent(
               this.application, this.args, context, exception);
       if (context != null && context.isActive()) {
          context.publishEvent(event);
       }
       else {
          ...
          this.initialMulticaster.multicastEvent(event);
       }
    }
}

Example description: when calling several different methods of eventpublishingronlistener class, you can directly declare an event in the method and pass it to SimpleApplicationEventMulticaster listener group. You don't need to care which listener should listen to and handle the event. The specific processing logic is encapsulated in multicasevent of initialMulticaster, Increased the convenience of use.

 

Tags: Spring xml SpringBoot JDK

Posted on Mon, 22 Jun 2020 03:30:06 -0400 by d_mc_a