基于servlet3.0搭建spring mvc
1、概述
还记得我刚学Java Web的时候,是17年,那时候servlet和jsp还在延续它的辉煌,ssh和ssm仍然是企业潮流,开发工具还是eclipse,有时候一个Tomcat字符集乱码的问题都要解决很久,老师教我们从servlet,到jsp,再到ssh和ssm,可是学到最后,即使我们的项目能跑了,可再让我们手工搭一遍,根本就摸不着头脑,因为配置太多了,我们搞不清楚原理,搞不清楚为什么要这么配置,自然也就记不住。
然后,往往是这些复杂的配置,才能更好地反映我们的基本功扎不扎实,现在的spring boot封装太过完美了,以及有了更强大的开发工具IDEA,几分钟就能搭建出一个能跑的项目来。还记得当年我们都是在web.xml里配置各种servlet,filter,用了spring boot之后,突然一瞬间就再也没碰过web.xml了,以前由于有各种东西要学,其中的猫腻也就没有追究了。
如今的我,已经不是当年的我。本文的目的,不是为了回味当年的servlet、jsp和web.xml的,我会教你如何不用web.xml,去搭建一个spring mvc应用,注意,我不会引入spring boot,也不会引入持久层框架,而是专注于一个最小化的、能跑的spring mvc应用,我会在IDEA中配置Tomcat,然后将应用放到Tomcat中。最终的效果是,我们能在浏览器上访问静态资源,也能在浏览器上访问Controller。
相信我,你会学到很多基础但很重要的东西。
2、Spring MVC请求流程
- 请求到达DispatcherServlet
- DispatcherServlet会将请求委托给控制器,但控制器会有多个,因此需要先查找一个或多个处理器映射(handler mapping)
- 找到控制器,且控制器处理好后,就会将模型与视图(逻辑名,需要解析)返给DispatcherServlet
- DispatcherServlet再去调用视图解析器,视图解析器将解析出来的视图返给DispatcherServlet
- 最后将视图和模型打包发给前端
3、Spring MVC不需要web.xml的原理
传统方式,servlet会配置在web.xml中,然后打成war包。但是基于servlet3.0规范和3.1增强,就不需要web.xml了。
原理:
- 在servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果能发现的话就会用它来配置Servlet容器。Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类会查找实现WebApplicationInitializer的类并将配置的任务交给他们来完成,Spring3.2引入了一个便利的WebApplicationInitializer基础实现,就是AbstractAnnotationConfigDispatcherServletInitializer。
- 也就是说,AbstractAnnotationConfigDispatcherServletInitializer的任意子类都会自动地配置DispatcherServlet和Spring应用上下文;
- 继承AbstractAnnotationConfigDispatcherServletInitializer,需要实现三个方法:
-
- getServletMappings:将一个或多个路径映射到DispatcherServlet上
-
- getRootConfigClasses:用于定义DispatcherServlet应用上下文中的bean
-
- getServletConfigClasses:配置ContextLoaderListener创建的上下文中的bean
- Tomcat7.0以上版本支持servlet3.0
两个上下文的关系:
- 当DispatcherServlet启动时,它会创建Spring应用上下文,并加载配置文件或配置类中声明的bean
- 在Spring web环境中,还有一个上下文,这个上下文是由ContextLoaderListener创建的
- 一般DispatcherServlet用于加载包含web组件的bean,如控制器、视图解析器、处理器映射,而ContextLoaderListener负责加载应用中其它的bean,如dao
- AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet和ContextLoaderListener
4、搭建Spring MVC项目
1、项目整体架构
2、pom.xml
首先,在IDEA中创建一个Maven项目,完整的pom.xml如下所示。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bobo</groupId>
<artifactId>spring-mvc</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<spring.version>5.2.5.RELEASE</spring.version>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<resource.delimiter>@</resource.delimiter>
<maven.compiler.source>${java.version}</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
3、WebAppInitializer
这个类就是上文提到的AbstractAnnotationConfigDispatcherServletInitializer类的子类。
package com.bobo.springmvc.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
protected String[] getServletMappings() {
System.out.println("getServletMappings executed");
return new String[]{"/"};
}
}
4、RootConfig
ComponentScan用于扫描项目中的组件。
package com.bobo.springmvc.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.bobo.springmvc")
public class RootConfig {
}
5、WebConfig
需要使用@EnableWebMvc启用Spring MVC,并且配置视图解析器。
WEB-INF目录是web应用的安全目录,里面的资源不能被浏览器直接访问。如果资源是放到src/main/web目录下的,是可以被浏览器直接访问的。
package com.bobo.springmvc.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.bobo.springmvc"})
public class WebConfig implements WebMvcConfigurer {
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
/**
* 配置静态资源的处理.将静态资源的请求转发到servlet容器中的默认servlet,而不是让DispathcerServlet去处理
* @param configurer
*/
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
6、HelloController
package com.bobo.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class HelloController {
@RequestMapping(method = RequestMethod.GET,value = "/toHello")
public String toHello(){
System.out.println("请求到达HelloController.toHello方法");
return "hello";
}
}
7、web目录
仔细看,src/main/web文件夹中间有一个实心的蓝色圆点,它标志这是一个web目录,这个是怎么生成的呢?
在IDEA的Project Structure->Modules找到对应的项目,然后右键->Add->选择Web,然后在Web Resource Directories新增一个选项,如下图所示。
首页index.html和hello.html的内容随意,只要最后能被访问到就行。
8、配置Tomcat
下载:https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.83/bin/ ,解压
修改logging.properties,凡是UTF-8都改为GBK,否则IDEA控制台会中文乱码,如下图所示。
将项目导出为artifact,如下图所示。
这里有一个Output directory,如下图所示,我们先记住它。
在IDEA中配置好Tomcat,然后部署项目,如下图所示。
添加进来后,将下面的路径改为/。其实不改也行,主要是为了方便。
5、运行测试
一切都准备好后,运行项目,我们发现在Output Directory目录下多了些东西,这是Tomcat启动时生成的。使用tree /F命令,输出如下所示。
F:.
│ index.html
│
├─META-INF
│ MANIFEST.MF
│
└─WEB-INF
├─classes
│ └─com
│ └─bobo
│ └─springmvc
│ ├─config
│ │ RootConfig.class
│ │ WebAppInitializer.class
│ │ WebConfig.class
│ │
│ └─controller
│ HelloController.class
│
├─lib
│ spring-aop-5.2.5.RELEASE.jar
│ spring-beans-5.2.5.RELEASE.jar
│ spring-context-5.2.5.RELEASE.jar
│ spring-core-5.2.5.RELEASE.jar
│ spring-expression-5.2.5.RELEASE.jar
│ spring-jcl-5.2.5.RELEASE.jar
│ spring-web-5.2.5.RELEASE.jar
│ spring-webmvc-5.2.5.RELEASE.jar
│
└─views
hello.html
在浏览器上分别访问首页http://localhost:8080/和Controller http://localhost:8080/toHello 进行测试,如果能成功访问,那就说明大功告成啦!