2020应该建设什么网站,哪个网站可以做兼职讲师,网站建设结论,酷炫html5网站目录 一、SpringBoot的WEB开发
1.1 静态资源的处理
1.1.1 静态资源目录
1#xff09;SpringBoot静态资源处理
2#xff09;关于静态资源处理的配置
3#xff09;欢迎页面的处理
4#xff09;修改SpringBoot资源访问路径
1.1.2 WebJars资源
1.2 注册Servlet三大组件…目录 一、SpringBoot的WEB开发
1.1 静态资源的处理
1.1.1 静态资源目录
1SpringBoot静态资源处理
2关于静态资源处理的配置
3欢迎页面的处理
4修改SpringBoot资源访问路径
1.1.2 WebJars资源
1.2 注册Servlet三大组件
1.2.1 扫描原生组件
1.2.2 使用SpringBoot方式注册
1.3 SpringBoot项目配置
1.3.1 SpringBoot部署War项目
1.3.4 SpringBoot配置JSP
1 War项目部署JSP
2 Jar项目部署JSP
1.4 静态资源目录问题
1.4.1 关于classpath目录
1.4.2 关于/目录
1.4.3 关于JSP问题
1.5 SpringBoot项目打包
1.5.1 SpringBoot的Jar项目打包
1.5.2 SpringBoot的War项目打包
1.5.3 SpringBoot的Jar项目打包JSP文件
1.6 SpringBoot静态资源处理原理
1.2.1 WebMvcAutoConfiguration类
1.6.2 关于webjars资源
1分析源码
2定制webjars访问路径
1.6.3 关于静态资源
1分析源码
2根目录问题
二、SpringBoot错误处理
2.1 SpringBoot错误处理规则
2.1.1 定制错误页面
2.2.2 定制错误json数据响应
1定义异常处理器
2转发error请求
2.2.3 定制错误数据封装属性类
2.2.4 定制错误处理请求处理类
2.2 SpringBoot 错误处理原理
2.2.1 ErrorMvcAutoConfiguration源码分析
2.2.2 ErrorController源码分析
1BasicErrorController源码分析-视图解析
2BasicErrorController源码分析-数据封装
3自定义ErrorController
2.2.3 ErrorAttribute源码分析
1DefaultErrorAttributes源码分析
2自定义ErrorAttribute 一、SpringBoot的WEB开发 1.1 静态资源的处理 我们之前在学习SpringMVC时需要配置静态资源的放行SpringBoot的WEB场景启动器中已经集成了SpringMVC在 SpringBoot 中则不再需要进行此项配置SpringBoot已经默认完成了这一工作。 SpringBoot父工程 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.0.1.RELEASE/version
/parentdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency
/dependencies 1.1.1 静态资源目录 1SpringBoot静态资源处理 当访问项目中的任意资源时Spring Boot 会默认从以下路径中查找资源文件优先级依次降低 1classpath:/META-INF/resources/
2classpath:/resources/
3classpath:/static/
4classpath:/public/
5)/ Tips根(/)目录比较特殊我们后面再具体说明暂时先分析4个目录 当SpingBoot接收到静态资源的请求时会默认去以上4个路径寻找需要的静态资源 访问http://localhost:8080/hello.html 2关于静态资源处理的配置 访问SpringBoot在所有的资源/**时默认会去5个静态资源目录寻找我们可以更改静态资源目录也可以禁用SpringBoot默认静态资源访问规则由我们来自定义 1修改SpringBoot资源访问目录 server:port: 8080
spring:resources:# 更改静态资源目录static-locations: # 默认为/META-INF/resources/、/resources/、/static/、/public/、/- classpath:/aaa- classpath:/bbb 静态资源目录修改为aaa、bbb之后原来的4个目录只有META-INF/resources目录有效其他三个目录均无效 优先级为aaabbbMETA-INF/resources 3欢迎页面的处理 SpringBoot默认会去4个静态资源static-locations目录下寻找index.html页面 Tips访问静态首页或欢迎页时其查找顺序也遵循默认静态资源的查找顺序即先查找优先级高的目录在查找优先级低的目录直到找到 index.html 为止。 设置欢迎页面 4修改SpringBoot资源访问路径 server:port: 8080
spring:mvc:# 访问/views/** 下面的所有请求才去静态目录下面寻找(static-locations) 默认为 /**static-path-pattern: /views/** 注意如果修改了SpringBoot默认的静态资源访问目录那么欢迎页面将会失效 修改SpringBoot资源访问目录 server:port: 8080
spring:mvc:# 访问/views/** 下面的所有请求才去静态目录下面寻找(static-locations) 默认为 /**static-path-pattern: /views/**resources:# 更改静态资源目录 默认为/META-INF/resources/、/resources/、/static/、/public/、/static-locations:- classpath:/aaa- classpath:/bbb# 禁用SpringBoot的静态资源访问规则(static-path-pattern和static-locations都不生效了)add-mappings: true 1.1.2 WebJars资源 为了让页面更加美观让用户有更多更好的体验Web 应用中通常会使用大量的 JS 和 CSS例如 jQueryBackbone.js 和 Bootstrap 等等。通常我们会将这些 Web 前端资源拷贝到 Java Web 项目的 webapp 相应目录下进行管理。但是 Spring Boot 项目是以 JAR 包的形式进行部署的不存在 webapp 目录那么 Web 前端资源该如何引入到 Spring Boot 项目中呢 WebJars 可以将 Web 前端资源JSCSS 等打成一个个的 Jar 包然后将这些 Jar 包部署到 Maven 中央仓库中进行统一管理当 Spring Boot 项目中需要引入 Web 前端资源时只需要访问 WebJars 官网找到所需资源的 pom 依赖将其导入到项目中即可。 WebJars官网WebJars - Web Libraries in Jars 在SpringBoot中所有 /webjars/** 的请求 都去 classpath:/META-INF/resources/webjars/ 找资源 dependencygroupIdorg.webjars/groupIdartifactIdjquery/artifactIdversion3.6.0/version
/dependency 将jquery的webjars资源引入到项目中后查看项目的依赖路径 访问http://localhost:8080/webjars/jquery/3.6.0/jquery.js 1.2 注册Servlet三大组件 SpringBoot默认是以jar包的方式启动嵌入式的Tomcat来启动SpringBoot的web应用并没有web.xml并且所有的WEB资源全部被SpringBoot所接管我们直接编写Servlet、WebFilter、Listener并不会生效 SpringBoot提供了多种注册原生Servlet组件的方法 Servlet package com.dfbz.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;/*** author lscl* version 1.0* intro:*/
WebServlet(/test)
public class TestServlet extends HttpServlet {Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println(servlet running...);resp.getWriter().write(hello servlet!);}
} TestFilter package com.dfbz.filter;import org.springframework.stereotype.Component;import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;/*** author lscl* version 1.0* intro:*/
WebFilter(/*)
public class TestFilter implements Filter {Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println(test filter...);}Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println(before testFilter...);chain.doFilter(request,response);System.out.println(after testFilter...);}Overridepublic void destroy() {System.out.println(destroy filter...);}
} Listener package com.dfbz.listener;import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;/*** author lscl* version 1.0* intro:*/
WebListener
public class TestListener implements ServletContextListener {Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println(contextInitialized...);}Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println(contextDestroyed);}
} 1.2.1 扫描原生组件 我们可以使用原生的servlet注解来帮助我们配置三大组件但是需要交给SpringBoot进行三大注解的组件扫描 在启动类中标注ServletComponentScan package com.dfbz;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;/*** author lscl* version 1.0* intro:*/
SpringBootApplication
ServletComponentScan // 扫描启动类的子包(孙包等)下的Servlet原生三大组件
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class);}
} 1.2.2 使用SpringBoot方式注册 SpringBoot提供了一系列用于注册三大原生组件的Bean 注册ServletServletRegistrationBean
注册FilterFilterRegistrationBean
注册ListenerServletListenerRegistrationBean package com.dfbz;import com.dfbz.filter.TestFilter;
import com.dfbz.listener.RequestListener;
import com.dfbz.servlet.TestServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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 java.util.Arrays;/*** author lscl* version 1.0* intro:*/
SpringBootApplication
//ServletComponentScan // 扫描启动类的子包(孙包等)下的Servlet原生三大组件
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class);}/*** 注册Servlet* return*/Beanpublic ServletRegistrationBean testServlet(){ServletRegistrationBean testServletBean new ServletRegistrationBean(new TestServlet(),/test);return testServletBean;}/*** 注册Filter* return*/Beanpublic FilterRegistrationBean testFilter(){FilterRegistrationBean testFilterBean new FilterRegistrationBean();testFilterBean.setFilter(new TestFilter());testFilterBean.setUrlPatterns(Arrays.asList(/*));return testFilterBean;}/*** 注册Listener* return*/Beanpublic ServletListenerRegistrationBean testListener(){ServletListenerRegistrationBeanTestListener testListenerBean new ServletListenerRegistrationBean(new TestListener());return testListenerBean;}
} Tips使用了注册Bean的方式注册三大组件不需要添加WebServlet等注解 1.3 SpringBoot项目配置 1.3.1 SpringBoot部署War项目 我们之前项目并没有使用Tomcat来部署是因为web的场景启动器中含有内置的tomcat并且项目的打包方式为Jar。实际开发也可能会将项目打war包将项目部署到独立的Tomcat中 SpringBoot项目打包成war包部署到Tomcat运行需要添加一个Tomcat引导器 package com.dfbz;import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Configuration;Configuration
public class TomcatInitializer extends SpringBootServletInitializer {Overrideprotected SpringApplicationBuilder configure(SpringApplicationBuilder application) {//传入SpringBoot应用的主程序return application.sources(Application.class);}
} 我们的静态资源可以放在webapp目录下也可以放在SpringBoot的静态资源目录中。 项目结构如下 从原则上来讲我们可以使用引导类来启动项目也可以和我们以前一样使用IDEA中的Tomcat插件部署项目但是由于idea的原因如果使用引导类来启动默认情况下是不会打包webapp目录的而使用tomcat来启动才会打包webapp目录 使用clean命令清空项目然后使用引导类来启动项目观察编译后的target目录 我们查看target目录发现并没有打包webapp目录中的文件这个时候使用浏览器访问http://localhost:8080/demo02.html肯定是访问不了的 使用clean命令清空项目然后使用tomcat来启动项目观察编译后的target目录 此时在浏览器访问http://localhost:8080/demo02.html发现可以正常访问 1.3.4 SpringBoot配置JSP SpringBoot配置JSP时项目的打包方式不同配置方式也不同 1 War项目部署JSP 如果打包方式是war那么JSP直接放在webapp目录即可其他4个静态目录存放的JSP不能被访问 创建一个war项目 使用Tomcat启动项目访问 访问http://localhost:8080/demo01.jsp失败 访问http://localhost:8080/demo02.jsp失败 访问http://localhost:8080/demo03.jsp失败 访问http://localhost:8080/demo04.jsp失败 访问http://localhost:8080/demo05.jsp成功 2 Jar项目部署JSP 打包方式为Jar时只有/META-INF/resources目录可以部署JSP文件 创建一个新的jar项目项目结构如下 由于jar项目是内置的Tomcat要引入SpringBoot对内置Tomcat的JSP解析支持 dependencygroupIdorg.apache.tomcat.embed/groupIdartifactIdtomcat-embed-jasper/artifactId
/dependency JSP热部署配置 server:servlet:jsp:init-parameters:development: true # 开启JSP热部署 访问http://localhost:8080/demo01.jsp成功 访问http://localhost:8080/demo02.jsp失败 访问http://localhost:8080/demo03.jsp失败 访问http://localhost:8080/demo04.jsp失败 1.4 静态资源目录问题 SpringBoot将访问/**的请求对接到了以下5个目录 classpath:/META-INF/resources
classpath:/resources
classpath:/static
classpath:/public
/ 1.4.1 关于classpath目录 前面4个目录都在classpath下那具体什么是classpath目录呢严格来说classpath指的并不是maven项目的resources目录而是项目编译完成的classes目录如下 只不过maven项目中的resources目录下的资源都会被打包到classes目录下另外我们还可以通过配置让其他目录的内容也打包到classpath下。 我们可以在maven中添加打包配置 buildresourcesresource!--打包/src/main/resources/META-INF/resource目录下的资源--directorysrc/main/resources/abc/directory!--打包到classpath目录下的static目录--targetPathstatic/targetPath/resource/resources
/build Tips 1build标签中配置的是当项目打包时将src/main/resources/abc目录打包到classpath下的static目录而启动引导类并不会打包项目我们必须手动执行maven的package命令才能够将项目打包
2build标签配置了之后maven的目录打包规则会以build标签中配置的为准以前的默认打包规则就没有了即只会打包abc目录中的内容到static目录其他目录都不进行打包 这样一来abc目录的资源将会被打包到static目录了也就可以被SpringBoot正常访问了 Tips测试完毕记得注释build标签的内容否则会影响后面的测试 另外classpath的目录并不是固定的Jar项目和War项目以及idea打包和maven的package打包的classpath位置都不一样但是不管classpath的位置在哪只要是maven项目/src/main/resources目录的内容总是会被打包到classpath。 1.4.2 关于/目录 5个静态目录中最后一个目录是//代表的是根目录的意思具体根目录是哪里War项目和Jar项目不同 在Jar项目中根目录为classpath:/MEAT-INF/resources 在War项目中根目录为项目名 可以看到war项目将webapp目录下的资源都打包到了根目录/因此war项目中5个目录都可以正常放置我们的静态资源 1.4.3 关于JSP问题 前面我们部署JSP时规则如下 1在Jar项目中JSP必须放置在MEAT-INF/resources目录这是因为在Jar项目中/MEAT-INF/resources最终会被打包到根目录/SpringBoot对根目录做了特殊处理让其可以访问JSP但是其他4个目录并没有
2在War项目中JSP必须放置在webapp目录因为在War项目中webapp目录中的内容最终会被打包到根目录/ 1.5 SpringBoot项目打包 1.5.1 SpringBoot的Jar项目打包 我们使用maven的package命令对我们刚刚项目进行打包发现只会有我们自己编写的代码并没有将所依赖的SpringBoot那些jar包打包到项目来这样的项目肯定是启动不了的 将任意一个jar项目打包查看包的内部结构 只有我们自己编写的代码并没有打包SpringBoot相关jar包这样的一个项目肯定是运行不起来的 SpringBoot的打包插件就是帮助我们打包SpringBoot项目的该插件不仅会将我们编写的代码打包进来而且也会把所依赖的SpringBoot那些jar也都统统打包到我们的项目中使我们的项目变成一个可以独立运行的项目 引入SpringBoot打包插件 buildplugins!--SpringBoot打包插件--plugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdversion1.4.2.RELEASE/version/plugin/plugins
/build TipsJar项目使用maven的package打成Jar包后classpath目录位置在BOOT-INF/classes根目录还是在MEAT-INF/resources 1.5.2 SpringBoot的War项目打包 接着我们尝试打包web项目观察打包的项目结构 我们知道/代表根目录在War项目打包时webapp目录的内容将会被打包到根目录/ TipsJar项目使用maven的package打成Jar包后classpath目录位置在WEB-INF/classes根目录是项目名 1.5.3 SpringBoot的Jar项目打包JSP文件 我们知道只有根目录放置的JSP文件才可以被正常访问而Jar项目中只有src/main/resources/MEAT-INF/resources目录中的内容才可以被打包到根目录因此正常情况下我们的JSP都必须放在该目录那如果我们自己定义一个webapp目录是否会被打包到根目录呢答案是不能的。因为webapp目录只有在war项目中才会被打包到根目录 观察下面项目打包案例 但是我们可以告诉maven这个/src/main/webapp目录下的资源我们也需要打包 buildresourcesresource!--打包src/main/webapp目录下的资源--directorysrc/main/webapp/directory!--打包到jar包中的META-INF/resources目录下--targetPathMETA-INF/resources/targetPath/resource!--更改了maven的打包路径之后,static目录也需要手动指定打包路径--resourcedirectorysrc/main/resources/static/directorytargetPathBOOT-INF/classes/static/targetPath/resource/resourcesplugins!--SpringBoot打包插件--plugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdversion1.4.2.RELEASE/version/plugin/plugins
/build Tips我们更改了Maven的打包路径之后Maven自己的默认的打包规则将会失效此时如果我们有其他的静态资源编写在SpringBoot提供的那4个目录中则需要一起指定打包 执行打包package命令后查看jar包中的文件 此时我们将项目中的webapp目录下的资源打包到了/META-INF/resource目录下而该目录正是Jar项目的根目录 而实际开发不管是/src/main/webapp目录还是SpringBoot提供的四个目录我们都会将其打包在一起此时这5个目录不允许存在同名文件会覆盖 buildresourcesresource!--打包src/main/webapp目录下的资源--directorysrc/main/webapp/directory!--打包到jar包中的META-INF/resources目录下--targetPathMETA-INF/resources/targetPath/resourceresourcedirectorysrc/main/resources/META-INF/resources/directorytargetPathMETA-INF/resources/targetPath/resourceresourcedirectorysrc/main/resources/resources/directorytargetPathMETA-INF/resources/targetPath/resourceresourcedirectorysrc/main/resources/static/directorytargetPathMETA-INF/resources/targetPath/resourceresourcedirectorysrc/main/resources/public/directorytargetPathMETA-INF/resources/targetPath/resource/resourcesplugins!--SpringBoot打包插件--plugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdversion1.4.2.RELEASE/version/plugin/plugins
/build 重新打包观察打包之后的目录结构 1.6 SpringBoot静态资源处理原理 1.2.1 WebMvcAutoConfiguration类 我们通过上面的章节已经学习到SpringBoot任何的 /** 请求都会映射到其静态资源目录4个任何为 /webjars/** 请求都会来到项目中的 /META-INF/resources/webjars/** 目录那么SpringBoot关于SpringMVC的配置有哪些呢 关于SpringBoot的SpringMVC相关配置大都在WebMvcAutoConfiguration配置类中 Configuration
ConditionalOnWebApplication(type Type.SERVLET)
ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE 10)
AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration { 在WebMvcAutoConfiguration类中有一个重要的内部类WebMvcAutoConfigurationAdapter在该类中配置了大量SpringMVC相关的一些基础配置其中还关联了WebMvcProperties.class、ResourceProperties.class对SpringMVC进行了大量的配置初始化 WebMvcAutoConfiguration源码如下 // 标注当前类是一个配置类
Configuration// 必须要在WEB环境下才会生效
ConditionalOnWebApplication(type Type.SERVLET)// 必须引入SpringMVC/Servlet相关依赖才会生效
ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })// 当前IOC容器里面必须没有WebMvcConfigurationSupport这个Bean的时候生效
ConditionalOnMissingBean(WebMvcConfigurationSupport.class)// 配置类顺序
AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE 10)// 必须等到DispatcherServletAutoConfiguration和ValidationAutoConfiguration加载完毕后才会生效
AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {public static final String DEFAULT_PREFIX ;public static final String DEFAULT_SUFFIX ;private static final String[] SERVLET_LOCATIONS { / };// SpringMVC解决表单提交put/delete问题BeanConditionalOnMissingBean(HiddenHttpMethodFilter.class)public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();}// 表单内容校验BeanConditionalOnMissingBean(HttpPutFormContentFilter.class)ConditionalOnProperty(prefix spring.mvc.formcontent.putfilter, name enabled, matchIfMissing true)public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {return new OrderedHttpPutFormContentFilter();}// 标注是一个配置类ConfigurationImport(EnableWebMvcConfiguration.class)EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })Order(0)public static class WebMvcAutoConfigurationAdapterimplements WebMvcConfigurer, ResourceLoaderAware {// 配置信息转换的转换器Overridepublic void configureMessageConverters(ListHttpMessageConverter? converters) {converters.addAll(this.messageConverters.getConverters());}// 配置请求异步处理Overridepublic void configureAsyncSupport(AsyncSupportConfigurer configurer) {Duration timeout this.mvcProperties.getAsync().getRequestTimeout();if (timeout ! null) {configurer.setDefaultTimeout(timeout.toMillis());}}// 配置统一路径Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());configurer.setUseRegisteredSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());}// 配置内容协商处理Overridepublic void configureContentNegotiation(ContentNegotiationConfigurer configurer) {WebMvcProperties.Contentnegotiation contentnegotiation this.mvcProperties.getContentnegotiation();configurer.favorPathExtension(contentnegotiation.isFavorPathExtension());configurer.favorParameter(contentnegotiation.isFavorParameter());if (contentnegotiation.getParameterName() ! null) {configurer.parameterName(contentnegotiation.getParameterName());}MapString, MediaType mediaTypes this.mvcProperties.getContentnegotiation().getMediaTypes();mediaTypes.forEach(configurer::mediaType);}// 配置视图解析器BeanConditionalOnMissingBeanpublic InternalResourceViewResolver defaultViewResolver() {InternalResourceViewResolver resolver new InternalResourceViewResolver();resolver.setPrefix(this.mvcProperties.getView().getPrefix());resolver.setSuffix(this.mvcProperties.getView().getSuffix());return resolver;}// 配置BeanNameViewResolverBeanConditionalOnBean(View.class)ConditionalOnMissingBeanpublic BeanNameViewResolver beanNameViewResolver() {BeanNameViewResolver resolver new BeanNameViewResolver();resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);return resolver;}// 注册ConverterOverridepublic void addFormatters(FormatterRegistry registry) {for (Converter?, ? converter : getBeansOfType(Converter.class)) {registry.addConverter(converter);}for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {registry.addConverter(converter);}for (Formatter? formatter : getBeansOfType(Formatter.class)) {registry.addFormatter(formatter);}}private T CollectionT getBeansOfType(ClassT type) {return this.beanFactory.getBeansOfType(type).values();}// 添加资源映射Overridepublic 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/** 请求来到 classpath:/META-INF/resources/webjars/ 目录下if (!registry.hasMappingForPattern(/webjars/**)) {customizeResourceHandlerRegistration(registry.addResourceHandler(/webjars/**).addResourceLocations(classpath:/META-INF/resources/webjars/).setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));}// 获取到 spring.mvc.staticPathPattern 配置(默认为/**)String staticPathPattern this.mvcProperties.getStaticPathPattern();if (!registry.hasMappingForPattern(staticPathPattern)) {customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)/* 将此路径映射到: spring.resources.staticLocationsspring.resources.staticLocations 默认为: {classpath:/META-INF/resources/, classpath:/resources/,classpath:/static/, classpath:/public/ }*/.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));}}private Integer getSeconds(Duration cachePeriod) {return (cachePeriod null ? null : (int) cachePeriod.getSeconds());}// 欢迎页面映射Beanpublic WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext) {return new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext),applicationContext, getWelcomePage(),this.mvcProperties.getStaticPathPattern());}// 在staticLocations的基础上又加上了/,/代表webapp目录下的资源也可以被访问static String[] getResourceLocations(String[] staticLocations) {String[] locations new String[staticLocations.length SERVLET_LOCATIONS.length];System.arraycopy(staticLocations, 0, locations, 0, staticLocations.length);// SERVLET_LOCATIONS/System.arraycopy(SERVLET_LOCATIONS, 0, locations, staticLocations.length,SERVLET_LOCATIONS.length);return locations;}private OptionalResource getWelcomePage() {String[] locations getResourceLocations(this.resourceProperties.getStaticLocations());// 获取staticLocations配置的4个映射路径return Arrays.stream(locations).map(this::getIndexHtml) // 在四个类路径下寻找index.html.filter(this::isReadable) // 过滤掉不存在的.findFirst(); // 只要第一个}private Resource getIndexHtml(String location) {return this.resourceLoader.getResource(location index.html);}private boolean isReadable(Resource resource) {try {return resource.exists() (resource.getURL() ! null);}catch (Exception ex) {return false;}}// 欢迎图标的映射ConfigurationConditionalOnProperty(value spring.mvc.favicon.enabled, matchIfMissing true)public static class FaviconConfiguration implements ResourceLoaderAware {private final ResourceProperties resourceProperties;private ResourceLoader resourceLoader;public FaviconConfiguration(ResourceProperties resourceProperties) {this.resourceProperties resourceProperties;}Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader resourceLoader;}Beanpublic SimpleUrlHandlerMapping faviconHandlerMapping() {SimpleUrlHandlerMapping mapping new SimpleUrlHandlerMapping();mapping.setOrder(Ordered.HIGHEST_PRECEDENCE 1);mapping.setUrlMap(Collections.singletonMap(**/favicon.ico,faviconRequestHandler()));return mapping;}Beanpublic ResourceHttpRequestHandler faviconRequestHandler() {ResourceHttpRequestHandler requestHandler new ResourceHttpRequestHandler();requestHandler.setLocations(resolveFaviconLocations());return requestHandler;}private ListResource resolveFaviconLocations() {String[] staticLocations getResourceLocations(this.resourceProperties.getStaticLocations());ListResource locations new ArrayList(staticLocations.length 1);Arrays.stream(staticLocations).map(this.resourceLoader::getResource).forEach(locations::add);locations.add(new ClassPathResource(/));return Collections.unmodifiableList(locations);}}}
} 1.6.2 关于webjars资源 1分析源码 查看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();if (!registry.hasMappingForPattern(/webjars/**)) {// 如果访问的是/webjars/** 则去 /META-INF/resources/webjars/ 目录下寻找资源customizeResourceHandlerRegistration(registry.addResourceHandler(/webjars/**).addResourceLocations(classpath:/META-INF/resources/webjars/).setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));}后面代码省略....
} 2定制webjars访问路径 package com.dfbz;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** author lscl* version 1.0* intro:*/
Configuration
//EnableWebMvc // 该注解表示定制SpringMVC,即SpringBoot配置的SpringMVC相关组件全部失效
public class MyWebMvcAutoConfiguration implements WebMvcConfigurer {Value(${spring.mvc.my-webjars-locaitons})private String locations;Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {// 与SpringBoot默认的webjars目录并不是互补配置registry.addResourceHandler(/webjars/**).addResourceLocations(locations);}
} Tips配置之后SpringBoot默认的webjars资源路径将会失效 application.yml server:port: 8080
spring:mvc:my-webjars-locaitons: classpath:/webjars/ 分别在classpath目录下的/webjars、/static目录添加资源 访问 http://localhost:8080/demo01.js http://localhost:8080/demo02.js 1.6.3 关于静态资源 1分析源码 在WebMvcAutoConfiguration类的addResourceHandlers方法下半部分代码是关于其他静态资源处理的 Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {前半部分代码省略....// 获取SpringBoot默认的访问路径(/**)String staticPathPattern this.mvcProperties.getStaticPathPattern();if (!registry.hasMappingForPattern(staticPathPattern)) {customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern) // 如果访问 /**.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())) // 就去这里找资源(5个目录).setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));}
} getResourceLocations方法主要是将根(/)目录加入到SpringBoot的4个静态资源目录中成为5个静态资源目录 static String[] getResourceLocations(String[] staticLocations) {// SERVLET_LOCATIONSString[] locations new String[staticLocations.length SERVLET_LOCATIONS.length];System.arraycopy(staticLocations, 0, locations, 0, staticLocations.length);System.arraycopy(SERVLET_LOCATIONS, 0, locations, staticLocations.length,SERVLET_LOCATIONS.length);return locations;
} 2根目录问题 我们之前分析过当修改了SpringBoot的默认静态资源目录后唯独/META-INF/resources资源仍然可以被访问这是为什么呢 修改application.yml server:port: 8080
spring:resources:# 更改静态资源目录static-locations: # 默认为/META-INF/resources/、/resources/、/static/、/public/、/- classpath:/aaa- classpath:/bbb 分析WebMvcAutoConfiguration源码 查看jar包的targer目录 正因为根(/)目录是classpath:/META-INF/resources目录这就是将SpringBoot的静态资源目录修改为aaa、bbb的时候只有classpath:META-INF/resources目录有效其他三个均不生效的原因 二、SpringBoot错误处理 2.1 SpringBoot错误处理规则 当我们访问服务器一个不存在的地址时SpringBoot会封装一个好一个完整的响应给到客户端并且能够根据客户端的不同来选择响应的数据 当使用浏览器发送请求时响应的是一个页面 当使用Postman等接口工具时响应的则是一个json数据 2.1.1 定制错误页面 当SpringBoot出现错误提供错误页面时会将请求转发到 /error/错误码.html我们只需要在/static/error/错误码.html目录下提供对应的错误页面即可 准备一个Controller package com.dfbz.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;/*** author lscl* version 1.0* intro:*/
Controller
public class HelloController {GetMapping(/hello)ResponseBodypublic String hello() {return hello;}GetMapping(/demo01)ResponseBodypublic String demo01() {int i 1 / 0;return demo01...;}PostMapping(/demo02) // post请求,如果不是post请求则出现405ResponseBodypublic String demo02() {return demo02...;}
} 在浏览器地址栏访问 http://localhost:8080/demo01出现500 http://localhost:8080/demo02出现405 我们也可以进行错误页面的模糊匹配即提供4xx、5xx处理4xx的错误和5xx的错误 Tips当4xx与404冲突时精确优先优先寻找精确的状态码.html 2.2.2 定制错误json数据响应 1定义异常处理器 第一种方法定义异常处理器 package com.dfbz.exceptionHandler;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;
import java.util.Map;/*** author lscl* version 1.0* intro:*/
ControllerAdvice
public class MyExceptionHandler {ResponseBodyExceptionHandler(ArithmeticException.class)public MapString,Object handleException(Exception e){MapString,Object map new HashMap();map.put(ms1,hello);map.put(ms2,world);return map;}
} 上面的处理方法有个弊端 1只能处理服务器端的错误不能处理4xx的错误
2永远只能返回json数据不能自适应客户端如果客户端是浏览器应该响应的是一个视图 2转发error请求 我们可以采用SpringBoot提供的方式如果出现了异常则转发到 /error交给SpringBoot去处理 修改异常处理器 ExceptionHandler(ArithmeticException.class)
public String handleException(Exception e, Model model) {// 传入我们自己的错误状态码 4xx 5xx否则就不会进入定制错误页面的解析流程model.addAttribute(javax.servlet.error.status_code, 500);// 进行业务处理.....// 然后转发到 /error 请求,交给SpringBoot处理后续操作return forward:/error;
} 此时分别采用浏览器和Postman测试工具发送请求http://localhost:8080/demo01出现异常发现存在自适应效果 但是我们发现SpringBoot响应Json数据时永远是如下的数据 {timestamp: 2021-09-21T10:15:28.0920000,status: 500,error: Internal Server Error,message: / by zero,path: /hello
} 我们能否定制化错误信息呢当然是可以的 2.2.3 定制错误数据封装属性类 SpringBoot的错误Json数据都是由DefaultErrorAttributes类进行封装的我们想要携带自己的错误信息可以继承该类重写其方法然后添加我们自己的错误信息 package com.dfbz.controller;import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;/*** author lscl* version 1.0* intro:*/
Component
public class MyErrorAttributes extends DefaultErrorAttributes {Overridepublic MapString, Object getErrorAttributes(WebRequest webRequest,boolean includeStackTrace) {// 保留SpringBoot原来的错误信息MapString, Object map super.getErrorAttributes(webRequest, includeStackTrace);// 在原来的基础上添加我们自己的错误信息map.put(message1,aaa);map.put(message2,bbb);return map;}
} 使用Postman发送数据测试 2.2.4 定制错误处理请求处理类 我们知道SpringBoot在处理异常时会将请求转发给/error然后继续后续操作我们可以定义一个Controller来接收/error请求 自定义错误处理Controller package com.dfbz.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** author lscl* version 1.0* intro: 自定义错误请求控制器*/
RestController
public class MyErrorController {RequestMapping(/error)public String error() {return 我是自定义的ErrorController....;}
} 但是启动服务器后出现如下错误 public org.springframework.http.ResponseEntityjava.util.Mapjava.lang.String, java.lang.Object org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
to {[/error]}: There is already myErrorController bean method 提示/error路径已经已经被其他Controller定义了原因很简单因为SpringBoot内部已经定义了一个Controller来处理/error路径我们再定义一个路径肯定跟SpringBoot定义的冲突了 我们可以将我们的Controller实现SpringBoot相应的规范 package com.dfbz.controller;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;/*** author lscl* version 1.0* intro: 自定义错误请求控制器*/
RestController
public class MyErrorController implements ErrorController {// 读取server.error.path配置,如果没有就读取error.path,如果没有就为/errorValue(${server.error.path:${error.path:/error}})private String errorPath;// 如果出现了错误执行我们自定义的逻辑来处理RequestMapping(${server.error.path:${error.path:/error}})public String result() {return 我是自定义的ErrorController.... errorPath;}Overridepublic String getErrorPath() {return errorPath;}
} 重启服务器访问http://localhost:8080/demo01 很显然我们自定义的Controller肯定没有自适应功能 浏览器访问 postman访问 2.2 SpringBoot 错误处理原理 通过前面的学习我们已经能够明白自定义属性封装类 2.2.1 ErrorMvcAutoConfiguration源码分析 SpringBoot关于错误处理的配置都在ErrorMvcAutoConfiguration类中有描述 package org.springframework.boot.autoconfigure.web.servlet.error;Configuration
ConditionalOnWebApplication(type Type.SERVLET)
ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
AutoConfigureBefore(WebMvcAutoConfiguration.class)
EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class })
public class ErrorMvcAutoConfiguration {private final ServerProperties serverProperties;private final ListErrorViewResolver errorViewResolvers;// 从IOC容器里面获取ServerProperties给自身赋值public ErrorMvcAutoConfiguration(ServerProperties serverProperties,ObjectProviderListErrorViewResolver errorViewResolversProvider) {// 赋值this.serverProperties serverProperties;this.errorViewResolvers errorViewResolversProvider.getIfAvailable();}// 默认的错误属性配置类(如果容器没有配置ErrorAttributes才配置默认的DefaultErrorAttributes)BeanConditionalOnMissingBean(value ErrorAttributes.class, search SearchStrategy.CURRENT)public DefaultErrorAttributes errorAttributes() {return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException());}// 默认的错误请求处理类(如果容器没有配置ErrorController才配置默认的BasicErrorController)BeanConditionalOnMissingBean(value ErrorController.class, search SearchStrategy.CURRENT)public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {return new BasicErrorController(errorAttributes, this.serverProperties.getError(),this.errorViewResolvers);}// 错误页面定制化器Beanpublic ErrorPageCustomizer errorPageCustomizer() {return new ErrorPageCustomizer(this.serverProperties);}......
} 如果我们自己定义了ErrorAttributes、ErrorController等组件那么SpringBoot提供的将不会生效 2.2.2 ErrorController源码分析 1BasicErrorController源码分析-视图解析 SpringBoot出现错误后将会把请求转发到/errorSpringBoot内部定义了ErrorController类来处理错误请求我们之所以能看到错误自适应效果都是因为SpringBoot内部提供的ErrorController帮我们处理的 查看BasicErrorController源码 package org.springframework.boot.autoconfigure.web.servlet.error;Controller
RequestMapping(${server.error.path:${error.path:/error}})
public class BasicErrorController extends AbstractErrorController {private final ErrorProperties errorProperties;public BasicErrorController(ErrorAttributes errorAttributes,ErrorProperties errorProperties) {this(errorAttributes, errorProperties, Collections.emptyList());}// 从IOC容器中获取ErrorAttributes、ErrorProperties给自身属性赋值public BasicErrorController(ErrorAttributes errorAttributes,ErrorProperties errorProperties, ListErrorViewResolver errorViewResolvers) {super(errorAttributes, errorViewResolvers);Assert.notNull(errorProperties, ErrorProperties must not be null);this.errorProperties errorProperties;}Overridepublic String getErrorPath() {return this.errorProperties.getPath();}// 如果客户端能够处理text/html类型的响应(浏览器发送过来的请求,Accept:text/html)RequestMapping(produces text/html)public ModelAndView errorHtml(HttpServletRequest request,HttpServletResponse response) {// 获取响应状态码HttpStatus status getStatus(request);MapString, 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 ? new ModelAndView(error, model) : modelAndView);}// 处理除了Accept:text/html之外请求的响应RequestMappingResponseBodypublic ResponseEntityMapString, Object error(HttpServletRequest request) {// 调用该方法获取错误信息MapString, Object body getErrorAttributes(request,isIncludeStackTrace(request, MediaType.ALL));HttpStatus status getStatus(request);return new ResponseEntity(body, status);}protected boolean isIncludeStackTrace(HttpServletRequest request,MediaType produces) {// 获取ErrorProperties对象的IncludeStacktrace参数IncludeStacktrace include getErrorProperties().getIncludeStacktrace();// 如果是ALWAYS就返回trueif (include IncludeStacktrace.ALWAYS) {return true;}if (include IncludeStacktrace.ON_TRACE_PARAM) {return getTraceParameter(request);}return false;}// 获取ErrorProperties对象protected ErrorProperties getErrorProperties() {return this.errorProperties;}} 响应状态码源码AbstractErrorController protected HttpStatus getStatus(HttpServletRequest request) {Integer statusCode (Integer) request.getAttribute(javax.servlet.error.status_code);if (statusCode null) {return HttpStatus.INTERNAL_SERVER_ERROR;}try {return HttpStatus.valueOf(statusCode);}catch (Exception ex) {return HttpStatus.INTERNAL_SERVER_ERROR;}
} 响应状态码是从request中获取javax.servlet.error.status_code属性值 解析视图源码 而在ErrorMvcAutoConfiguration配置类中创建了该对象 如果是来自浏览器的访问 调用DefaultErrorViewReslover来解析视图 DefaultErrorViewResolver源码如下 // 解析视图
Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,MapString, Object model) {// 状态码为500ModelAndView modelAndView resolve(String.valueOf(status), model);if (modelAndView null SERIES_VIEWS.containsKey(status.series())) {modelAndView resolve(SERIES_VIEWS.get(status.series()), model);}return modelAndView;
}private ModelAndView resolve(String viewName, MapString, Object model) {// error/500String errorViewName error/ viewName;TemplateAvailabilityProvider provider this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext);if (provider ! null) {return new ModelAndView(errorViewName, model);}return resolveResource(errorViewName, model);
}private ModelAndView resolveResource(String viewName, MapString, Object model) {// 获取SringBoot的四个默认目录for (String location : this.resourceProperties.getStaticLocations()) {try {Resource resource this.applicationContext.getResource(location);resource resource.createRelative(viewName .html);// 判断哪个目录下存在500.html文件if (resource.exists()) {return new ModelAndView(new HtmlResourceView(resource), model);}}catch (Exception ex) {}}return null;
} 2BasicErrorController源码分析-数据封装 前面我们分析BasicErrorController对于浏览器发送的请求是如何处理的接下来我们分析BasicErrorController对于非浏览器发送的请求该如何处理 在BasicErrorController的构造方法中对ErrorAttribute类进行了赋值该ErrorAttribute是从IOC容器中获取的 调用了父类AbstractErrorController的构造方法 BasicErrorController对于非浏览器发送过来的请求出现异常时则是走下面的方法来处理 getErrorAttributes方法 protected MapString, Object getErrorAttributes(HttpServletRequest request,boolean includeStackTrace) {WebRequest webRequest new ServletWebRequest(request);// 调用了ErrorAttribute类的getErrorAttribute方法来获取错误信息return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
} 最终错误信息的获取还是调用ErrorAttibute类的getErrorAttribute方法来获取 而此时的ErrorAttribute类正是我们提供的MyErrorAttribute类 关于ErrorProperties类该类是关于错误信息的属性配置类但该类上面并未标注ConfigurationProperties注解那我们该如何修改里面的值呢 我们打开源码ErrorMvcAutoConfiguration ErrorProperties是在创建BasicErrorController时通过ServerProperties来获取的ErrorProperties也就是说ErrorProperties存在与ServerProperties内部 因此我们在application.yml中配置 server:port: 8080error:include-stacktrace: always # 开启异常追踪信息 最终把参数传递给了ErrorAttribute类 传递给了我们自定义的ErrorAttribute 3自定义ErrorController package com.dfbz.controller;import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;/*** author lscl* version 1.0* intro: 自定义错误请求控制器*/
Controller
RequestMapping(${server.error.path:${error.path:/error}})
public class MyErrorController implements ErrorController {private ApplicationContext applicationContext;private ResourceProperties resourceProperties;private ErrorAttributes errorAttributes;private ErrorProperties errorProperties;public MyErrorController(ApplicationContext applicationContext,ResourceProperties resourceProperties,ErrorAttributes errorAttributes,ServerProperties serverProperties) {this.applicationContext applicationContext;this.resourceProperties resourceProperties;this.errorAttributes errorAttributes;this.errorProperties serverProperties.getError();}// 读取server.error.path配置,如果没有就读取error.path,如果没有就为/errorValue(${server.error.path:${error.path:/error}})private String errorPath;/*** 处理浏览器发送的请求导致的异常* param request* param response* return*/RequestMapping(produces text/html)public String response_html(HttpServletRequest request, HttpServletResponse response) {// 获取到响应状态码Integer statusCode (Integer) request.getAttribute(javax.servlet.error.status_code);// 设置响应状态码response.setStatus(statusCode);// 拼接视图名称String viewName error/ statusCode;// 获取4个默认静态资源访问路径for (String location : this.resourceProperties.getStaticLocations()) {try {Resource resource this.applicationContext.getResource(location);resource resource.createRelative(viewName .html);// 如果资源存在,就返回if (resource.exists()) {return viewName .html;}} catch (Exception ex) {}}return null;}/*** 处理非浏览器发送的请求导致的异常* param request* return*/RequestMappingResponseBodypublic MapString, Object response_json(HttpServletRequest request) {// 包装成WebRequest对象WebRequest webRequest new ServletWebRequest(request);// 获取errorProperties中配置的includeStacktrace属性ErrorProperties.IncludeStacktrace include errorProperties.getIncludeStacktrace();Boolean isIncludeStackTrace false;if (include ErrorProperties.IncludeStacktrace.ALWAYS) {isIncludeStackTrace true;}// 调用ErrorAttributes获取错误信息MapString, Object model this.errorAttributes.getErrorAttributes(webRequest, isIncludeStackTrace);return model;}Overridepublic String getErrorPath() {return errorPath;}
} 浏览器访问 postman访问 达到了自适应效果 2.2.3 ErrorAttribute源码分析 1DefaultErrorAttributes源码分析 public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered {private static final String ERROR_ATTRIBUTE DefaultErrorAttributes.class.getName() .ERROR;private final boolean includeException;public DefaultErrorAttributes() {this(false);}public DefaultErrorAttributes(boolean includeException) {this.includeException includeException;}// 省略部分代码...public MapString, Object getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {MapString, Object errorAttributes new LinkedHashMap();// 添加timestamperrorAttributes.put(timestamp, new Date());// 添加状态码this.addStatus(errorAttributes, webRequest);// 错误信息this.addErrorDetails(errorAttributes, webRequest, includeStackTrace);// 添加路径信息this.addPath(errorAttributes, webRequest);return errorAttributes;}private void addStatus(MapString, Object errorAttributes,RequestAttributes requestAttributes) {Integer status getAttribute(requestAttributes,javax.servlet.error.status_code);if (status null) {errorAttributes.put(status, 999);errorAttributes.put(error, None);return;}errorAttributes.put(status, status);try {errorAttributes.put(error, HttpStatus.valueOf(status).getReasonPhrase());}catch (Exception ex) {// Unable to obtain a reasonerrorAttributes.put(error, Http Status status);}}private void addErrorDetails(MapString, Object errorAttributes,WebRequest webRequest, boolean includeStackTrace) {// 通过getError获取触发的异常信息Throwable error getError(webRequest);if (error ! null) {while (error instanceof ServletException error.getCause() ! null) {error ((ServletException) error).getCause();}// 如果为true就添加异常类信息if (this.includeException) {errorAttributes.put(exception, error.getClass().getName());}addErrorMessage(errorAttributes, error);// 如果为true就添加异常追踪信息if (includeStackTrace) {addStackTrace(errorAttributes, error);}}Object message getAttribute(webRequest, javax.servlet.error.message);if ((!StringUtils.isEmpty(message) || errorAttributes.get(message) null) !(error instanceof BindingResult)) {errorAttributes.put(message,StringUtils.isEmpty(message) ? No message available : message);}}// 获取触发的异常对象Overridepublic Throwable getError(WebRequest webRequest) {Throwable exception getAttribute(webRequest, ERROR_ATTRIBUTE);if (exception null) {exception getAttribute(webRequest, javax.servlet.error.exception);}return exception;}SuppressWarnings(unchecked)private T T getAttribute(RequestAttributes requestAttributes, String name) {return (T) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);}
} 2自定义ErrorAttribute package com.dfbz.controller;import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.util.NestedServletException;import java.util.HashMap;
import java.util.Map;/*** author lscl* version 1.0* intro:*/
Component
public class MyErrorAttributes implements ErrorAttributes {/*** 封装错误属性遵守我们自己的规则** param webRequest: Request的封装对象,可以获取请求相关书籍* param includeStackTrace: 是否要打印追踪异常最终信息,通过如下配置可以开启异常信息的打印* server:* error:* include-stacktrace: always* return*/Overridepublic MapString, Object getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {HashMapString, Object body new HashMap();// 添加自己的错误信息body.put(message1, aaa);body.put(message2, bbb);if (includeStackTrace) {Throwable error getError(webRequest);// 打印错误追踪信息error.printStackTrace();body.put(trace, error.getMessage());}return body;}// 添加追踪信息时异常的类型Overridepublic Throwable getError(WebRequest webRequest) {// 获取实际触发的异常NestedServletException attribute (NestedServletException) webRequest.getAttribute(javax.servlet.error.exception, WebRequest.SCOPE_REQUEST);Throwable exception attribute.getCause();return exception;}
}