当前源码:spring-boot 2.2.5.RELEASE
版本!
1.声明
当前内容是用来了解和复习SpringBoot中的BasicErrorController源码的,在于一次配置了日志的时候发现访问一个错误的url产生的就是一个模板错误页面
使用springboot的时候产生这个肯定是访问路径没有被映射,所以才产生的
本人debug发现:
发现这个无论访问任何不存在的url都会调用BasicErrorController中的errorHtml方法!(所以本人决定解析这个类,并了解作用)
2.分析BasicErrorController类
首先通过名字发现BasicErrorController就是一个基本错误控制器
,就是一个Controller,也就是说spring可能使用@Controller注解
,其方法中必定有@RequestMapping用来处理error
问题
1.查看这个类的注解
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
}
发现@RequestMapping("${server.error.path:${error.path:/error}}")
,可能就是用来匹配路径为/error的,本人表示有点不明白!就是使用了@Controller
2.查看这个类调用的errorHtml方法
// 表示当前响应为html/text
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request); // 获取http状态码
// 获取错误的model(不可修改的)
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);
}
发现该方法就是一个设置返回为html/text,并且居于状态码,并产生modelAndView的方法
由于返回的ModelAndView是需要通过ViewResolver进行解析的成view才能返回的,所以继续找到对应的错误视图解析器
3.找到错误视图解析器
1.查看当前BasicErrorController所在的包,结果如下
我们发现了ErrorViewResolver和DefaultErrorViewResolver(所以可以判断spring中默认就是使用DefaultErrorViewResolver进行解析的
)
ErrorMvcAutoConfiguration
这个类应该就是Error视图自动配置类(访问不存在的url的配置类
)
4.查看DefaultErrorViewResolver类源码
查看这个类的静态代码块:
static {
Map<Series, String> views = new EnumMap<>(Series.class);
views.put(Series.CLIENT_ERROR, "4xx");
views.put(Series.SERVER_ERROR, "5xx");
SERIES_VIEWS = Collections.unmodifiableMap(views);
}
默认项SERIES_VIEWS 中添加了错误代码,分为客户端错误:4XX和服务器错误:5XX
,并且是不可修改的Map集合
查看解析视图的方法
// 解析错误的视图
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
// 传递当前的 HttpStatus 中的状态码:即4xx,5xx
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;
}
//通过视图名称和model解析视图
private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName; // 为error/4xx,5xx
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
this.applicationContext); // 判断是否存在模板
if (provider != null) { // 存在就返回模板视图
return new ModelAndView(errorViewName, model);
}
return resolveResource(errorViewName, model); // 否则返回资源视图(即当前文件error/中存在4xx或者5xx的html页面)
}
// 按照名称和model解析资源视图
private ModelAndView resolveResource(String viewName, Map<String, Object> model) {
// 获取静态资源路径
for (String location : this.resourceProperties.getStaticLocations()) {
try {
// 通过路径获取resource
Resource resource = this.applicationContext.getResource(location);
resource = resource.createRelative(viewName + ".html"); // 通过error/4xx.html等是否存在
if (resource.exists()) {
// 存在就返回他
return new ModelAndView(new HtmlResourceView(resource), model);
}
}
catch (Exception ex) {
}
}
return null;
}
这里发现了HtmlResourceView这个内部类
private static class HtmlResourceView implements View {
private Resource resource;
HtmlResourceView(Resource resource) {
this.resource = resource;
}
@Override
public String getContentType() {
return MediaType.TEXT_HTML_VALUE;
}
// 发现所有的view中都是具有这个方法的,所以通过这个方法可以实现所有的视图转发
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception {
// 设置响应类型
response.setContentType(getContentType());
// 将当前的html文件读入为输入流,并写入到响应流中
FileCopyUtils.copy(this.resource.getInputStream(), response.getOutputStream());
}
}
通过上面发现,为什么我们在resources文件中添加了error文件夹,并创建4xx.html,或者5xx.html的时候可以被springboot自动解析并在页面显示
5.现在来看ErrorMvcAutoConfiguration
首先通过ErrorMvcAutoConfiguration名称发现,这是一个错误mvc自动配置类,所以这个类必须有@Configuration注解
1.查看这个类的注解
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
// Load before the main WebMvcAutoConfiguration so that the error View is available
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@EnableConfigurationProperties({ ServerProperties.class, ResourceProperties.class, WebMvcProperties.class })
发现这个类在web应用中并且类型是Servlet在会创建,并且容器中必须有Servlet和DispatcherServlet存在,必须在WebMvcAutoConfiguration前面启动配置
==2.发现这个类中定义了很多bean,并且确实注入了BasicErrorController ==
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
errorViewResolvers.orderedStream().collect(Collectors.toList()));
}
3.发现这个类的静态内部类DefaultErrorViewResolverConfiguration(又是一个配置类),发现这个类中注入了DefaultErrorViewResolver这个错误视图解析器!
@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean(ErrorViewResolver.class)
DefaultErrorViewResolver conventionErrorViewResolver() {
return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties);
}
==4.继续查看WhitelabelErrorViewConfiguration这个配置类,发现了StaticView ==
private final StaticView defaultErrorView = new StaticView();
@Bean(name = "error")
@ConditionalOnMissingBean(name = "error")
public View defaultErrorView() {
return this.defaultErrorView;
}
发现这个类名字叫error,估计有什么特殊作用
5.继续查看StaticView类
发现这个内容很熟悉
发现结果就是这个,所以当我们访问一个不存在的url的时候就会创建StaticView,并启用render方法,写出一个text/html的模板信息
6.总结
1.我们使用Springboot访问一个不存在的url的时候,默认会创建error映射,并使用BasicErrorController中的@RequestMapping
2.当BasicErrorController中的对应的@RequestMapping方法处理完后返回ModelAndView,就需要通过DefaultErrorViewResolver来解析并获取View
3.当前解析的时候默认会使用StaticView来解析并生成(调用render方法)模板返回
4.SpringBoot之所以这个简单,是因为ErrorMvcAutoConfiguration在启动的时候就被加载了各种需要的配置类,它将我们需要的东西都配置好了,所以我们使用起来很简单
!
以上纯属个人见解,如有问题请联系本人!