027:Servlet与SpringMVC关系
1 SpringMVC深度源码分析课程介绍
课题内容:
1.Servlet与SpringMVC之间的关系
2.Servlet框架线程是否安全
3.SpringMVC是如何完全无web.xml启动的?
2 基于ide构建ServletMaven工程
Servlet与SpringMVC关系
SpringMVC是基于Servlet封装的MVC框架。
Servlet简单的介绍
Servlet是sun公司提供的一门用于开发动态web资源的技术。
Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
1、编写一个Java类,实现servlet接口。
2、把开发好的Java类部署到web服务器中。
按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet。
快速搭建Servlet环境
1.建立maven项目选择webapp
创建项目时候添加archetypeCatalog=internal参数,解决创建maven web项目慢的问题
2.引入Servlet的jar包
两种方式:tomcat自带Servlet;使用maven引用
3.项目引入tomcat jar包
4.创建java目录,Mark Directory as Sources Root
5.创建Servlet类并配置xml
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("this is a servlet");
}
}
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.mayikt.servlet.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
基于注解方式配置
@WebServlet("/")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("this is a servlet");
}
}
启动tomcat,测试结果:
3 如何证明Servlet线程是否安全
Servlet线程是否安全?
不安全,Servlet是单例的 在高并发情况下,可能会存在线程安全问题。
@WebServlet("/")
public class MyServlet extends HttpServlet {
private Integer count = 0;
public MyServlet() {
System.out.println("MyServlet 无参构造函数被执行...");
}
// 如何证明servlet线程不安全 单例才会共享
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
count++;
resp.getWriter().println("this is a servlet,count:" + count);
}
// 发送两次请求
// 第一次请求 1 第二次请求 2
// 如果发送多次请求,count累计在增加,说明count是共享的
}
验证结果:
4 ServletContainerInitializer用法
ServletContainerInitializer
在web容器启动时加载第三方依赖jar包,提供给第三方组件做一些初始化的工作,例如注册servlet或者filtes等,servlet规范中通过ServletContainerInitializer实现此功能。
每个框架要使用ServletContainerInitializer就必须在对应的jar包的META-INF/services 目录创建一个名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类(全路径类名)。
ServletContainerInitializer实现类
@HandlesTypes(value = MyHandlesType.class)
public class MyServletContainerInitializer implements ServletContainerInitializer {
/**
* onStartup 作用是servlet容器初始化加载一些操作 比如第三方依赖信息、手动加载servlet、监听器、过滤器
*
* @param set 获取继承该MyHandlesType类所有子类class信息(不包含本身类) 感兴趣的类
* @param servletContext
* @throws ServletException
*/
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
// 1.获取感兴趣的类
for (Class<?> c : set) {
System.out.println("c:" + c);
}
// 2.手动给容器添加servlet、监听器、过滤器
ServletRegistration.Dynamic payServlet = servletContext.addServlet("payServlet", new PayServlet());
payServlet.addMapping("/pay"); //设置映射地址
// 如何能够让tomcat启动加载到该类MyServletContainerInitializer
// tomcat 默认读取META-INF
}
}
HandlesType指定的类及其子类
public class MyHandlesType {
}
public class MemberUtils extends MyHandlesType {
}
public class PayUtils extends MyHandlesType{
}
手动添加的servlet
public class PayServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("this is pay");
}
}
测试结果:
5 基于注解方式构建SpringMVC框架
Springboot中实现没有web.xml基于注解方式启动
引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
SpringServletContainerInitializer作用:加载第三方依赖信息实现初始化操作
6 使用注解形式启动SpringMVC项目
@RestController
public class IndexController {
@RequestMapping(value = "/", produces = "text/html;charset=UTF-8")
public String index() {
return "蚂蚁课堂 666";
}
}
@Configuration
@ComponentScan("com.mayikt.controller")
@EnableWebMvc
public class SpringMvcConfig {
// @EnableWebMvc 等同于开启springmvc注解方式
// @Configuration 替代xml
// @ComponentScan 替代扫包范围
// DispatcherServlet
}
public class WebInitializer implements WebApplicationInitializer {
// 使用WebApplicationInitializer替代web.xml
// 为什么WebInitializer不需要注解,能够自动找到该类?
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 1.启动SpringMVC容器 类注入到Spring中
AnnotationConfigWebApplicationContext app = new AnnotationConfigWebApplicationContext();// 启动SpringMVC Web
// 2.注入springmvc的配置文件
app.register(SpringMvcConfig.class);
// 3.将DispatcherServlet注入到servlet容器中
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(app));
// 4.设置url路径映射
dispatcher.addMapping("/");
dispatcher.setLoadOnStartup(1);// 设置优先级 最高表示最早被加载
}
}
注意删除webapp目录下的所有内容
测试结果:
7 SpringServletContainerInitializer源码分析
Springmvc注解方式启动原理分析:
- 引入Spring-Web依赖Jar包,加载SpringServletContainerInitializer 提供给SpringMVC实现初始化;
- 使用注解@HandlesTypes WebApplicationInitializer;
- WebInitializer继承WebApplicationInitializer;
- Servlet容器在启动的时候加载WebInitializer;
- 调用onStartup方法回调传递servletContext;
- servletContext手动注册DispatcherServlet、配置文件。
原理:Sevlet容器在初始化的时候会加载到SpringServletContainerInitializer类,然后找@HandlesTypes定义类的子类,回调传递servletContext。然后手动注册DispatcherServlet、注册springmvc配置文件。