在之前我们的项目都是以jar包结尾的,没有放webapp的地方。
springboot最大的特点:自动装配,创建应用,选择模块导入starter,只需要专注于业务代码
springboot到底帮我们配置了什么,我们能不能修改?能修改哪些东西?能不能扩展
xxxAutoConfiguration:向容器中自动配置组件
xxxProperties:自动配置类,装配配置文件中自定义的一些内容
要解决的问题:
导入静态资源html,css,js
首页
写jsp的地方,模板引擎Thymeleaf
装配和扩展SpringMVC
增删改查
拦截器
1、静态资源处理
我们项目中有许多的静态资源,比如css,js等文件,这个SpringBoot怎么处理呢?
如果我们是一个web应用,我们的main下会有一个webapp,我们以前都是将所有的页面导在这里面的,但是我们现在是pom,打包方式为jar的方式,那么这种方式SpringBoot能不能来给我们写页面呢?当然是可以的,但是SpringBoot对于静态资源放置的位置,是有规定的!
静态资源映射规则
SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置类里面,里面有很多配置方法,有一个方法为addResourceHandlers 添加资源处理
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
// 已禁用默认资源处理
logger.debug("Default resource handling disabled");
return;
}
// 缓存控制
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
// webjars 配置
// 所有的 /webjars/**,都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源;
if (!registry.hasMappingForPattern("/webjars/**")) {
// addResourceHandler:访问前缀
// addResourceLocations:资源路径
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
// 静态资源配置
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
规则一:webjars
Webjars本质就是以jar包的方式引入我们的静态资源 , 我们以前要导入一个静态资源文件,直接导入即可,使用SpringBoot需要使用Webjars。
要使用jQuery,我们只要要引入jQuery对应版本的pom依赖即可!
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.4.1</version>
</dependency>
导入完毕,查看webjars目录结构,并访问Jquery.js文件!
访问:只要是静态资源,SpringBoot就会去对应的路径寻找资源,我们这里访问:http://localhost:8080/webjars/jquery/3.4.1/jquery.js
规则二
项目中要是使用自己的静态资源该怎么导入呢?
我们去找staticPathPattern发现第二种映射规则 :/** , 访问当前的项目任意资源,它会去找 resourceProperties 这个类,我们可以点进去看一下分析:
// 进入方法
public String[] getStaticLocations() {
return this.staticLocations;
}
// 找到对应的值
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
// 找到路径
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
};
ResourceProperties 可以设置和我们静态资源有关的参数;这里面指向了它会去寻找资源的文件夹,即上面数组的内容。
所以得出结论,以下四个目录存放的静态资源可以被我们识别,它们的优先级从上到下,所以如果static和public里面都有index.html则优先加载static中的index.html。
"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
我们可以在resources根目录下新建对应的文件夹,都可以存放我们的静态文件;
比如我们访问 http://localhost:8080/1.js , 他就会去这些文件夹中寻找对应的静态资源文件;
自定义静态资源路径
我们也可以自己通过配置文件来指定一下,哪些文件夹是需要我们放静态资源文件的,在application.properties中配置;
spring:
resources:
static-locations: [classpath:/haha/]
一旦自己定义了静态文件夹的路径,原来的自动配置就都会失效了!
自定义访问前缀
spring:
mvc:
static-path-pattern: /res/**
* 代表一层目录或目录下的一个文件
** 任意层目录任意文件
访问路径:当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找
首页处理
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(), // getWelcomePage 获得欢迎页
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return welcomePageHandlerMapping;
}
点进去继续看
private Optional<Resource> getWelcomePage() {
// 获取静态资源路径
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
// 欢迎页就是一个location下的的 index.html 而已
private Resource getIndexHtml(String location) {
return this.resourceLoader.getResource(location + "index.html");
}
欢迎页,静态资源文件夹下的所有 index.html 页面;被 /** 映射。
比如我访问 http://localhost:8080/ ,就会找静态资源文件夹下的 index.html
可以配置静态资源路径,但是不可以配置静态资源的访问前缀,否则导致 index.html不能被默认访问。
spring:
# mvc:
# static-path-pattern: /res/** 这个会导致welcome page功能失效
resources:
static-locations: [classpath:/haha/]
网站图标
与其他静态资源一样,Spring Boot在配置的静态内容位置中查找 favicon.ico。如果存在这样的文件,它将自动用作应用程序的favicon。
spring:
# mvc:
# static-path-pattern: /res/** 这个会导致 Favicon 功能失效
2、整合Thymeleaf
SpringBoot不推荐使用JSP作为视图层技术,而是默认使用Thymeleaf来做动态页面。template目录用来存放类似于Thymeleaf这样的模板引擎。
引入Thymeleaf:
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
我们已经引入了Thymeleaf,怎么使用?
我们首先得按照SpringBoot的自动配置原理看一下我们这个Thymeleaf的自动配置规则,在按照那个规则,我们进行使用。
我们去找一下Thymeleaf的自动配置类:ThymeleafProperties
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
}
我们可以在其中看到默认的前缀和后缀!
我们只需要把我们的html页面放在类路径下的templates下,thymeleaf就可以帮我们自动渲染了。
使用thymeleaf什么都不需要配置,只需要将他放在指定的文件夹下即可!
测试
在页面导入Thymeleaf命名空间,以获得更好的提示,代码如下:
<!--引入命名空间-->
<html xmlns:th="http://www.thymeleaf.org">
@Controller
public class ThymeleafController {
@GetMapping("/test")
public String index(Model model){
List<User> list = new ArrayList<>();
for(int i = 0; i < 5; i++){
User u = new User();
u.setId(i);
u.setName("小明" + i);
u.setAddress("陕西" + i);
list.add(u);
}
model.addAttribute("list", list);
return "test";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1" width="60%" align="center">
<tr>
<td>编号</td>
<td>姓名</td>
<td>地址</td>
</tr>
<tr th:each="user:${list}">
<td th:text="${user.id}"></td>
<td th:text="${user.name}"></td>
<td th:text="${user.address}"></td>
</tr>
</table>
</body>
</html>
3、SpringBoot返回JSON数据
项目开发中,接口与接口之间,以及前后端之间数据的传输都使用JSON格式。在SpringBoot中是接口返回JSON格式数据很简单,在Controller中使用@RestController注解即可返回JSON格式的数据,@RestController也是SpringBoot新增的一个注解,包含了原来的@Controller和@ResponseBody,@ResponseBody是将返回的数据结构转换为JSON格式。
在默认情况下,使用@RestController注解即可将返回的数据转换成JSON格式,在SpringBoot中默认使用的是JSON解析技术框架Jackson。我们打开pom.xml文件中的spring-boot-starter-web依赖,可以看到spring-boot-starter-json依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.6.3</version>
<scope>compile</scope>
</dependency>
3.1 常用数据类型转换为JSON格式
@RestController
@RequestMapping("/json")
public class JsonController {
@RequestMapping("/user")
public User getUser(){
return new User(10,"林昊天","九龙湖M4");
}
@RequestMapping("/list")
public List<User>getUserList(){
List<User>userList=new ArrayList<>();
User user1=new User(1,"林昊天1号","九龙湖桃1");
User user2=new User(2,"林昊天2号","九龙湖橘1");
userList.add(user1);
userList.add(user2);
return userList;
}
@RequestMapping("/map")
public Map<String,Object>getMap(){
Map<String,Object>map=new HashMap<>(3);
User user=new User(4,"沈子怡1号","九龙湖梅5");
map.put("作者信息",user);
map.put("博客地址","abaaba");
map.put("公众号","sss");
return map;
}
}
控制层接口完成后,分别返回了User对象、List集合和Map集合。结果如下:
3.2 Jackson对null的处理
在实际项目中,难免会遇到一些null值。当转JSON格式时,不希望这些null值出现,例如我们希望所有的null值在转JSON格式时都变成空字符串。
在SpringBoot中我们做一下配置即可,新建一个Jackson配置类:
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder){
ObjectMapper objectMapper=builder.createXmlMapper(false).build();
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException{
jsonGenerator.writeString("");
}
});
return objectMapper;
}
}
然后修改上面返回的Map接口,将几个值改为null进行测试,代码如下:
public Map<String,Object>getMap(){
Map<String,Object>map=new HashMap<>(3);
User user=new User(4,"沈子怡1号","九龙湖梅5");
map.put("作者信息",user);
map.put("博客地址","abaaba");
map.put("公众号","sss");
map.put("爱人",null);
map.put("女朋友",null);
return map;
}
可以看到结果如下(null字段转成了空字符串):
3.3 封装统一返回的数据结构
在实际项目中,除了要封装数据外,往往需要在返回的JSON格式中添加一些其他信息,例如返回一些状态码code,返回一些msg给调用者,这样调用者可以根据code或者msg做一些逻辑判断。所以在实际项目中,我们需要封装一个统一的JSON返回结构用于存储返回信息。
定义统一JSON结构
由于封装的JSON数据类型不确定,所以在定义统一的JSON结构时,我们需要用到泛型。统一的JSON结构中属性包括数据、状态码、提示信息即可,构造方法可以根据实际业务需求做相应的添加,一般来说,应该有默认的返回结构,也应该有用户指定的返回结构,代码如下:
public class JsonResult<T> {
private T data;
private String code;
private String msg;
/**
* 若没有数据返回,默认状态码为0,提示信息为:操作成功!
*/
public JsonResult() {
this.code = "0";
this.msg = "操作成功!";
}
/**
* 若没有数据返回,可以人为指定状态码和提示信息
* @param code
* @param msg
*/
public JsonResult(String code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 有数据返回时,状态码为0,默认提示信息为:操作成功!
* @param data
*/
public JsonResult(T data) {
this.data = data;
this.code = "0";
this.msg = "操作成功!";
}
/**
* 有数据返回,状态码为0,人为指定提示信息
* @param data
* @param msg
*/
public JsonResult(T data, String msg) {
this.data = data;
this.code = "0";
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
修改Controller中的返回值类型及测试
由于JsonResult使用了泛型,所以所有的返回值类型都可以使用该统一结构,在具体的场景将泛型替换为具体的数据类型即可,非常方便,也便于维护。根据以上的JsonResult,修改Controller:
@RestController
@RequestMapping("/json")
public class JsonController {
@RequestMapping("/user")
public JsonResult<User> getUser(){
User user= new User(10,"林昊天","九龙湖M4");
return new JsonResult<>(user);
}
@RequestMapping("/list")
public JsonResult<List<User>>getUserList(){
List<User>userList=new ArrayList<>();
User user1=new User(1,"林昊天1号","九龙湖桃1");
User user2=new User(2,"林昊天2号","九龙湖橘1");
userList.add(user1);
userList.add(user2);
return new JsonResult<>(userList,"获取用户列表成功");
}
@RequestMapping("/map")
public JsonResult<Map<String,Object>>getMap(){
Map<String,Object>map=new HashMap<>(3);
User user=new User(4,"沈子怡1号","九龙湖梅5");
map.put("作者信息",user);
map.put("博客地址","abaaba");
map.put("公众号","sss");
map.put("爱人",null);
map.put("女朋友",null);
return new JsonResult<>(map);
}
}
结果如下:
//map
{"data":{"作者信息":{"id":4,"name":"沈子怡1号","address":"九龙湖梅5"},"博客地址":"abaaba","公众号":"sss","爱人":"","女朋友":""},"code":"0","msg":"操作成功!"}
//list
{"data":[{"id":1,"name":"林昊天1号","address":"九龙湖桃1"},{"id":2,"name":"林昊天2号","address":"九龙湖橘1"}],"code":"0","msg":"获取用户列表成功"}
//user
{"data":{"id":10,"name":"林昊天","address":"九龙湖M4"},"code":"0","msg":"操作成功!"}
通过封装,不但将数据通过JSON传给前端或者其他接口,还附上了状态码和提示信息,这在实际项目场景中用得非常广泛。
4、SpringBoot中的异常处理
在项目开发过程中,不管是对底层数据库的操作,还是业务层的处理,以及控制层的处理,都不可避免地会遇到各种可预知的、不可预知的异常需要处理。
@Controller
public class ExceptionController {
@RequestMapping("/exceptionMethod")
public String exceptionMethod(Model model){
model.addAttribute("msg","没有抛出异常");
int num = 1 / 0;
return "index";
}
}
4.1默认处理异常机制
在遇到异常时,SpringBoot会自动跳转到一个默认的异常页面
而做服务端的,浏览器并非为唯一客户端,还有安卓客户端app显示错误数据的方式,如下:(使用postman工具模拟安卓客户端app接受请求),Spring Boot会响应给安卓客户端json数据。
Spring Boot 默认提供了程序出错的结果映射路径/error。这个/error请求会在BasicErrorController中处理,其内部是通过判断请求头中的Accept的内容是否为text/html来区分请求是来自客户端浏览器(浏览器通常默认自动发送请求头内容Accept:text/html)还是客户端接口的调用,以此来决定返回页面视图还是 JSON 消息内容。
该类中有两个方法,errorHtml方法返回ModelAndView,这两个方法都能接收"/error"请求,error方法返回ResponseEntity也就是json数据,说明这两个方法对应的就是浏览器和客户端,errorHtml方法上的produces等于"text/html",就是响应html类型的数据,而在浏览器中优先接受的就是该类型,客户端优先接受的是application/json类型,所以Spring Boot就能自适应返回不同的数据了。
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
}
具体源码分析可参考:Spring Boot自适应定制错误数据原理
4.2 自定义错误页面
有模板引擎的情况下;error/状态码, 【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下】,发生此状态码的错误就会来到对应的页面。
我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html),其次才是templates目录下的error.html。
页面能获取的信息;timestamp:时间戳; status:状态码;error:错误提示;exception:异常对象; message:异常消息; errors:JSR303数据校验的错误都在这里
没有模板引擎(模板引擎找不到这个错误页面),默认静态资源文件夹下找;以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Springboot <span th:text="${status}"></span>错误页面</h1>
<br>
<span th:text="${message}"></span>
</body>
</html>
4.3 使用@ExceptionHandler注解处理局部异常
Spring MVC提供了@ExceptionHandler这个注解,再SpringBoot里我们可以用它做异常捕获。直接在对应的Controller里增加一个异常处理的方法,并使用@ExceptionHandler标识它即可,属于局部处理异常:
@Controller
public class ExceptionController {
@RequestMapping("/exceptionMethod")
public String exceptionMethod(Model model){
model.addAttribute("msg","没有抛出异常");
int num = 1 / 0;
return "index";
}
@ExceptionHandler({Exception.class})
public String handleNullPointerException(Exception e) {
System.out.println("局部异常处理:" + e.getMessage());
return "error";
}
}
@ExceptionHandler配置的value指定需要拦截的异常类型
这样只能做到单一的Controller异常处理,项目中一般都存在多个Controller,它们对于大多数异常处理的方法都大同小异,这样就得在每一个Controller里都编写一个对应的异常处理方法,所以不推荐使用。
4.4 使用@ControllerAdvice注册处理全局异常
实际开发中,需要对异常分门别类地进行处理,使用@ControllerAdvice+@ExceptionHandler注解能够处理全局异常,推荐使用这种方式,可以根据不同的异常对不同的异常进行处理。
使用方式:定义一个类,使用@ControllerAdvice注解该类,使用@ExceptionHandler注解方法。
记得注释掉局部异常的代码,优先匹配的是局部异常处理代码,其次才是全局异常处理代码。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler({ArithmeticException.class})
public String arithmeticExceptionHandler(ArithmeticException e) {
System.out.println("全局异常处理:" + e.getMessage());
return "error";
}
}
如果需要处理其他异常,如NullPointerException异常,只需在GlobalException类中定义一个方法使用@ExceptionHandler(value = {NullPointerException.class})注解该方法,在该方法内部处理异常就好了。
5、配置嵌入式Servlet容器
没有使用SpringBoot开发时,需要安装Tomcat环境,项目打包成War包后进行部署。而SpringBoot默认使用Tomcat作为嵌入式的Servlet容器。
5.1 如何定制和修改Servlet容器的相关配置
在内置的Tomcat中,不再有web,xml文件可供我们修改。在SpringBoot中修改Servlet容器相关的配置有两种方式可供选择,一种是在配置文件中修改,另一种是通过配置类的方式去修改。
在配置文件中修改(具体修改的参数可以查看ServerProperties类):
spring.mvc.date-format=yyyy-MM-dd
spring.thymeleaf.cache=false
spring.messages.basename=i18n.login
server.port=8081
server.servlet.context-path=/
server.tomcat.uri-encoding=utf-8
编写一个WebServerFactoryCustomizer:嵌入式的Servlet容器的定制器,来修改Servlet容器的配置:
@Configuration
public class MyMvcConfig {
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory>webServerFactoryWebServerFactoryCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8081);
}
};
}
}
5.2 注册Servlet三大组件——Servlet、Filter、Listener
一般情况下,使用Spring、Spring MVC等框架后,几乎不需要再使用Servlet、Filter、Listener了但有时再整合一些第三方框架时,可能还是不得不使用Servlet,如在整合报表插件时就需要使用Servlet。SpringBoot对整合这些基本的Web组件也提供了很好的支持。
由于SpringBoot默认是以Jar包的方式启动嵌入式的Servlet容器从而启动SpringBoot的Web应用,没有使用web.xml文件。所以可以用下面的方式在SpringBoot项目中添加三个组件:
@WebServlet("/servlet")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello MyServlet");
System.out.println("name:"+req.getParameter("name"));
}
}
@WebListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("web项目启动了。。。");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("web项目销毁了。。。");
}
}
@WebFilter("/")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter--init");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException, IOException {
System.out.println("myFilter--doFilter");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("MyFilter--destroy");
}
}
当然要使用三大组件的注解,就必须先在SpringBoot主配置类(即标注了@SpringBootApplication注解的类)上添加@ServletComponentScan注解,以实现对Servlet、Filter及Listener的扫描:
@ServletComponentScan
@SpringBootApplication
public class SpringdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringdemoApplication.class, args);
}
}
5.3替换为其他嵌入式Servlet容器
SpringBoot默认使用的是Tomcat,当然 也可以切换成其他容器,而且切换的方式也比较简单,只需引入其他容器的依赖将当前的依赖排除。
jetty比较适合做长连接的项目,例如聊天等这种一直要连接网络进行通信的项目。
想要将容器从Tomcat切换成jetty,可在pom.xml文件中导入相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions><!--移除Tomcat-->
<exclusion>
<groupId>spring-boot-starter-tomcat</groupId>
<artifactId>org.springframework.boot</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--引入其他的Servlet容器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
2.6 在SpringBoot中使用拦截器
SpringBoot延续了Spring MVC提供的AOP风格拦截器,拥有精细的拦截处理能力,在SpringBoot中拦截器的使用更为方便。这里用登录的例子展现拦截器的基本使用。拦截器用途广,可以对URL路径进行拦截,也可以用于权限验证、解决乱码、操作日志记录、性能监控、异常处理等。
一般用户登录功能我们可以这样实现:要么往session中写一个user,要么针对每一个user生成一个token。第二种生成方式更好,针对第二种方式,如果用户登录成功了,则每次请求的时候都会带上该用户的token;如果未登录成功,则没有该token,服务端可以通过检测这个token参数的有无来判断用户有没有登录成功,从而实现拦截功能。
public class LoginInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
String methodName = method.getName();
logger.info("====拦截到了方法:{},在该方法执行之前执行====", methodName);
// 判断用户有没有登陆,一般登陆之后的用户都有一个对应的token
String token = request.getParameter("token");
if (null == token || "".equals(token)) {
logger.info("用户未登录,没有权限执行……请登录");
return false;
}
// 返回true才会继续执行,返回false则取消当前请求
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("执行完方法之后进执行(Controller方法调用之后),但是此时还没进行视图渲染");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("整个请求都处理完咯,DispatcherServlet也渲染了对应的视图咯,此时我可以做一些清理的工作了");
}
}
每一个拦截器都需要实现HandlerInterceptor接口,实现这个接口有三种方法,每种方法会在请求调用的不同时期完成,因为我们需要在接口调用之前拦截请求并判断是否登录成功,所以这里需要使用preHandler方法,在里面写验证逻辑,最后返回true或false,确定请求是否合法。
通过配置类注册拦截器
创建一个配置类InterceptorConfig ,并实现WebMvcConfigurer 接口,覆盖接口中的addInterceptors方法,并为该配置类添加@Configuration注解,标注此类为一个配置类。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//需要拦截的路径,/**表示需要拦截所有请求
String[] addPathPatterns={"/**"};
//不需要拦截的路径
String [] excludePathPaterns={
"/login.html",
"/registry.html"
};
//注册一个登录拦截器
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns(addPathPatterns)
.excludePathPatterns(excludePathPaterns);
//注册一个权限拦截器 如果有多个拦截器 ,只需要添加以下一行代码
//registry.addInterceptor(new LoginInterceptor())
// .addPathPatterns(addPathPatterns)
// .excludePathPatterns(excludePathPatterns);
}
}
启动项目,在浏览器输入http://localhost:8081/interceptor/test后查看控制台日志,发现请求被拦截,如下所示:
如果输入http://localhost:8081/interceptor/test?token=123即可正常运行:
参考文章:【SpringBoot】SpringBoot学习笔记——SpringBoot整合web开发_shen子怡的博客-CSDN博客