前言:
springmvc一直在版本更新迭代,其实springboot的诞生并不是那么突兀 和springmvc还是有所关联的,只是很多人都停留在最早那一套ssm框架搭建(application.xml等配置) 在springmvc 5开始 就舍弃了之前那一套流程
回顾:
传统的servlet web项目 : web.xml中注册servlet
<!-- 两个标签中的servlet name要一致; 进行映射 url pattern指定路径-->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/a</url-pattern>
</servlet-mapping>
servlet3.0之后:@WebServlet
原理: 在3.0之后 servlet采用了注解的形式 注解写在servlet中,那么反射便可以获取到当前类信息 于是web,xml中类名可以省略,那么就还剩一个最重要的节点 ,所以很明显 @WebServlet(“value”) value就是url地址了,这样web.xml便可以省略。
分割线===========================================
下面看看springmvc 5之后是怎么处理的
- tomcat之所以被称为web容器 是因为它遵循了servlet规范,servlet3.0中 规定,web容器启动时,必须调用ServletContainerInitializer (简称SCI)实例中的onStarup方法,而SCI的实现类 通过java SPI声明自己是ServletContainerInitializer 的provider
这里可能会有点绕 我们先来看看springmvc是怎么做的 在META-INF底下建一个services包,写上文件名,文件里面写的 就是SCI的实现类
demo 代码如下
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
// 测试servlet
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter pw = resp.getWriter();
pw.write("hello spring mvc 5");
pw.close();
return;
}
}
注意看注释内容 讲解都在注释
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Set;
/**
* Description:SCI实现类
* 类比spring mvc中 SpringServletContainerInitializer implements ServletContainerInitializer
* 在spring mvc中 也正是SpringServletContainerInitializer 暴露在SPI中
* 同理 我们自定义的类TomcatServletContainerInitializer 也应该暴露在SPI中
* date: 2021/3/12
* @author:qkj
* @since JDK 1.8
*/
/**
* servlet 容器还会扫描 被@HandlesTypes 注解的接口实现类
* HandlesTypes :javax.servlet.annotation; 这也是servlet3.0规范
* TomcatWebApplicationInitializer 类比springmvc中 WebApplicationInitializer
*/
@HandlesTypes(TomcatWebApplicationInitializer.class)
public class TomcatServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext ctx) throws ServletException {
if (set != null) {
Iterator<Class<?>> iterator = set.iterator();
while (iterator.hasNext()) {
Class<?> clazz = (Class<?>) iterator.next();
if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())
&& TomcatWebApplicationInitializer.class.isAssignableFrom(clazz)) {
try {
// 调用TomcatWebApplicationInitializer 接口实现类的 onStartup() 方法
((TomcatWebApplicationInitializer) clazz.newInstance()).onStartup(ctx);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
import javax.servlet.ServletContext;
// 接口 类比springmvc WebApplicationInitializer
public interface TomcatWebApplicationInitializer {
void onStartup(ServletContext servletContext);
}
- 重点来了 我们实现的是TomcatWebApplicationInitializer 而不是SCI,为什么还是能执行onStarup方法?因为TomcatWebApplicationInitializer 在上面SCI实现类中 被@HandlesTypes修饰了 所以也会被扫描
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
public class TomcatServletInitializer implements TomcatWebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
ServletRegistration.Dynamic registration = servletContext.addServlet(TestServlet.class.getName(), TestServlet.class);
// 如果值为整数或则0,表示容器在应用启动时候加载并且初始化这个Servlet 值越小servlet的优先级越高,就越先被加载
registration.setLoadOnStartup(1);
// url
registration.addMapping("/app");
}
}
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;
import java.io.File;
// 启动方法
public class TomcatStartup {
public static void main(String[] args) {
try {
// 创建Tomcat容器
Tomcat tomcatServer = new Tomcat();
// 端口号设置
tomcatServer.setPort(9000);
// 读取项目路径 加载静态资源
StandardContext ctx = (StandardContext) tomcatServer.addWebapp("/", new File("src/main").getAbsolutePath());
// 禁止重新载入
ctx.setReloadable(false);
// class文件读取地址
File additionWebInfClasses = new File("target/classes");
// 创建WebRoot
WebResourceRoot resources = new StandardRoot(ctx);
// tomcat内部读取Class执行
resources.addPreResources(
new DirResourceSet(resources, "/WEB-INF/classes", additionWebInfClasses.getAbsolutePath(), "/"));
tomcatServer.start();
// 异步等待请求执行
tomcatServer.getServer().await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
pom.xml
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.15</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat/tomcat-jasper -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>8.5.15</version>
</dependency>
</dependencies>