现在各大技术社区 Spring Boot 的文章越来越多, Spring Boot 相关的图文, 视频教程越来越多, 使用 Spring Boot 的互联网公司也越来越多; Java 程序员现在出去面试, Spring Boot 已经成了必问的内容.
一切都在证明, Spring Boot 已经成为了 Java 程序员必备的技能. 并且可以预见的是未来 Spring Boot 的发展还会更好.
所以对 Java 程序员来说其中不乏说对 Spring Boot 非常熟悉的, 然后当问到一些 Spring Boot 核心功能和原理的时候, 没人能说得上来, 或者说不到点上, 可以说一个问题就问趴下了!(问题: 你能讲下为什么我们要用 Spring Boot 吗?)
相信我, 上面这些类似的问题, 90% 有经验的 Java 程序员超都曾遇见过! 但很少有系统化的回答.
因此, 总结了这份 Spring Boot 核心知识点实战教程, 通过这份教程, 带你梳理 Spring Boot 技术体系.
文末有彩蛋~
Spring Boot2 教程
在 Spring Boot 项目中, 正常来说是不存在 xml 配置, 这是因为 Spring Boot 不推荐使用 xml , 注意, 并非不支持, Spring Boot 推荐开发者使用 Java 配置来搭建框架, Spring Boot 中, 大量的自动化配置都是通过 Java 配置来实现的, 这一套实现方案, 我们也可以自己做, 即自己也可以使用纯 Java 来搭建一个 SSM 环境, 即在项目中, 不存在任何 xml 配置, 包括 web.xml .
环境要求:
使用纯 Java 来搭建 SSM 环境, 要求 Tomcat 的版本必须在 7 以上.
1, 创建工程
创建一个普通的 Maven 工程 (注意, 这里可以不必创建 Web 工程), 并添加 SpringMVC 的依赖, 同时, 这里环境的搭建需要用到 Servlet , 所以我们还需要引入 Servlet 的依赖 (一定不能使用低版本的 Servlet), 最终的 pom.xml 文件如下:
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
- <version>5.1.6.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-API</artifactId>
- <version>4.0.1</version>
- <scope>provided</scope>
- </dependency>
2 , 添加 Spring 配置
工程创建成功之后, 首先添加 Spring 的配置文件, 如下:
- @Configuration
- @ComponentScan(basePackages = "org.javaboy", useDefaultFilters = true,
- excludeFilters = {
- @ComponentScan.Filter(type = FilterType.ANNOTATION, classes =
- Controller.class)
- })
- public class SpringConfig {
- }
关于这个配置, 我说如下几点:
@Configuration 注解表示这是一个配置类, 在我们这里, 这个配置的作用类似于 applicationContext.xml
@ComponentScan 注解表示配置包扫描, 里边的属性和 xml 配置中的属性都是一一对应的, useDefaultFilters 表示使用默认的过滤器, 然后又除去 Controller 注解, 即在 Spring 容器中扫描除了 Controller 之外的其他所有 Bean .
3, 添加 SpringMVC 配置
接下来再来创建 springmvc 的配置文件:
- @Configuration
- @ComponentScan(basePackages = "org.javaboy",useDefaultFilters =
- false,includeFilters = {
- @ComponentScan.Filter(type =
- FilterType.ANNOTATION,classes = Controller.class)
- })
- public class SpringMVCConfig {
- }
注意, 如果不需要在 SpringMVC 中添加其他的额外配置, 这样就可以了. 即视图解析器, JSON 解析, 文件上传...... 等等, 如果都不需要配置的话, 这样就可以了.
4, 配置 Web.xml
此时, 我们并没 Web.xml 文件, 这时, 我们可以使用 Java 代码去代替 Web.xml 文件, 这里会用到 WebApplicationInitializer , 具体定义如下:
- public class WebInit implements WebApplicationInitializer {
- public void onStartup(ServletContext servletContext) throws ServletException
- {
- // 首先来加载 SpringMVC 的配置文件
- AnnotationConfigWebApplicationContext ctx = new
- AnnotationConfigWebApplicationContext();
- ctx.register(SpringMVCConfig.class);
- // 添加 DispatcherServlet
- ServletRegistration.Dynamic springmvc =
- servletContext.addServlet("springmvc", new DispatcherServlet(ctx));
- // 给 DispatcherServlet 添加路径映射
- springmvc.addMapping("/");
- // 给 DispatcherServlet 添加启动时机
- springmvc.setLoadOnStartup(1);
- }
- }
WebInit 的作用类似于 Web.xml, 这个类需要实现 WebApplicationInitializer 接口, 并实现接口中的方法, 当项目启动时, onStartup 方法会被自动执行, 我们可以在这个方法中做一些项目初始化操作, 例如加载 SpringMVC 容器, 添加过滤器, 添加 Listener, 添加 Servlet 等.
注意:
由于我们在 WebInit 中只是添加了 SpringMVC 的配置, 这样项目在启动时只会去加载 SpringMVC 容器, 而不会去加载 Spring 容器, 如果一定要加载 Spring 容器, 需要我们修改 SpringMVC 的配置, 在 SpringMVC 配置的包扫描中也去扫描 @Configuration 注解, 进而加载 Spring 容器, 还有一种方案可以解决这个问题, 就是直接在项目中舍弃 Spring 配置, 直接将所有配置放到 SpringMVC 的配置中来完成, 这个在 SSM 整合时是没有问题的, 在实际开发中, 较多采用第二种方案, 第二种方案, SpringMVC 的配置如下:
- @Configuration
- @ComponentScan(basePackages = "org.javaboy")
- public class SpringMVCConfig {
- }
这种方案中, 所有的注解都在 SpringMVC 中扫描, 采用这种方案的话, 则 Spring 的配置文件就可以删除了.
5, 测试
最后, 添加一个 HelloController , 然后启动项目进行测试:
- @RestController
- public class HelloController {
- @GetMapping("/hello")
- public String hello() {
- return "hello";
- }
- }
启动项目, 访问接口, 结果如下:
Spring Boot 全局异常处理
在 Spring Boot 项目中 , 异常统一处理, 可以使用 Spring 中 @ControllerAdvice 来统一处理, 也可以自己来定义异常处理方案. Spring Boot 中, 对异常的处理有一些默认的策略, 我们分别来看.
默认情况下, Spring Boot 中的异常页面 是这样的:
我们从这个异常提示中, 也能看出来, 之所以用户看到这个页面, 是因为开发者没有明确提供一个 / error 路径, 如果开发者提供了 /error 路径 , 这个页面就不会展示出来, 不过在 Spring Boot 中, 提供 / error 路径实际上是下下策, Spring Boot 本身在处理异常时, 也是当所有条件都不满足时, 才会去找 /error 路径. 那么我们就先来看看, 在 Spring Boot 中, 如何自定义 error 页面, 整体上来说, 可以分为两种, 一种是静态页面, 另一种是动态页面.
静态异常页面
自定义静态异常页面, 又分为两种, 第一种 是使用 HTTP 响应码来命名页面, 例如 404.html,405.HTML,500.HTML ...., 另一种就是直接定义一个 4xx.HTML, 表示 400-499 的状态都显示这个异常页面, 5xx.HTML 表示 500-599 的状态显示这个异常页面.
默认是在 classpath:/static/error/ 路径下定义相关页面:
此时, 启动项目, 如果项目抛出 500 请求错误, 就会自动展示 500.HTML 这个页面, 发生 404 就会展示 404.HTML 页面. 如果异常展示页面既存在 5xx.HTML, 也存在 500.HTML , 此时, 发生 500 异常时, 优先展示 500.HTML 页面.
动态异常页面
动态的异常页面定义方式和静态的基本 一致, 可以采用的页面模板有 jsp,freemarker,thymeleaf.
动态异常页面, 也支持 404.HTML 或者 4xx.HTML , 但是一般来说, 由于动态异常页面可以直接展示异常详细信息, 所以就没有必要挨个枚举错误了 , 直接定义 4xx.HTML(这里使用 thymeleaf 模板) 或者 5xx.HTML 即可.
注意, 动态页面模板, 不需要开发者自己去定义控制器, 直接定义异常页面即可 ,Spring Boot 中自带的异常处理器会自动查找到异常页面.
页面定义如下:
页面内容如下:
- <!DOCTYPE HTML>
- <HTML lang="en" xmlns:th="http://www.thymeleaf.org">
- <head>
- <meta charset="UTF-8">
- <title>
- Title
- </title>
- </head>
- <body>
- <h1>
- 5xx
- </h1>
- <table border="1">
- <tr>
- <td>
- path
- </td>
- <td th:text="${path}">
- </td>
- </tr>
- <tr>
- <td>
- error
- </td>
- <td th:text="${error}">
- </td>
- </tr>
- <tr>
- <td>
- message
- </td>
- <td th:text="${message}">
- </td>
- </tr>
- <tr>
- <td>
- timestamp
- </td>
- <td th:text="${timestamp}">
- </td>
- </tr>
- <tr>
- <td>
- status
- </td>
- <td th:text="${status}">
- </td>
- </tr>
- </table>
- </body>
- </HTML>
默认情况下, 完整的异常信息就是这 5 条, 展示 效果如下 :
如果动态页面和静态页面同时定义了异常处理页面, 例如 classpath:/static/error/404.HTML 和 classpath:/templates/error/404.HTML 同时存在时, 默认使用动态页面. 即完整的错误页面查找
方式应该是这样:
发生了 500 错误 --> 查找动态 500.HTML 页面 --> 查找静态 500.HTML --> 查找动态 5xx.HTML--> 查找静态 5xx.HTML.
自定义异常数据
默认情况下, 在 Spring Boot 中, 所有的异常数据其实就是上文所展示出来的 5 条数据, 这 5 条数据定义在 org.springframework.boot.Web.reactive.error.DefaultErrorAttributes 类中, 具体定义在 getErrorAttributes 方法中 :
- public Map<String, Object> getErrorAttributes(ServerRequest request,
- boolean includeStackTrace) {
- Map<String, Object> errorAttributes = new LinkedHashMap<>();
- errorAttributes.put("timestamp", new Date());
- errorAttributes.put("path", request.path());
- Throwable error = getError(request);
- HttpStatus errorStatus = determineHttpStatus(error);
- errorAttributes.put("status", errorStatus.value());
- errorAttributes.put("error", errorStatus.getReasonPhrase());
- errorAttributes.put("message", determineMessage(error));
- handleException(errorAttributes, determineException(error),
- includeStackTrace);
- return errorAttributes;
- }
DefaultErrorAttributes 类本身则是在
org.springframework.boot.autoconfigure.Web.servlet.error.ErrorMvcAutoConfiguration 异常自动配置类中定义的, 如果开发者没有自己提供一个 ErrorAttributes 的实例的话, 那么 Spring Boot 将自动提供一个 ErrorAttributes 的实例, 也就是 DefaultErrorAttributes .
基于此 , 开发者自定义 ErrorAttributes 有两种方式 :
直接实现 ErrorAttributes 接口
继承 DefaultErrorAttributes(推荐), 因为 DefaultErrorAttributes 中对异常数据的处理已经完成, 开发者可以直接使用.
具体定义如下:
- @Component
- public class MyErrorAttributes extends DefaultErrorAttributes {
- @Override
- public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean
- includeStackTrace) {
- Map<String, Object> map = super.getErrorAttributes(webRequest,
- includeStackTrace);
- if ((Integer)map.get("status") == 500) {
- map.put("message", "服务器内部错误!");
- }
- return map;
- }
- }
定义好的 ErrorAttributes 一定要注册成一个 Bean , 这样, Spring Boot 就不会使用默认的 DefaultErrorAttributes 了, 运行效果如下图:
自定义异常视图
异常视图默认就是前面所说的静态或者动态页面, 这个也是可以自定义的, 首先 , 默认的异常视图加载逻辑在 org.springframework.boot.autoconfigure.Web.servlet.error.BasicErrorController 类的 errorHtml 方法中, 这个方法用来返回异常页面 + 数据, 还有另外一个 error 方法, 这个方法用来返回异常数据 (如果是 Ajax 请求, 则该方法会被触发).
- @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);
- }
在该方法中 , 首先会通过 getErrorAttributes 方法去获取异常数据 (实际上会调用到 ErrorAttributes 的实例 的 getErrorAttributes 方法), 然后调用 resolveErrorView 去创建一个 ModelAndView , 如果这里创建失败, 那么用户将会看到默认的错误提示页面.
正常情况下, resolveErrorView 方法会来到 DefaultErrorViewResolver 类的 resolveErrorView 方法中:
- @Override
- public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
- status,
- Map<String, Object> model) {
- ModelAndView modelAndView = resolve(String.valueOf(status.value()),
- model);
- if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
- modelAndView = resolve(SERIES_VIEWS.get(status.series()),
- model);
- }
- return modelAndView;
- }
在这里, 首先以异常响应码作为视图名分别去查找动态页面和静态页面, 如果没有查找到, 则再以 4xx 或者 5xx 作为视图名再去分别查找动态或者静态页面.
要自定义异常视图解析, 也很容易 , 由于 DefaultErrorViewResolver 是在 ErrorMvcAutoConfiguration 类中提供的实例, 即开发者没有提供相关实例时, 会使用默认的 DefaultErrorViewResolver , 开发者提供了自己的 ErrorViewResolver 实例后, 默认的配置就会失效, 因此, 自定义异常视图, 只需要提供 一个 ErrorViewResolver 的实例即可:
- @Component
- public class MyErrorViewResolver extends DefaultErrorViewResolver {
- public MyErrorViewResolver(ApplicationContext applicationContext,
- ResourceProperties resourceProperties) {
- super(applicationContext, resourceProperties);
- }
- @Override
- public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
- status, Map<String, Object> model) {
- return new ModelAndView("/aaa/123", model);
- }
- }
实际上, 开发者也可以在这里定义异常数据 (直接在 resolveErrorView 方法重新定义一个 model , 将参数中的 model 数据拷贝过去并修改, 注意参数中的 model 类型为 UnmodifiableMap, 即不可以直接修改), 而不需要自定义 MyErrorAttributes. 定义完成后, 提供一个名为 123 的视图, 如下图:
如此之后, 错误试图就算定义成功了.
总结
实际上也可以自定义异常控制器 BasicErrorController , 不过我觉得这样太大动干戈了, 没必要, 前面几种方式已经可以满足我们的大部分开发需求了. 如果是前后端分离架构, 异常处理还有其他一些处理方案, 这个以后和大家聊.
篇幅有限, 其他内容就不在这里一一展示了, 这份 Spring Boot 实战教程已整理成一份 PDF 文档, 共有 200 多页.
关注公众号: 程序零世界, 回复 666 获取这份整理好的 Spring Boot 实战教程.
最后
欢迎大家一起交流, 喜欢文章记得点 ge 赞哟, 感谢支持!
来源: https://www.cnblogs.com/MonsterJ/p/13307490.html