目录
1.SpringMVC自动配置概览
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-developing-web-applications
link
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)
The auto-configuration adds the following features on top of Spring’s defaults:
- Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
- 内容协商视图解析器和BeanName视图解析器
- Support for serving static resources, including support for WebJars (covered later in this document)).
- 静态资源(包括webjars)
- Automatic registration of Converter, GenericConverter, and Formatter beans.
- 自动注册 Converter,GenericConverter,Formatter
- Support for HttpMessageConverters (covered later in this document).
- 支持 HttpMessageConverters (后来我们配合内容协商理解原理)
- Automatic registration of MessageCodesResolver (covered later in this document).
- 自动注册 MessageCodesResolver (国际化用)
- Static index.html support.
- 静态index.html 页支持
- Custom Favicon support (covered later in this document).
- 自定义 Favicon
- Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
- 自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)
- 不用@EnableWebMvc注解。使用 @Configuration + WebMvcConfigurer 自定义规则
- 声明 WebMvcRegistrations 改变默认底层组件
- 使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC
2.简单分析
2.1 静态资源访问
2.1.1 静态资源目录
只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources
访问 : 当前项目根路径/ + 静态资源名
原理: 静态映射/**。
请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面
2.1.2改变默认的静态资源路径(以后静态资源都在这里面)
resources:
static-locations: [classpath:/haha/]
2.1.3 静态资源访问前缀
spring:
mvc:
static-path-pattern: /res/**
这样可以区分开静态资源和动态资源
2.2 webjar
导入前端开发组件工具
自动映射 :/webjars/**
https://www.webjars.org/
2.2.1 导入jQuery
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径
2.2 自定义 Favicon(页面图标)
2.3 静态资源配置原理
- SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
- SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效
看见enable(启动)可知给容器配置了什么
置文件的相关属性和xxx进行了绑定。WebMvcProperties=spring.mvc、ResourceProperties=spring.resources
//有参构造器所有参数的值都会从容器中确定
//ResourceProperties resourceProperties;获取和spring.resources绑定的所有的值的对象
//WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象
//ListableBeanFactory beanFactory Spring的beanFactory
//HttpMessageConverters 找到所有的HttpMessageConverters
//ResourceHandlerRegistrationCustomizer 找到 资源处理器的自定义器。=========
//DispatcherServletPath
//ServletRegistrationBean 给应用注册Servlet、Filter....
2.3.1 资源处理的默认规则
yaml: resources:
add-mappings: false 禁用所有静态资源规则
3. 请求参数处理
3.1 请求映射
Rest风格:(使用HTTP请求方式动词来表示对资源的操作)
-
不使用:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
-
使用:/user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
-
核心Filter:WebMvcAutoConfiguration下的HiddenHttpMethodFilter
-
用法: 表单method=post,隐藏域 _method=put
-
SpringBoot中手动开启
3.1.1测试(只是表单提交时使用rest风格)
3.1.1.1 Controller
package com.node.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/1.jpg")
public String hello(){
return "2";
}
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
}
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
}
3.1.1.2 静态资源
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
测试REST风格:
<form action="/user" method="get">
<input value="REST-GET 提交" type="submit">
</form>
<form action="/user" method="post">
<input value="REST-POST 提交" type="submit">
</form>
<form action="/user" method="post">
<!--因为没有method没有delete提交方法,所以需要隐藏域_method-->
<input name="_method" type="hidden" value="DElETE">
<input value="REST-DELETE 提交" type="submit">
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT">
<input value="REST-PUT 提交" type="submit">
</form>
</body>
</html>
3.1.2 Rest原理(表单提交要使用REST的时候)
表单提交会带上_method=PUT
请求过来被HiddenHttpMethodFilter拦截
- 请求是否正常,并且是POST
- 获取到_method的值。
- 兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
Rest使用客户端工具,
• 如PostMan直接发送Put、delete等方式请求,无需Filter。
3.1.3 SpringMVC新注解
package com.node.demo.controller;
import org.springframework.web.bind.annotation.*;
@RestController
public class HelloController {
@RequestMapping("/1.jpg")
public String hello(){
return "2";
}
@GetMapping //@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-张三";
}
@PostMapping //@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-张三";
}
@PutMapping //@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-张三";
}
@DeleteMapping //@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-张三";
}
}
3.1.4 请求映射原理
SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet
–>doDispatch()
所有的请求映射都在HandlerMapping中。
- 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
- 如果有就找到这个请求对应的handler
- 如果没有就是下一个 HandlerMapping
- SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
- SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
3.2 普通参数与基本注解
3.2.1 注解
@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
@RequestPart:用在multipart/form-data表单提交请求的方法上(多数用于文件上传),@RequestParam和@RequestPart对比,@RequestPart适用于复杂的请求域(像JSON,XML)
3.2.1.1 测试
3.2.1.1.1 HelloController
package com.node.demo.controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class HelloController {
// car/2/owner/zhangsan
@GetMapping("/car/{id}/owner/{username}")
public Map<String,Object> getCar(@PathVariable("id") Integer id, //地址路径变量
@PathVariable("username") String name,
@PathVariable Map<String,String> pv,
@RequestHeader("User-Agent") String userAgent, //获取请求头
@RequestHeader Map<String,String> header,
@RequestParam("age") Integer age, //获取请求参数
@RequestParam("inters") List<String> inters,
@RequestParam Map<String,String> params,
@CookieValue("Idea-43d88c3f") String _ga){
/*
@MatrixVariable:矩阵变量:与@RequestParam类似,但是是对路径的修改。需要手动开启。
地址语法: /cars/sell;low=34;brand=byd,audi,yd ;下面的是参数
* */
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
map.put("pv",pv);
map.put("userAgent",userAgent);
map.put("headers",header);
map.put("age",age);
map.put("inters",inters);
map.put("params",params);
map.put("Idea-43d88c3f",_ga);
return map;
}
@PostMapping("/save")
public Map postMethod(@RequestBody String content){ //获取请求体【post】
Map<String,Object> map = new HashMap<>();
map.put("content",content);
return map;
}
}
3.2.1.1.2 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="car/2/owner/zhangsan?age=1&inters=game&inters=lla">car/{id}/owner/{username}</a>
<br/>
<form action="/save" method="post">
测试@RequestBody获取数据
<br/>
<p>用户名:<input name="username"/></p>
<p>密码:<input name="password"/></p>
<input type="submit" value="提交">
</form>
</body>
</html>
3.2.1.1.2 AttributeController
package com.node.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/*
@RestController:@RestController注解,相当于@Controller+@ResponseBody两个注解的结合,
返回json数据不需要在方法前面加@ResponseBody注解了,
但使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面
*/
@Controller
public class AttributeController {
@GetMapping("/goto")
public String goToPage(HttpServletRequest request){
request.setAttribute("msg","跳转成功");
request.setAttribute("code","200");
return "forward:/success";
}
@ResponseBody
@GetMapping("/success")
public Map success(@RequestAttribute("msg") String msg,
@RequestAttribute("code") Integer code,
HttpServletRequest request){
Object msg1=request.getAttribute("msg");
Map<String,Object> map=new HashMap<>();
map.put("msg",msg);
map.put("code",code);
map.put("msg1",msg1);
return map;
}
}
3.3 注解参数处理原理
先略
3.4 Thymeleaff
Thymeleaf是一个用于Web和独立环境的现代服务器端Java模板引擎,能够处理HTML、XML、JavaScript、CSS甚至纯文本。类似与jsp,简单来说是从后端代码的值传递到前端
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
表达式名字 | 语法 | 用途 |
---|---|---|
变量取值 | ${…} | 获取请求域、session域、对象 |
选择变量 | *{…} | $1600 |
消息 | #{…} | $1600 |
链接 | @{…} | $1600 |
片段表达式 | ~{…} | $1600 |
3.4.1 字面量
文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
空值: null
变量: one,two,… 变量不能有空格
3.4.2 文本操作
字符串拼接: +
变量替换: |The name is ${name}|
3.4.3 数学运算
运算符: + , - , * , / , %
3.4.4 布尔运算
运算符: and , or
一元运算: ! , not
3.4.5 比较运算
比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )
3.4.6 条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
3.4.7 特殊操作
无操作: _
3.4.8 设置属性值-th:attr
3.4.8.1 设置单个值
<form action="subscribe.html" th:attr="action=@{/subscribe}">
<fieldset>
<input type="text" name="email" />
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
</fieldset>
</form>
3.4.8.2 设置多个值
<img src="../../images/gtvglogo.png" th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
3.4.8.3 以上两个的代替写法 th:xxxx
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
<form action="subscribe.html" th:action="@{/subscribe}">
3.4.9 迭代
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
3.4.10 条件运算
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
3.4.11 th:fragment
3.4.12 测试
3.4.12.1 引入starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
3.4.12.2 application.yml
server:
servlet:
context-path: /static
3.4.12.3 ViewController
package com.node.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ViewController {
@GetMapping("/hha")
public String hha(Model model){
//model中的数据会被放在请求域中request.setAttribute ( "a " , aa)
model.addAttribute("msg","你好,哈哈");
model.addAttribute("link","http://www.baidu.com");
return "success";
}
}
3.4.12.4 success.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${msg}"></h1>
<h2>
<a th:href="${link}">去百度</a>
<a th:href="@{link}">去百度2</a>
</h2>
</body>
</html>
4.web原生组件注入(Servlet、Filter、Listener)
4.1 方法一:使用Servlet API
@ServletComponentScan(basePackages = “com.atguigu.admin”) :指定原生Servlet组件都放在那里
@WebServlet(urlPatterns = “/my”):效果:直接响应,没有经过Spring的拦截器
@WebFilter(urlPatterns={"/css/","/images/"})
@WebListener
4.1.1 创建项目
4.1.2 主程序
@ServletComponentScan(basePackages = "com.node.demo")
4.1.3 MyServlet
package com.node.demo.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("66666");
}
}
4.1.4 MyFilter
package com.node.demo.servlet;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@Slf4j
@WebFilter(urlPatterns = {"/css/*"}) //过滤器,过滤静态资源css下的资源(操作自己操作)
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("MyFilter初始化完成");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("MyFilter工作");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
log.info("MyFilter销毁");
}
}
4.1.5 MyservletContextListener
package com.node.demo.servlet;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@Slf4j
@WebListener
public class MyservletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("MyservletContextListener监听到项目初始化完成");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("MyservletContextListener监听到项目销毁");
}
}
4.2 方法二:使用RegistrationBean
4.2.1 注释
注释掉MyServlet、MyFilter、MyservletContextListener里面的
//@WebServlet(urlPatterns = “/my”)
//@WebFilter(urlPatterns = {"/css/*"})
//@WebListener
4.2.2 MyRegistConfig
package com.node.demo.servlet;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
@Configuration
public class MyRegistConfig {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter,myServlet());
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MyservletContextListener myservletContextListener = new MyservletContextListener();
return new ServletListenerRegistrationBean(myservletContextListener);
}
}
5.定制化原理
自己定制更改配置属性
5.1 常见方式
5.1.1 修改配置文件
application.properties、application.yml
5.1.2 xxxxxCustomizer:定制Servlet容器
xxxxxCustomizer:定制化器,可以改变xxxx的默认规则
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
把配置文件的值和ServletWebServerFactory 进行绑定
- 修改配置文件 server.xxx
- 直接自定义 ConfigurableServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
5.1.3 xxxConfiguration+ @Bean
编写自定义的配置类 xxxConfiguration;+ @Bean替换、增加容器中默认组件;视图解析器
5.1.4 Web应用 编写一个配置类实现 WebMvcConfigurer 即可定制化web功能;+ @Bean给容器中再扩展一些组件
重要
5.1.5 @EnableWebMvc + WebMvcConfigurer +@Bean
可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能
6.原理分析套路
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties – 绑定配置文件项
加粗部分需要我们自己操作,中间部分Spring boot帮我们配置好