SpringBoot的web开发 webJar 以jar包的形式来引入前端资源,比如jquery 或者是BootStrap:https://www.webjars.org
前端资源映射规则 核心源代码:WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public void addResourceHandlers (ResourceHandlerRegistry registry) { if (!this .resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled" ); } else { this .addResourceHandler(registry, "/webjars/**" , "classpath:/META-INF/resources/webjars/" ); this .addResourceHandler(registry, this .mvcProperties.getStaticPathPattern(), (registration) -> { registration.addResourceLocations(this .resourceProperties.getStaticLocations()); if (this .servletContext != null ) { ServletContextResource resource = new ServletContextResource(this .servletContext, "/" ); registration.addResourceLocations(new Resource[]{resource}); } }); } }
访问:http://localhost:8080//webjars/jquery/3.3.1-2/jquery.js
控制台打印:
源码追踪:
1 org.springframework.web.servlet.resource.ResourceHttpRequestHandler#handleRequest方法 >org.springframework.web.servlet.resource.ResourceHttpRequestHandler#getResource >org.springframework.web.servlet.resource.ResourceResolverChain#resolveResource >org.springframework.web.servlet.resource.PathResourceResolver#resolveResourceInternal >org.springframework.web.servlet.resource.PathResourceResolver#getResource(真正的资源映射 处理逻辑)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Nullable private Resource getResource (String resourcePath, @Nullable HttpServletRequest request, List<? extends Resource> locations) { for (Resource location : locations) { try { String pathToUse = encodeOrDecodeIfNecessary(resourcePath, request, location); Resource resource = getResource(pathToUse, location); if (resource != null ) { return resource; } } catch (IOException ex) { if (logger.isDebugEnabled()) { String error = "Skip location [" + location + "] due to error" ; if (logger.isTraceEnabled()) { logger.trace(error, ex); } else { logger.debug(error + ": " + ex.getMessage()); } } } } return null ; }
静态页面映射 我们直接把静态页面放在static的目录下,直接可以在路径直接访问
Springboot整合springmvc 自动装配的组件 ①:ContentNegotiatingViewResolver 和 BeanNameViewResolver 视图解析器 视图解析器的作用:根据方法的值找到对应的视图
②:Support for serving static resources, including support for WebJars 支持静态资源和webJars
③:Converter ,日期格式化器 Formatter
④:消息装换器: HttpMessageConverters
⑤:首页设置index.html
⑥:图标支持 Favicon
拓展SpringBoot的mvc配置 自己写一个配置类 继承 WebMvcConfigurer 需要什么组件 就注册什么组件
如何往容器中添加一个拦截器
往容器中增加一个过滤器
往容器中增加一个 servlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 @Configuration public class MvcConfig implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { HandlerInterceptor handlerInterceptor = new HandlerInterceptor() { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("myHandlerInterceptor...前置拦截器:..." ); return true ; } }; registry.addInterceptor(handlerInterceptor).addPathPatterns("/**" ).excludePathPatterns("/index.html" ); } @Bean public FilterRegistrationBean filterRegistrationBean () { Filter filter = new Filter() { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("myFilter 的 doFilter方法" ); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy () { } }; FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(filter); filterRegistrationBean.addUrlPatterns("/*" ); return filterRegistrationBean; } @Bean public ServletRegistrationBean myHttpServlet () { HttpServlet httpServlet = new HttpServlet() { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("myHttpServlet" ); super .doPost(req,resp); } }; ServletRegistrationBean servletRegistration = new ServletRegistrationBean(httpServlet); servletRegistration.addUrlMappings("/hello1" ); return servletRegistration; } }
接管springboot的mvc配置 方式 :
使用一个@EnableWebMvc来标识到配置类上,就会导致配置失效
原理:
@EnableWebMvc 为容器中导入了DelegatingWebMvcConfiguration的组件,是WebMvcConfigurationSupport的实现类,而WebMvcAutoConfiguration在WebMvcConfigurationSupport不存在时才会自动配置
1 2 3 4 5 6 7 8 9 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(DelegatingWebMvcConfiguration.class) public @interface EnableWebMvc { }public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
1 2 3 4 5 6 7 8 @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration
SpringBoot错误处理机制 postman 请求 http://localhost:8080/hello3
1 2 3 4 5 6 { "timestamp" : 1629035261980 , "status" : 404 , "error" : "Not Found" , "path" : "/hello3" }
浏览器
如何跳转到/error 1 2 3 DispatchServlet . doDispatch 找不到 返回StandardHostValve . customApplicationDispatcher . doForward 转发 到 /error
自动配置的异常处理的一些bean 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 DefaultErrorAttributes @Bean @ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT) public DefaultErrorAttributes errorAttributes () { return new DefaultErrorAttributes(); } BasicErrorController:/error请求处理控制器 @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())); } ErrorPageCustomizer:错误页面定制器 @Bean public ErrorPageCustomizer errorPageCustomizer (DispatcherServletPath dispatcherServletPath) { return new ErrorPageCustomizer(this .serverProperties, dispatcherServletPath); } DefaultErrorViewResolver @Bean @ConditionalOnBean(DispatcherServlet.class) @ConditionalOnMissingBean(ErrorViewResolver.class) DefaultErrorViewResolver conventionErrorViewResolver () { return new DefaultErrorViewResolver(this .applicationContext, this .resources); } 注入默认错误页面 defaultErrorView @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(prefix = "server.error.whitelabel", name = "enabled", matchIfMissing = true) @Conditional(ErrorTemplateMissingCondition.class) protected static class WhitelabelErrorViewConfiguration { private final StaticView defaultErrorView = new StaticView(); @Bean(name = "error") @ConditionalOnMissingBean(name = "error") public View defaultErrorView () { return this .defaultErrorView; } @Bean @ConditionalOnMissingBean public BeanNameViewResolver beanNameViewResolver () { BeanNameViewResolver resolver = new BeanNameViewResolver(); resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10 ); return resolver; } }
处理/error请求的过程,以浏览器请求为例
org.springframework.web.servlet.DispatcherServlet#doDispatch
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml (基础错误控制器)
org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController#resolveErrorView 遍历errorViewResolver 解析错误视图
org.springframework.boot.autoconfigure.web.servlet.error.DefaultErrorViewResolver#resolveErrorView //查找error/“ + viewName视图是否存在,如果没有对应的解析精确匹配的状态码 使用模糊匹配比如4XX 5XX
return (modelAndView != null) ? modelAndView : new ModelAndView(“error”, model);找不到对应解析器,则查找 error 对应的model
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 解析error视图View
定制错误异常信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 @ControllerAdvice public class ExcepctionHandler { @ExceptionHandler(Exception.class) @ResponseBody public BasicResponse handlerException (Exception e) { return BasicResponse.failed(); } }@Data public class BasicResponse <T > { private int code; private String msg; private T info; private static final Integer SUCCESS_CODE = 200 ; private static final String SUCCESS_MSG = "请求成功" ; private static final Integer FAIL_CODE = 500 ; private static final String FAIL_MSG = "服务器报错" ; public static BasicResponse success () { return new BasicResponse<>(SUCCESS_CODE,SUCCESS_MSG); } public static BasicResponse success (String successMsg) { return new BasicResponse<>(SUCCESS_CODE, successMsg); } public static BasicResponse failed () { return new BasicResponse<>(FAIL_CODE, FAIL_MSG); } public static BasicResponse failed (String failedMsg) { return new BasicResponse<>(FAIL_CODE, failedMsg); } public static <T> BasicResponse failed (String failedMsg,T object) { return new BasicResponse<>(FAIL_CODE, failedMsg,object); } public static BasicResponse failed (Integer code,String failedMsg) { return new BasicResponse<>(code, failedMsg); } private BasicResponse (int code, String msg) { this .code = code; this .msg = msg; } private BasicResponse (int code, String msg, T info) { this (code, msg); this .info = info; } }