SpringBoot源码之BasicErrorController、DefaultErrorViewResolver、ErrorMvcAutoConfiguration类

  • Post author:
  • Post category:其他



当前源码:

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在启动的时候就被加载了各种需要的配置类,它将我们需要的东西都配置好了,所以我们使用起来很简单



以上纯属个人见解,如有问题请联系本人!



版权声明:本文为weixin_45492007原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。