Spring集成tomcat作为web服务器与SpringBoot的WEB服务实现研究
一、 问题SpringBoot默认使用Tomcat作为servlet容器的web服务器,而通常tomcat的servlet容器需要使用web.xml来进行配置,但在SpringBoot中却找不到任何xml相关的配置,那么是怎么将tomcat嵌入到程序中的呢?二、 原理Tomcat自7.0版本开始已经完全支持Servlet3.0。Servlet3.0规范的一个主要目标是无需手动...
一、 问题
SpringBoot默认使用Tomcat作为servlet容器的web服务器,而通常tomcat的servlet容器需要使用web.xml来进行配置,但在SpringBoot中却找不到任何xml相关的配置,那么是怎么将tomcat嵌入到程序中的呢?
二、 原理说明
Tomcat自7.0版本开始已经完全支持Servlet3.0。Servlet 3.0 规范的一个主要目标是无需手动修改应用程序 web.xml 文件,即在web容器启动时为提供给第三方组件(servlet)做一些初始化工作的机会,例如注册可部署 servlet,filter(过滤器)和 listener(监听器)等。servlet3.0规范中通过javax.servlet.ServletContainerInitializer实现此功能。每个框架要使用javax.servlet.ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的javax.servlet.ServletContainerInitializer实现类,那么,当web容器启动时就会运行这个初始化器做一些组件内的初始化工作。一般伴随着javax.servlet.ServletContainerInitializer一起使用的还有javax.servlet.annotation.HandlesTypes注解,通过javax.servlet.annotation.HandlesTypes可以将感兴趣的一些类注入到javax.servlet.ServletContainerInitializer的onStartup方法作为参数传入。
三、 无web.xml的servlet程序实现
- 步骤1:定义接口TheWebApplicationInitializer,该接口含有void onStartup(ServletContext servletContext);方法;
public interface SpringWebApplicationInitializer {
void onStartup(ServletContext servletContext);
}
- 步骤2:实现SpringWebApplicationInitializer接口,并在实现类里注册servlet
public class SpringWebAppInitializerImpl implements SpringWebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
servletContext.log("Tomcal 启动过程中注册自己的servlet...");
//这里的TestHttpServlet类为继承HttpServlet的servlet类
ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher", new TestHttpServlet());
registration.setLoadOnStartup(1);
registration.addMapping("/*");
servletContext.log("Tomcal 的servlet注册完毕...");
}
}
- 步骤3:实现javax.servlet.ServletContainerInitializer接口,实现void onStartup()方法;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.annotation.HandlesTypes;
@HandlesTypes(SpringWebApplicationInitializer.class)
public class TheServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<SpringWebApplicationInitializer> initializers = new LinkedList<SpringWebApplicationInitializer>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers())
&& SpringWebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((SpringWebApplicationInitializer) 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("Spring WebApplicationInitializers detected on classpath: " + initializers);
for (SpringWebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
- 步骤4:新建META-INF/services/javax.servlet.ServletContainerInitializer文本文件,并在该文件中记录javax.servlet.ServletContainerInitializer的实现类
io.gitee.inrgihc.webserver.web.SpringServletContainerInitializer
四、使用spring-mvc简化servlet程序的编写
在编写基于spring-mvc的servlet程序时,会引入spring-webmvc-5.2.5.RELEASE.jar,改jar依赖引入spring-web-5.2.5.RELEASE.jar。而在spring-web-5.2.5.RELEASE.jar中,已经按照上述提及的servlet3.0规范,在META-INF/services/javax.servlet.ServletContainerInitializer文本文件中配置了实现类,其配置的实现类为:
org.springframework.web.SpringServletContainerInitializer
在该实现类SpringServletContainerInitializer中的实现如下:
@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override 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); } } }
从代码中可以看出,其实现过程中又调用了接口org.springframework.web.WebApplicationInitializer的实现类的onStartup()方法。
为此,我们基于spring-mvc编写servlet时,可实现org.springframework.web.WebApplicationInitializer接口,在onStartup()方法里实现spring相关的初始化工作,如下:
/** * 此处代码可参考: * <p> * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html * </p> * */ public class ApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { // Load Spring web application configuration // 这里使用基于注解的方式配置Spring的元数据,当然也可以通过xml来配置 AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); ac.register(MyAppConfig.class); //注册配置类 ac.setServletContext(servletContext); //设置web容器的context // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(ac); ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/"); } }
上述代码中,MyAppConfig为一个使用Spring-mvc注解@Configuration配置的java配置类,在该类里可以配置扫包的路径即HTTP消息的转换器等,代码如下:
import java.util.List; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @EnableWebMvc @ComponentScan("io.gitee.inrgihc.webmvc") public class MyAppConfig implements WebMvcConfigurer { /** * 配置HTTP消息转换器 */ @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // 配置为fastjson的json转换器 // FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); // converters.add(converter); // 配置为Jackson的json转换器 MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converters.add(converter); } }
然后即可编写各种Controller控制器接口了。
写好的程序打成war包后即可部署到tomcat容器中运行了。
参考示例工程代码:02-springmvc-web
参考示例工程代码:03-tomcat-springmvc
五、基于spring-mvc及内嵌tomcat实现SpringBoot
1、整合tomcat与spring
将将第三章嵌入tomcat的代码与第四章中实现WebApplicationInitializer接口初始化spring的代码进行整理,即实现了简易类似SpringBoot的web功能,代码如下:
public class WebApplication implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { // Load Spring web application configuration AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext(); ac.register(MyAppConfig.class); //ac.refresh(); // Create and register the DispatcherServlet DispatcherServlet servlet = new DispatcherServlet(ac); ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/*"); } public static void main(String[] args) throws LifecycleException { Tomcat tomcat = new Tomcat(); Server server = tomcat.getServer(); Service service = tomcat.getService(); service.setName("Tomcat-embbeded"); Connector connector = new Connector("HTTP/1.1"); connector.setPort(8080); service.addConnector(connector); // 这里不考虑jsp解析器的异常 // 即忽略:java.lang.ClassNotFoundException: org.apache.jasper.servlet.JspServlet tomcat.addWebapp("", System.getProperty("user.dir") + File.separator); server.start(); System.out.println("tomcat服务器启动成功.."); server.await(); } }
参考示例工程代码:04-spring-webserver
2、SpringBoot的实现
SpringBoot将其进行了简化,代码为:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
参考示例工程代码:05-spring-boot
更多推荐
所有评论(0)