Spring Web - 与 ServletContainer 初始化相关的 Initializer

相关Initializer的类图如下:
在这里插入图片描述

1. javax.servlet.ServletContainerInitializer (interface)

ServletContainerInitializers (SCIs) are registered via an entry in the file META-INF/services/javax.servlet.ServletContainerInitializer that must be included in the JAR file that contains the SCI implementation.

需要注册SCI的Jar包必须包含META-INF/services/javax.servlet.ServletContainerInitializer文件,所有的SCIs通过该文件进行注册。

该方式只在war包部署方式下有效。
在这里插入图片描述

SCI processing is performed regardless of the setting of metadata-complete. SCI processing can be controlled per JAR file via fragment ordering. If an absolute ordering is defined, the only those JARs included in the ordering will be processed for SCIs. To disable SCI processing completely, an empty absolute ordering may be defined.

SCIs register an interest in annotations (class, method or field) and/or types via the javax.servlet.annotation.HandlesTypes annotation which is added to the class.

在这里插入图片描述

2. org.springframework.web.SpringServletContainerInitializer (class)

org.springframework.web.SpringServletContainerInitializer is an implementation of javax.servlet.ServletContainerInitializer.

Servlet 3.0 ServletContainerInitializer designed to support code-based configuration of the servlet container using Spring’s WebApplicationInitializer SPI as opposed to (or possibly in combination with) the traditional web.xml-based approach.

与传统基于web.xml的配置方式不同,Servlet3.0设计了ServletContainerInitializer,ServletContainerInitializer支持通过Spring的WebApplicationInitializer SPI编写基于代码的Servlet容器配置。

Mechanism of Operation
This class will be loaded and instantiated and have its onStartup method invoked by any Servlet 3.0-compliant container during container startup assuming that the spring-web module JAR is present on the classpath. This occurs through the JAR Services API ServiceLoader.load(Class) method detecting the spring-web module’s META-INF/services/javax.servlet.ServletContainerInitializer service provider configuration file. See the JAR Services API documentation as well as section 8.2.4 of the Servlet 3.0 Final Draft specification for complete details.

3. org.springframework.web.WebApplicationInitializer (interface)

Interface to be implemented in Servlet 3.0+ environments in order to configure the ServletContext programmatically – as opposed to (or possibly in conjunction with) the traditional web.xml-based approach.
Implementations of this SPI will be detected automatically by SpringServletContainerInitializer, which itself is bootstrapped automatically by any Servlet 3.0 container. See its Javadoc for details on this bootstrapping mechanism.

与传统的基于web.xml的配置方式不同,在Servlet3.0+的环境中,可以通过代码的方式配置ServletContext,而你需要做的就是实现WebApplicationInitializer接口。

以下是几种配置ServletContext的方式:

1. The traditional, XML-based approach
Most Spring users building a web application will need to register Spring’s DispatcherServlet. For reference, in WEB-INF/web.xml, this would typically be done as follows:

<servlet>
  <servlet-name>dispatcher</servlet-name>
  <servlet-class>
    org.springframework.web.servlet.DispatcherServlet
  </servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
  <url-pattern>/</url-pattern>
</servlet-mapping>

2. The code-based approach with WebApplicationInitializer
Here is the equivalent DispatcherServlet registration logic, WebApplicationInitializer-style:

public class MyWebAppInitializer implements WebApplicationInitializer {
     @Override
     public void onStartup(ServletContext container) {
       XmlWebApplicationContext appContext = new XmlWebApplicationContext();
       appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
 
       ServletRegistration.Dynamic dispatcher =
         container.addServlet("dispatcher", new DispatcherServlet(appContext));
       dispatcher.setLoadOnStartup(1);
       dispatcher.addMapping("/");
     }
 }

3. A 100% code-based approach to configuration

public class MyWebAppInitializer implements WebApplicationInitializer {

   @Override
   public void onStartup(ServletContext container) {
     // Create the 'root' Spring application context
     AnnotationConfigWebApplicationContext rootContext =
       new AnnotationConfigWebApplicationContext();
     rootContext.register(AppConfig.class);

     // Manage the lifecycle of the root application context
     container.addListener(new ContextLoaderListener(rootContext));

     // Create the dispatcher servlet's Spring application context
     AnnotationConfigWebApplicationContext dispatcherContext =
       new AnnotationConfigWebApplicationContext();
     dispatcherContext.register(DispatcherConfig.class);

     // Register and map the dispatcher servlet
     ServletRegistration.Dynamic dispatcher =
       container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
     dispatcher.setLoadOnStartup(1);
     dispatcher.addMapping("/");
   }
}

4. org.springframework.boot.web.support.SpringBootServletInitializer (class)

org.springframework.boot.web.support.SpringBootServletInitializer is an implementation of org.springframework.web.WebApplicationInitializer.

An opinionated WebApplicationInitializer to run a SpringApplication from a traditional WAR deployment. Binds Servlet, Filter and ServletContextInitializer beans from the application context to the server.

这是一个通过传统的WAR包部署方式运行SpringApplication的WebApplicationInitializer实现。它可以将应用容器中的Servlet、Filter和ServletContextInitializer相关的bean绑定到服务器(ServletContainer)。

org.springframework.boot.web.servlet.support.SpringBootServletInitializer#onStartup

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
	// Logger initialization is deferred in case an ordered
	// LogServletContextInitializer is being used
	this.logger = LogFactory.getLog(getClass());
	WebApplicationContext rootAppContext = createRootApplicationContext(
			servletContext);
	if (rootAppContext != null) {
		servletContext.addListener(new ContextLoaderListener(rootAppContext) {
			@Override
			public void contextInitialized(ServletContextEvent event) {
				// no-op because the application context is already initialized
			}
		});
	}
	else {
		this.logger.debug("No ContextLoaderListener registered, as "
				+ "createRootApplicationContext() did not "
				+ "return an application context");
	}
}

5. org.springframework.boot.web.servlet.ServletContextInitializer (interface)

Interface used to configure a Servlet 3.0+ context programmatically. Unlike WebApplicationInitializer, classes that implement this interface (and do not implement WebApplicationInitializer) will not be detected by SpringServletContainerInitializer and hence will not be automatically bootstrapped by the Servlet container.

This interface is primarily designed to allow ServletContextInitializers to be managed by Spring and not the Servlet container.

不同于WebApplicationInitializer,该接口的实现类不会被SpringServletContainerInitializer识别因此不会被Servlet容器自动执行。ServletContextInitializers主要被Spring管理而不是Servlet容器。

如果需要添加Servlet配置,需要编写ServletRegistrationBean的实现类并注册到BeanFactory。
如果需要添加Filter配置,需要编写FilterRegistrationBean的实现类并注册到BeanFactory。

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize

/**
 * Returns the {@link ServletContextInitializer} that will be used to complete the
 * setup of this {@link WebApplicationContext}.
 * @return the self initializer
 * @see #prepareWebApplicationContext(ServletContext)
 */
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
	return this::selfInitialize;
}

private void selfInitialize(ServletContext servletContext) throws ServletException {
	prepareWebApplicationContext(servletContext);
	registerApplicationScope(servletContext);
	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
			servletContext);
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}

所有可用于ServletContext配置的Bean可参考源码org.springframework.boot.web.servlet.ServletContextInitializerBeans#addServletContextInitializerBean(java.lang.String, org.springframework.boot.web.servlet.ServletContextInitializer, org.springframework.beans.factory.ListableBeanFactory)

private void addServletContextInitializerBean(String beanName,
	ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
	if (initializer instanceof ServletRegistrationBean) {
		Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
		addServletContextInitializerBean(Servlet.class, beanName, initializer,
				beanFactory, source);
	}
	else if (initializer instanceof FilterRegistrationBean) {
		Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
		addServletContextInitializerBean(Filter.class, beanName, initializer,
				beanFactory, source);
	}
	else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
		String source = ((DelegatingFilterProxyRegistrationBean) initializer)
				.getTargetBeanName();
		addServletContextInitializerBean(Filter.class, beanName, initializer,
				beanFactory, source);
	}
	else if (initializer instanceof ServletListenerRegistrationBean) {
		EventListener source = ((ServletListenerRegistrationBean<?>) initializer)
				.getListener();
		addServletContextInitializerBean(EventListener.class, beanName, initializer,
				beanFactory, source);
	}
	else {
		addServletContextInitializerBean(ServletContextInitializer.class, beanName,
				initializer, beanFactory, initializer);
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
initializer_list是C++11标准引入的一种数据结构,用于表示一个初始化列表。它可以用于函数参数、构造函数、赋值等场合。 初始化列表是由一系列用逗号隔开的值组成的,可以用花括号括起来。例如,{1, 2, 3}就是一个初始化列表,其中包含了三个整数1、2、3。 initializer_list的定义如下: ```cpp template<class T> class initializer_list { public: typedef const T* iterator; typedef const T& reference; typedef size_t size_type; initializer_list() noexcept; initializer_list(const T* p, size_t n) noexcept; size_t size() const noexcept; const T* begin() const noexcept; const T* end() const noexcept; }; ``` 使用initializer_list需要包含头文件<initializer_list>。 可以通过以下方式初始化initializer_list: ```cpp std::initializer_list<int> il = {1, 2, 3}; ``` 在函数参数中使用initializer_list可以方便地传递一组值: ```cpp void foo(std::initializer_list<int> il) { for (auto it = il.begin(); it != il.end(); ++it) { std::cout << *it << std::endl; } } foo({1, 2, 3}); ``` 在类的构造函数中使用initializer_list可以方便地对成员变量初始化: ```cpp class MyClass { public: MyClass(std::initializer_list<int> il) { for (auto it = il.begin(); it != il.end(); ++it) { vec.push_back(*it); } } private: std::vector<int> vec; }; MyClass mc = {1, 2, 3}; ``` 总之,initializer_list是一个十分方便的工具,可以用于各种场合,使代码更加简洁、清晰。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值