Servlet3.0+下使用ServletContainerInitializer注册JAVA组件

72 篇文章 17 订阅
56 篇文章 15 订阅

【1】ServletContainerInitializer是什么

在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。

每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。

一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。

Tomcat容器的ServletContainerInitializer机制的实现,主要交由Context容器和ContextConfig监听器共同实现。

ContextConfig监听器负责在容器启动时读取每个web应用的WEB-INF/lib目录下包含的jar包的META-INF/services/javax.servlet.ServletContainerInitializer,以及web根目录下的META-INF/services/javax.servlet.ServletContainerInitializer(即直接在src下建立的META-INF/services/javax.servlet.ServletContainerInitializer),通过反射完成这些ServletContainerInitializer的实例化,然后再设置到Context容器中。

Context容器启动时就会分别调用每个ServletContainerInitializer的onStartup方法,并将感兴趣的类作为参数传入。

这里写图片描述

基本的实现机制如上图

  • 首先通过ContextConfig监听器遍历每个jar包或web根目录的META-INF/services/javax.servlet.ServletContainerInitializer文件,根据读到的类路径实例化每个ServletContainerInitializer

  • 然后再分别将实例化好的ServletContainerInitializer设置进Context容器中。

  • 最后Context容器启动时分别调用所有ServletContainerInitializer对象的onStartup方法。

如下图所示,在org/springframework/spring-web/5.3.1/spring-web-5.3.1.jar!/META-INF/services/下就配置了文件javax.servlet.ServletContainerInitializer
在这里插入图片描述


【2】@HandlesTypes注解与SpringServletContainerInitializer

① @HandlesTypes注解

Servlet 3.0+后,该注解用于声明一组类,这些类将会被传递给javax.servlet.ServletContainerInitializer使用。注解源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SuppressWarnings("rawtypes") // Spec API does not use generics
public @interface HandlesTypes {
    Class[] value();
}

即, 容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来。

② SpringServletContainerInitializer

package org.springframework.web;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;

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

	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = Collections.emptyList();

		if (webAppInitializerClasses != null) {
			initializers = new ArrayList<>(webAppInitializerClasses.size());
			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);
		}
	}

}

代码解释如下:

  • ① 如果webAppInitializerClasses不为null,则逐个进行遍历;
  • ② 是WebApplicationInitializer类型&&非接口&&非抽象类,则进行实例化然后将实例放入List<WebApplicationInitializer>
  • ③ 如果initializers为空,也就是没有发现WebApplicationInitializer实现,则打印日志No Spring WebApplicationInitializer types detected on classpath,然后结束方法。
  • ④ 如果initializers不为空,则进行排序,然后调用每个实例的onStartup方法;

下面是SpringServletContainerInitializer 的onStartup方法的javadoc

ServletContext委派给应用程序中所有WebApplicationInitializer实现类(类和子接口)。因为SpringServletContainerInitializer声明了注解@HandlesTypes(WebApplicationInitializer.class),在Servlet 3.0+容器下会自动扫描类路径下WebApplicationInitializer的所有实现,然后将所有实现的类型作为方法的入参webAppInitializerClasses

如果没有发现WebApplicationInitializer的实现,则该方法将毫无作为。仅仅会打印日志No Spring WebApplicationInitializer types detected on classpath告诉用户ServletContainerInitializer的确已经指向但是没有发现WebApplicationInitializer任何实现。

如果有一个或多个WebApplicationInitializer实现被检测到,其将会按照顺序被实例化(如果声明了@Order注解或者实现了Ordered接口)。然后将会按照顺序调用每一个WebApplicationInitializer实现的onStartup(ServletContext)方法。把ServletContext委派给WebApplicationInitializer实例时为了方便其注册配置servlets 诸如springmvcDispatcherServlet,监听器诸如ContextLoaderListener或者其他 Servlet API组件如filters。


【3】实现ServletContainerInitializer并注册三大组件

① 创建文件

如下图所示:

这里写图片描述

直接在src下面建立META-INF/services/javax.servlet.ServletContainerInitializer


② 实现类

//容器启动的时候会将@HandlesTypes指定的这个类型下面的子类(实现类,子接口等)传递过来;
//传入感兴趣的类型;
@HandlesTypes(value={HelloService.class})
public class MyServletContainerInitializer implements ServletContainerInitializer {

	/**
	 * 应用启动的时候,会运行onStartup方法;
	 * 
	 * Set<Class<?>> arg0:感兴趣的类型的所有子类型;
	 * ServletContext arg1:代表当前Web应用的ServletContext;一个Web应用一个ServletContext;
	 * 
	 * 1)、使用ServletContext注册Web组件(Servlet、Filter、Listener)
	 * 2)、使用编码的方式,在项目启动的时候给ServletContext里面添加组件;
	 * 		必须在项目启动的时候来添加;
	 * 		1)、ServletContainerInitializer得到的ServletContext;
	 * 		2)、ServletContextListener得到的ServletContext;
	 */
	@Override
	public void onStartup(Set<Class<?>> arg0, ServletContext sc) throws ServletException {
		// TODO Auto-generated method stub
		System.out.println("感兴趣的类型:");
		for (Class<?> claz : arg0) {
			System.out.println(claz);
		}
		
		//注册组件  ServletRegistration  
		ServletRegistration.Dynamic servlet = sc.addServlet("userServlet", new UserServlet());
		//配置servlet的映射信息
		servlet.addMapping("/user");
		
		
		//注册Listener
		sc.addListener(UserListener.class);
		
		//注册Filter  FilterRegistration
		FilterRegistration.Dynamic filter = sc.addFilter("userFilter", UserFilter.class);
		//配置Filter的映射信息
		filter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");
		
	}

}

测试如下:

这里写图片描述

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
是的,Servlet 3.0 版本及其之后的版本增加了对文件上传的支持,并提供了相关的注解来简化文件上传的处理。 在 Servlet 3.0+ 版本中,可以使用 `@MultipartConfig` 注解来标记一个 Servlet 或者一个 Servlet 类,表示该 Servlet 可以接收包含文件上传的请求。这个注解可以用在 Servlet 类或者 `web.xml` 的 `<servlet>` 元素上。 使用 `@MultipartConfig` 注解时,可以指定一些属性,如 `location`、`maxFileSize`、`maxRequestSize` 等,来控制文件上传的相关配置。 下面是一个使用 `@MultipartConfig` 注解的示例: ```java @WebServlet(name = "UploadServlet", urlPatterns = {"/upload"}) @MultipartConfig( fileSizeThreshold = 1024 * 1024, // 1MB maxFileSize = 1024 * 1024 * 5, // 5MB maxRequestSize = 1024 * 1024 * 10 // 10MB ) public class UploadServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 处理文件上传逻辑 Part filePart = request.getPart("file"); String fileName = filePart.getSubmittedFileName(); InputStream fileContent = filePart.getInputStream(); // ... } } ``` 在上面的示例中,`@MultipartConfig` 注解指定了文件上传的一些配置,例如文件大小阈值、最大文件大小和最大请求大小。在 `doPost()` 方法中,使用 `request.getPart()` 方法获取上传的文件部分,然后可以通过 `Part` 对象获取文件名、输入流等信息进行处理。 通过这种方式,Servlet 3.0+ 版本的文件上传变得更加简单和方便。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值