该栏目会系统的介绍SpringBoot的知识体系,共分为基础部分、源代码和综合实例等模块
SpringMVC 自动配置
- 静态资源访问:包括index.html、Favicon及其其他静态资源
- 消息转换器:HttpMessageConverters
- 将请求数据绑定到JavaBean:ConfigurableWebBindingInitializer
- 格式转换器:Converter,GenericConverter,Formatter
- 视图解析器:BeanNameViewResolver
静态资源访问
1、概述
- :当被访问的静态资源放在
/static
、/public
、/resources
或 /META-INF/resources
可直接根据访问路径被访问
2、访问
- : 当前根路径
/
+ [ 访问前缀
] + 静态资源名
spring:
mvc:
static-path-pattern: /res/**
resources:
static-locations: [classpath:/path/]
3、支持欢迎页
- : 将
index.html
放在静态资源路径下(例如:/static/index.html)
4、支持自定义Favicon
- : 将
favicon.ico
放在静态资源路径下(例如:/static/favicon.ico)
5、访问原理

- SpringBoot 自动配置加载
WebMvcAutoConfiguration类
- WebMvcAutoConfiguration类 里有个
WebMvcAutoConfigurationAdapter内部类
用于加载相关资源 - WebMvcAutoConfigurationAdapter内部类 只有一个有参构造函数,其构造所需参数对象都从容器中获取
- WebMvcAutoConfigurationAdapter内部类 的
addResourceHandlers()
用来添加资源处理默认规则 - WebMvcAutoConfiguration类 里添加
WelcomePageHandlerMapping组件
用来处理欢迎页面
请求处理
1、请求访问案例
@RestController
public class UserController {
@GetMapping("user")
public String getUser() {
return "get";
}
@PostMapping("user")
public String postUser() {
return "post";
}
@DeleteMapping("user")
public String deleteUser() {
return "delete";
}
@PutMapping("user")
public String putUser() {
return "put";
}
}
spring:
mvc:
hiddenmethod:
filter:
enabled: true
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎页面</title>
</head>
<body>
<form action="/user" method="get">
<button type="submit">Get请求</button>
</form>
<form action="/user" method="post">
<button type="submit">post请求</button>
</form>
<form action="/user" method="post">
<input name="_method" value="DELETE" type="hidden"/>
<button type="submit">delete请求</button>
</form>
<form action="/user" method="post">
<input name="_method" value="PUT" type="hidden"/>
<button type="submit">put请求</button>
</form>
</body>
</html>
2、参数类型
注解 | 功能 |
---|
@PathVariable | 从Rest请求路径中获取参数 |
@RequestHeader | 请求头 |
@RequestAttribute | 获取request域属性 |
@RequestParam | 请求参数 |
@MatrixVariable | 矩阵变量 |
@CookieValue | 获取Cookie的参数 |
@RequestBody | 请求体 |
@RestController
public class RequestParamController {
@GetMapping("/getVariable/{id}")
public void getPathVariable(
@PathVariable("id") String id,
@PathVariable Map<String, String> params) {
System.out.println(params);
}
@GetMapping("/getHeader")
public void getRequestHeader(
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String, String> headers) {
System.out.println(headers);
}
@GetMapping("/getParam")
public void getParam(
@RequestParam("name") String name,
@RequestParam("inters") List<String> inters,
@RequestParam Map<String, String> params) {
System.out.println(params);
}
@GetMapping("/getCookie")
public void getCookie(
@CookieValue("JSESSIONID") String gc,
@CookieValue("JSESSIONID") Cookie cookie
) {
System.out.println(cookie);
}
@PostMapping("/getRequestBody")
public void getRequestBody(@RequestBody User user) {
System.out.println(user);
}
@GetMapping("/getRequestAttribute")
public void getRequestAttribute(@RequestAttribute("code") String code) {
System.out.println(code);
}
@GetMapping("/getMatrix1/{path}")
public void getMatrix1(@MatrixVariable("code") String code,
@MatrixVariable("var") String var) {
System.out.println(code + "--" + var);
}
@GetMapping("/getMatrix2/{path1}/{path2}")
public void getMatrix2(
@MatrixVariable(value = "code", pathVar = "path1") String code1,
@MatrixVariable(value = "code", pathVar = "path2") String code2) {
System.out.println(code1 + "--" + code2);
}
}
@Configuration
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper pathHelper = new UrlPathHelper();
pathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(pathHelper);
}
};
}
}
对象 | 描述 |
---|
ServletRequest | 请求域对象 |
MultipatRequest | 文件上传对象 |
HttpSession | Session域对象PushBuilder |
InputStream | 字节输入流对象 |
Reader | 字符输入流 |
HttpMethod | 请求方法对象 |
Map | Map集合对象 |
Model | 模型 |
RedirectAttributes | 用于重定向携带数据 |
ServletResponse | servlet响应对象 |
SessionStatus | session状态 |
自定义对象 | 用户自定义对象 |
@RestController
@RequestMapping("/object/")
public class RequestParamObject {
@GetMapping("getServletRequest")
public void getServletRequest(ServletRequest request, ServletResponse response) throws Exception {
request.setAttribute("code", "200");
request.getRequestDispatcher("/param/getAttribute").forward(request, response);
}
@GetMapping("getSession")
public void getSession(HttpSession session) {
System.out.println("session对象:" + session);
}
@GetMapping("getSessionStatus")
public void getSessionStatus(SessionStatus status) {
System.out.println("session状态对象:" + status);
}
@GetMapping("getServletResponse")
public void getServletResponse(ServletResponse response) throws IOException {
final PrintWriter writer = response.getWriter();
writer.write("Hello Java");
}
@GetMapping("getHttpMethod")
public void getHttpMethod(HttpMethod method) {
System.out.println("请求方法对象:" + method);
}
@GetMapping("getInputStream")
public void getInputStream(InputStream inputStream) {
System.out.println("字节输入流对象:" + inputStream);
}
@GetMapping("getReader")
public void getReader(Reader reader) {
System.out.println("字符输入流对象:" + reader);
}
@GetMapping("getMultipartFile")
public void getMultipartFile(MultipartFile multipartFile) {
System.out.println("文件上传对象:" + multipartFile);
}
@GetMapping("getMapObject")
public void getMapObject(@RequestParam Map<String, String> map) {
System.out.println("map对象:" + map);
}
@GetMapping("getModel")
public void getModel(Model model) {
System.out.println("Model对象:" + model);
}
@GetMapping("getRedirectAttributes")
public void getRedirectAttributes(RedirectAttributes redirectAttributes) {
System.out.println("redirectAttributes对象:" + redirectAttributes);
}
}
3、请求访问规定
- SpringBoot 启动时自动配置 WebMvcAutoConfiguration 类,在该类中注入了 OrderedHiddenHttpMethodFilter 过滤器组件,在该过滤器的过滤方法中规定了 RESTFUL 风格的请求规则
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
protected void doFilterInternal(){
}
}
}
}
4、请求映射
- 在 DispatcherServlet 类中的 doDispatch() 方法处理请求,最终通过 RequestMappingHandlerMapping 类,获取能处理该请求的处理器
public class DispatcherServlet extends FrameworkServlet {
protected void doService() {
doDispatch(request, response);
}
protected void doDispatch() {
mappedHandler = getHandler(processedRequest);
}
}
5、请求参数绑定
- 根据处理器获取处理器适配器, 使用处理器适配器遍历所有参数解析器,将处理器所需的参数与请求中携带的参数进行绑定
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch() {
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
}
public class RequestMappingHandlerAdapter {
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
}
}
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
}
}
public class InvocableHandlerMethod extends HandlerMethod {
public Object invokeForRequest(NativeWebRequest request,@Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
return doInvoke(args);
}
protected Object[] getMethodArgumentValues(NativeWebRequest request,
@Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request,
this.dataBinderFactory);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
}
6、响应处理
6.1 原理分析
- 调用能处理 @ResponseBody 注解的返回值处理器,返回值处理器遍历所有的消息转换器(Jackson2HttpMessageConverter),将对象转换为json对象
public class RequestMappingHandlerAdapter {
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
}
}
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException,HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
public abstract class AbstractMessageConverterMethodProcessor extends
AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler {
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
for (HttpMessageConverter<?> converter : this.messageConverters) {
}
}
}
6.2 自定义消息转换器
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseMessage implements Serializable {
private int code;
private String message;
}
public class ResponseMessageConverter implements HttpMessageConverter<ResponseMessage> {
@Override
public boolean canRead(Class<?> aClass, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> aClass, MediaType mediaType) {
return aClass.isAssignableFrom(ResponseMessage.class);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/x-near");
}
@Override
public ResponseMessage read(Class<? extends ResponseMessage> aClass,
HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(ResponseMessage responseMessage, MediaType mediaType,
HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
String message = responseMessage.getCode() + ";" + responseMessage.getMessage();
httpOutputMessage.getBody().write(message.getBytes());
}
}
@Configuration
public class WebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ResponseMessageConverter());
}
};
}
}
@RestController
@RequestMapping("/response/")
public class ResponseMessageController {
@GetMapping("responseMessage")
public ResponseMessage responseMessage() {
return new ResponseMessage(200, "Success!!");
}
}
7、视图解析
7.1 使用 Thymeleaf 框架
- 引入 Thymeleaf 场景
- 自动配置相关功能 SpringTemplateEngine、ThymeleafViewResolver
- 页面配置名称空间
7.2 原理解析
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch() {
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
protected void render(ModelAndView mv, HttpServletRequest request,
HttpServletResponse response) throws Exception {
}
}
拦截器
1、概述
2、原理解析
- 在 DispatcherServlet 类的 doDispatch() 方法调用 getHandler() 获取 HandlerExecutionChain 对象,该对象保存了处理请求的处理器和拦截器
- 先来顺序执行所有拦截器的 preHandle() 方法,如果当前拦截器prehandler()方法返回为 true。则执行下一个拦截器的 preHandle() 方法,如果当前拦截器返回为 false。直接倒序执行所有已经执行了的拦截器的 afterCompletion() 方法
- 所有拦截器都返回 true,则执行目标方法,目标完方法执行完后,倒序执行所有拦截器的 postHandle() 方法
- 前面的任何步骤有异常都会直接倒序触发 afterCompletion() 方法,页面成功渲染完成以后,也会倒序触发 afterCompletion() 方法

3、案例
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
final HttpSession session = request.getSession();
String loginName = (String) session.getAttribute("loginName");
if (loginName != null) {
return true;
}
request.setAttribute("msg","请先登录");
request.getRequestDispatcher("/").forward(request, response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
}
}
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/", "/login", "/css/**", "/html/**", "/images/**", "/js/**");
}
}
文件上传
1、原理分析
- SpringBoot 自动加载 MultipartAutoConfiguration 类,该类自动配置好了文件上传解析器(StandardServletMultipartResolver 类)
- 使用文件上传解析器判断并封装文件上传请求(MultipartHttpServletRequest 类)
- 参数解析器来解析请求中的文件内容封装成 MultipartFile 对象
- 将文件信息封装成一个 MultiValueMap<String, MultipartFile>,使用 FileCopyUtils 工具类实现文件流的拷贝
2、案例
@RestController
public class FileUploadController {
@GetMapping("/upload")
public String upload(@RequestPart("titleFile") MultipartFile titleFile,
@RequestPart("lives") MultipartFile[] lives) {
if (!titleFile.isEmpty()) {
try {
final String fileName = titleFile.getOriginalFilename();
titleFile.transferTo(new File("D:\\" + fileName));
} catch (IOException e) {
e.printStackTrace();
}
}
if (lives.length > 0) {
for (MultipartFile multipartFile : lives) {
try {
final String fileName = multipartFile.getOriginalFilename();
multipartFile.transferTo(new File("D:\\" + fileName));
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "main";
}
}
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
异常处理
1、异常处理规则
- 一旦控制器中报出了异常,SpringBoot 会以 /error的 ur l发送给 BasicExceptionController 控制器,然后跳转到默认显示异常的全局错误页面来展示异常信息
- 没有使用 ThymeLeaf 模板,SpringBoot 会到静态资源下 /error 文件夹下寻找错误提示页面(400.html、5xx.html)
- 使用 ThymeLeaf 模板时,SpringBoot 会自动到 templates/error/ 文件夹下寻找错误提示页面
2、异常处理原理
- SpringBoot自动加载
ErrorMvcAutoConfiguration类
配置异常处理规则 - 容器中加载
DefaultErrorAttributes类
,定义错误页面中可以包含哪些数据 - 容器中加载
BasicErrorController类
,处理错误信息,并响应错误页面或json数据 - 容器中加载
DefaultErrorViewResolver类
,寻找templates/error下静态文件夹下的错误提示页面(4xx、5xx等)进行响应
3、自定义全局异常处理
@ControllerAdvice
public class GlobalExceptionController {
@ExceptionHandler
public String handlerException(Model model, Exception e) {
model.addAttribute("message", e.getMessage());
return "error";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>系统内部异常</h1>
<h2 th:text="${message}"></h2>
</body>
</html>
原生 Servlet 组件
1、相关注解
- @ServletComponentScan:扫描原生Servlet组件
- @WebServlet:注解servlet组件
- @WebFilter:注解filter组件
- @WebListener:注解listener组件
2、案例
@WebServlet(urlPatterns = "/handler")
public class HandlerServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("处理请求...");
resp.getWriter().write("Hello Servlet");
}
}
@ServletComponentScan(basePackages = {"com.itnear.componet"})
@Configuration
public class WebServletConfig {
@Bean
public ServletRegistrationBean<HttpServlet> handlerServlet() {
WebServlet webServlet = new WebServlet();
return new ServletRegistrationBean<>(webServlet, "/web");
}
}
public class WebServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("处理请求");
resp.getWriter().write("Hello Web Servlet");
}
}
Web 容器
- SpringBoot 应用启动发现当前是 web 应用,web 应用会创建一个 web 版的 IOC 容器(ServletWebServerApplicationContext)
- 该容器启动时会加载 web 服务器工厂(ServletWebServerFactory)
- 因为我们引入的是 tomcat 的场景,所以该 web 服务工厂为 TomcatServletWebServerFactory ,接着创建 Tomcat 服务器并启动