spring系列11-springMVC初始化和异步处理

1.ServletContainerInitializer

在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。
我们平时常用的Tomcat容器主要通过Context容器和ContextConfig监听器完成这个机制。ContextConfig监听器负责在容器启动时读取每个web应用以及这个项目依赖的jar包中META-INF/services/目录下的javax.servlet.ServletContainerInitializer文件。将文件中ServletContainerInitializer的实现类加载并且通过反射实例化,设置到Context容器中。Context容器启动时分别调用所有ServletContainerInitializer对象的onStartup方法。
我们以springMVC为例分析下,spring中有个spring-web的jar包,如图:
在这里插入图片描述
这个文件中内容如下:
org.springframework.web.SpringServletContainerInitializer
这个类源码如下:
//HandlesTypes注解是将其括号里面的接口或者类的子类或者实现传入onStartup的方法中


@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {

	/**
	 * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer}
	 * implementations present on the application classpath.
	 * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)},
	 * Servlet 3.0+ containers will automatically scan the classpath for implementations
	 * of Spring's {@code WebApplicationInitializer} interface and provide the set of all
	 * such types to the {@code webAppInitializerClasses} parameter of this method.
	 * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath,
	 * this method is effectively a no-op. An INFO-level log message will be issued notifying
	 * the user that the {@code ServletContainerInitializer} has indeed been invoked but that
	 * no {@code WebApplicationInitializer} implementations were found.
	 * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected,
	 * they will be instantiated (and <em>sorted</em> if the @{@link
	 * org.springframework.core.annotation.Order @Order} annotation is present or
	 * the {@link org.springframework.core.Ordered Ordered} interface has been
	 * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)}
	 * method will be invoked on each instance, delegating the {@code ServletContext} such
	 * that each instance may register and configure servlets such as Spring's
	 * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener},
	 * or any other Servlet API componentry such as filters.
	 * @param webAppInitializerClasses all implementations of
	 * {@link WebApplicationInitializer} found on the application classpath
	 * @param servletContext the servlet context to be initialized
	 * @see WebApplicationInitializer#onStartup(ServletContext)
	 * @see AnnotationAwareOrderComparator
	 */
	@Override
	//webAppInitializerClasses就是上述HandlesTypes注解中的接口或者类的子类或者实现类
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

WebApplicationInitializer有三个抽象子类,这里我们看下其中两个AbstractDispatcherServletInitializer和AbstractAnnotationConfigDispatcherServletInitializer。
AbstractDispatcherServletInitializer用来注册DispatcherServlet。


protected void registerDispatcherServlet(ServletContext servletContext) {
		String servletName = getServletName();
		Assert.hasLength(servletName, "getServletName() must not return null or empty");

		WebApplicationContext servletAppContext = createServletApplicationContext();
		Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
		if (registration == null) {
			throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
					"Check if there is another servlet registered under the same name.");
		}

		registration.setLoadOnStartup(1);
		registration.addMapping(getServletMappings());
		registration.setAsyncSupported(isAsyncSupported());

		Filter[] filters = getServletFilters();
		if (!ObjectUtils.isEmpty(filters)) {
			for (Filter filter : filters) {
				registerServletFilter(servletContext, filter);
			}
		}

		customizeRegistration(registration);
	}

AbstractAnnotationConfigDispatcherServletInitializer用来注册根容器和servlet容器。

//注册根容器
protected WebApplicationContext createRootApplicationContext() {
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			context.register(configClasses);
			return context;
		}
		else {
			return null;
		}
	}
//注册Servlet容器
protected WebApplicationContext createServletApplicationContext() {
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			context.register(configClasses);
		}
		return context;
	}

根容器和Servlet容器的区别:servlet的容器用来处理@Controller,视图解析和web相关组件。而root根容器主要针对服务层和数据源DAO层及事务控制相关处理。
在这里插入图片描述

2.异步处理

springMVC中还有个有意思的异步请求。我们平时使用tomcat都是同步,客户端发情请求,tomcat启动一个线程处理,直到有相应返回,这个线程一直等待。当系统并发太高时,而tomcat的线程数有限,这时候系统就会到达瓶颈。这时候可以使用异步请求,客户端发起请求,tomcat启动一个线程,然后将请求交由另外一个线程池处理,连接的线程马上返回。
springMVC异步处理有两种方式:
(1)controller类的方法返回Callable
这里我们直接使用官方示例。

@PostMapping
public Callable processUpload(final MultipartFile file) {

return new Callable<String>() {
    public String call() throws Exception {
        // ...
        return "someView";
    }
};

}

看下官方解释:
//Controller返回一个Callable
Controller returns a Callable.
//Spring MVC向一个线程池提交一个Callable进行异步处理
Spring MVC starts asynchronous processing and submits the Callable to a TaskExecutor for processing in a separate thread.
//DispatcherServlet和Filter退出当前Servlet容器的线程,但响应保持打开
The DispatcherServlet and all Filter’s exit the Servlet container thread but the response remains open.
//Callable返回一个结果,Spring MVC将请求重发给Spring MVC去响应
The Callable produces a result and Spring MVC dispatches the request back to the Servlet container to resume processing.
//DispatcherServlet处理返回的结果
The DispatcherServlet is invoked again and processing resumes with the asynchronously produced result from the Callable.

(2)controller类的方法返回DeferredResult

@RequestMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<String>();
    // Save the deferredResult somewhere..
    return deferredResult;
}

// In some other thread...
deferredResult.setResult(data);
//看下官方解释
//Controller返回一个DeferredResult,并且将它保持在内存中的一个队列中
Controller returns a DeferredResult and saves it in some in-memory queue or list where it can be accessed.
//Spring MVC开始异步处理
Spring MVC starts async processing.
//DispatcherServlet和Filter退出当前Servlet容器的线程,但响应保持打开
The DispatcherServlet and all configured Filter’s exit the request processing thread but the response remains open.
//其他线程设置DeferredResult的值,Spring MVC将值返回给Servlet容器
The application sets the DeferredResult from some thread and Spring MVC dispatches the request back to the Servlet container.
//DispatcherServlet处理返回的结果
The DispatcherServlet is invoked again and processing resumes with the asynchronously produced result.

上述两种方式实际上是集成了servlet3.0提供的异步处理。看下官方解释:

//一个Servlet请求通过request.startAsync()启动异步处理,这样可以让Servlet和Filters退出,名单相应可以打开等待完成
A ServletRequest can be put in asynchronous mode by calling request.startAsync(). The main effect of doing so is that the Servlet, as well as any Filters, can exit but the response will remain open to allow processing to complete later.
//request.startasync()返回AsyncContext,可用于进一步控制异步处理。例如,它提供了方法分派,类似于servlet //API的转发,只是它允许应用程序在servlet容器线程上恢复请求处理。
The call to request.startAsync() returns AsyncContext which can be used for further control over async processing. For example it provides the method dispatch, that is similar to a forward from the Servlet API except it allows an application to resume request processing on a Servlet container thread.
//servletrequest提供对当前调度程序类型的访问,可用于区分处理初始请求、异步调度、转发和其他调度程序类型。
The ServletRequest provides access to the current DispatcherType that can be used to distinguish between processing the initial request, an async dispatch, a forward, and other dispatcher types.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值