Servlet与SpringBoot的前世今生个人分为了4个阶段
- Web1.0
原始的servlet,servlet中维护service(手动new),service中维护dao(QueryRunner等DbUtils),此时在中配置好url映射与servlet的关系,在tomcat接收到请求时,根据匹配的servlet,通过反射创建servlet对象,执行doGet或doPost方法。一般需要手动设置一些响应头信息。
- Web 2.0
此时有了spring框架,通过spring的ioc容器,可方便的创建出service层以及dao层的bean。但是servlet是需要被tomcat创建的,此时还无法让spring创建servlet,但是servlet中的service是可以从spring容器中获取的(不用再手动new了),service层也可以自动注入mapper。
- Web 3.0
虽然spring的ioc容器能够很方便管理项目中的service和mapper层的组件,但是没有办法解决servlet这个痛点,于是springmvc出现了。随之而来的DispatcherServlet代替了之前手动在中写的一大堆servlet,此时多出了controller层。只需要编写类以及方法,在方法上使用@RequestMapping注解指定url就可以处理请求了。如此方便的springmvc是一个怎样的流程?首先DispathcerServlet还是有tomcat创建,而tomcat在创建servlet后会调用其init方法,在这个方法中DispathcerServlet做了一系列初始化工作,最重要的就是创建一个子容器(一般springmvc只扫描controller), 并设置父容器(spring管理的service和mapper容器)。此时controller层就可以自动装配service,因为都被spring管理了,初始化好容器以后,继续初始化处理器适配器,参数解析器,视图解析器等九大组件,这些都初始化好以后,dispathcerservlet就可以接收并 处理请求了。请求来到dispathcerservlet,根据请求信息找到处理器(handlermethod),此时返回的是一个处理器执行链,包括拦截器,controller对象(如果被代理了,则会是代理对象)。再根据处理器找到处理器适配器,让适配器来执行目标方法。得到ModelAndView对象,交给视图解析器处理。此时我们不需要手动往response输出流中写内容,springmvc会帮我们处理。
- Web 4.0
4.0就是个人理解的springboot时代,springboot主要提供了两个强大功能,一个是自动配置,另一个就是内嵌式的servlet容器。
当引入 spring-boot-starter-web这个依赖的时候,发现启动项目时,自动开启了tcp链接,也就是启动了tomcat服务器,而其他地方并没有改变任何一行代码,这是怎么实现的?通过查看源码得知,在springboot启动时,会通过
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("", (ClassLoader)null) && !ClassUtils.isPresent("", (ClassLoader)null) && !ClassUtils.isPresent("", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
如果类路径下有特定的类,则会创建一个 AnnotationConfigServletWebServerApplicationContext容器,而这个容器重写了父类的onRefresh方法
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = this.getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("");
ServletWebServerFactory factory = this.getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
createWebServer.end();
this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
} else if (servletContext != null) {
try {
this.getSelfInitializer().onStartup(servletContext);
} catch (ServletException var5) {
throw new ApplicationContextException("Cannot initialize servlet context", var5);
}
}
this.initPropertySources();
}
正是在这个方法里面启动了服务器。
除此之外,springboot的自动配置也是极其方便的,以spring-boot-starter-data-redis为例,如果是之前的ssm工程,则需要引入各种依赖,同时也需要自己维护好这些依赖的版本,引入好依赖后,在中配置好和redis相关的bean以及连接工厂等,或者再写一个配置类,@Bean一下。这样在其他地方就能使用了。但是在springboot这些统统不需要做,只需要在properties配置文件中配置好相关属性即可。当引入redis-start之后,此时spring就会加载该jar包下文件,这个文件指定了spring需要加载的自动配置类,一般这种自动配置类都会结合一个xxxProperties类,比如redis的RedisProperties,此时spring就会继续创建这个Properties类,而这个Properties类的属性就是和文件配置的属性值绑定的。此时在创建RedisTemplate等Bean时就会用到这个Properties类中的属性 ,进而创建好Bean,这样在其他类中就可以使用了。
spring的autoconfiure包中维护了大量的xxxAutoConfguirtion,但是这些配置类不是都生效的,通常又会结合@Condition注解来使用,最典型的就是当类路径下有某个类时才会生效,在springboot的源码中,经常看到一些爆红的类,就是当前我们的项目没有引入这个类,但是发现运行springboot项目时并不会报错?这是因为我们在引入springboot或者其他第三方jar包时,引入的都是已经打包好的class文件,这种是已经被编译好的文件,即便当前类路径下并没有某个class也不会报错,对于class文件来说,里面的内容就是一些普通字符串,只有在真正用到某个类时,说不定才会报错,正常运行已经编译好的class文件不会报错。