文章目錄
- 1. SpringMVC自動配置概覽
- 2. 簡單功能分析
- 2.1 靜態資源訪問
- 2.1.1 靜態資源目錄
- 2.1.2 靜態資源訪問前綴
- 2.1.3 webjar
- 2.2 歡迎頁支持
- 2.3 自定義 Favicon
- 2.4 靜態資源配置原理
- 2.4.1 配置類只有一個有參構造器
- 2.4.2 資源處理的默認規則
- 2.4.3 歡迎頁的處理規則
- 2.4.4 favicon
- 3. 請求參數處理
- 3.1 請求映射
- 3.1.1 rest使用與原理
- 3.1.2 請求映射原理
- 3.2 普通參數與基本注解
- 3.2.1 注解
- 3.2.2 Servlet API
- 3.2.3 復雜參數
- 2.2.4 自定義對象參數
- 3.3 POJO封裝過程
- 3.4 參數處理原理
- 3.4.1 HandlerAdapter
- 3.4.2 執行目標方法
- 3.4.3 參數解析器-HandlerMethodArgumentResolver
- 3.4.4 返回值處理器
- 3.4.5 如何確定目標方法每一個參數的值
- 3.4.6 目標方法執行完成
- 3.4.7 處理派發結果
- 4. 數據響應與內容協商
- 4.1 響應JSON
- 4.1.1 jackson.jar+@ResponseBody
- 4.1.2 SpringMVC到底支持哪些返回值
- 4.1.3 HTTPMessageConverter原理
- 4.2 內容協商
- 4.2.1 引入xml依賴
- 4.2.2 postman分別測試返回json和xml
- 4.2.3 開啟瀏覽器參數方式內容協商功能
- 4.2.4 內容協商原理
- 4.2.5 自定義 MessageConverter
- 5. 視圖解析與模板引擎
- 5.1 視圖解析
- 5.1.1 視圖解析原理流程
- 5.2 模板引擎-Thymeleaf(略)
- 5.3 thymeleaf使用(略)
- 5.4 構建后臺管理系統(略)
- 6. 攔截器
- 6.1 HandlerInterceptor
- 6.2 配置攔截器
- 6.3 攔截器原理
- 7. 文件上傳
- 7.1 頁面表單
- 7.2 文件上傳代碼
- 7.3 自動配置原理
- 8. 異常處理
- 8.1 錯誤處理
- 8.1.1 默認規則
- 8.1.2 定制錯誤處理邏輯
- 8.1.3 異常處理自動配置原理
- 8.1.4 異常處理步驟流程
- 9. Web原生組件注入(Servlet、Filter、Listener)
- 9.1 使用Servlet API
- 9.2. 使用RegistrationBean
- 10. 嵌入式Servlet容器
- 10.1 切換嵌入式Servlet容器
- 10.2 定制Servlet容器
- 11. 定制化原理
- 11.1 定制化的常見方式
- 11.2 原理分析套路
- 11.2 原理分析套路
1. SpringMVC自動配置概覽
2. 簡單功能分析
2.1 靜態資源訪問
2.1.1 靜態資源目錄
只要靜態資源放在類路徑下: called /static (or /public or /resources or /META-INF/resources
訪問 : 當前項目根路徑/ + 靜態資源名
原理: 靜態映射/**。
請求進來,先去找Controller看能不能處理。
不能處理的所有請求又都交給靜態資源處理器。
靜態資源也找不到則響應404頁面
改變默認的靜態資源路徑
spring:mvc:static-path-pattern: /res/**resources:static-locations: [classpath:/haha/]
2.1.2 靜態資源訪問前綴
默認無前綴
spring:mvc:static-path-pattern: /res/**
當前項目 + static-path-pattern + 靜態資源名 = 靜態資源文件夾下找
2.1.3 webjar
自動映射 /webjars/**
https://www.webjars.org/
<dependency><groupId>org.webjars</groupId><artifactId>jquery</artifactId><version>3.5.1</version></dependency>
訪問地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依賴里面的包路徑
2.2 歡迎頁支持
- 靜態資源路徑下 index.html
- 可以配置靜態資源路徑
- 但是不可以配置靜態資源的訪問前綴。否則導致 index.html不能被默認訪問
spring:
# mvc:
# static-path-pattern: /res/** 這個會導致welcome page功能失效resources:static-locations: [classpath:/haha/]
2.3 自定義 Favicon
favicon.ico 放在靜態資源目錄下即可。
spring:
# mvc:
# static-path-pattern: /res/** 這個會導致 Favicon 功能失效
2.4 靜態資源配置原理
- SpringBoot啟動默認加載 xxxAutoConfiguration 類(自動配置類)
- SpringMVC功能的自動配置類 WebMvcAutoConfiguration,生效
@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 {}
- 給容器中配了什么。
@Configuration(proxyBeanMethods = false)@Import(EnableWebMvcConfiguration.class)@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })@Order(0)public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {}
- 配置文件的相關屬性和xxx進行了綁定。WebMvcPropertiesspring.mvc、ResourcePropertiesspring.resources
2.4.1 配置類只有一個有參構造器
//有參構造器所有參數的值都會從容器中確定
//ResourceProperties resourceProperties;獲取和spring.resources綁定的所有的值的對象
//WebMvcProperties mvcProperties 獲取和spring.mvc綁定的所有的值的對象
//ListableBeanFactory beanFactory Spring的beanFactory
//HttpMessageConverters 找到所有的HttpMessageConverters
//ResourceHandlerRegistrationCustomizer 找到 資源處理器的自定義器。=========
//DispatcherServletPath
//ServletRegistrationBean 給應用注冊Servlet、Filter....public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,ObjectProvider<DispatcherServletPath> dispatcherServletPath,ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {this.resourceProperties = resourceProperties;this.mvcProperties = mvcProperties;this.beanFactory = beanFactory;this.messageConvertersProvider = messageConvertersProvider;this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();this.dispatcherServletPath = dispatcherServletPath;this.servletRegistrations = servletRegistrations;}
2.4.2 資源處理的默認規則
@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的規則if (!registry.hasMappingForPattern("/webjars/**")) {customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/").setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));}//String staticPathPattern = this.mvcProperties.getStaticPathPattern();if (!registry.hasMappingForPattern(staticPathPattern)) {customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern).addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));}}
spring:
# mvc:
# static-path-pattern: /res/**resources:add-mappings: false 禁用所有靜態資源規則
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/" };/*** Locations of static resources. Defaults to classpath:[/META-INF/resources/,* /resources/, /static/, /public/].*/private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
2.4.3 歡迎頁的處理規則
HandlerMapping:處理器映射。保存了每一個Handler能處理哪些請求。 @Beanpublic WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());return welcomePageHandlerMapping;}WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {//要用歡迎頁功能,必須是/**logger.info("Adding welcome page: " + welcomePage.get());setRootViewName("forward:index.html");}else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {// 調用Controller /indexlogger.info("Adding welcome page template: index");setRootViewName("index");}}
2.4.4 favicon
3. 請求參數處理
3.1 請求映射
3.1.1 rest使用與原理
@RequestMapping(value = "/user",method = RequestMethod.GET)public String getUser(){return "GET-張三";}@RequestMapping(value = "/user",method = RequestMethod.POST)public String saveUser(){return "POST-張三";}@RequestMapping(value = "/user",method = RequestMethod.PUT)public String putUser(){return "PUT-張三";}@RequestMapping(value = "/user",method = RequestMethod.DELETE)public String deleteUser(){return "DELETE-張三";}@Bean@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();}//自定義filter@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();methodFilter.setMethodParam("_m");return methodFilter;}
spring:mvc:hiddenmethod:filter:enabled: true #開啟頁面表單的Rest功能
3.1.2 請求映射原理
SpringMVC功能分析都從 org.springframework.web.servlet.DispatcherServlet -> doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 找到當前請求使用哪個Handler(Controller的方法)處理mappedHandler = getHandler(processedRequest);//HandlerMapping:處理器映射。/xxx->>xxxx
RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射規則
所有的請求映射都在HandlerMapping中。
- SpringBoot自動配置歡迎頁的 WelcomePageHandlerMapping 。訪問 /能訪問到index.html;
- SpringBoot自動配置了默認 的 RequestMappingHandlerMapping
- 請求進來,挨個嘗試所有的HandlerMapping看是否有請求信息。
- 如果有就找到這個請求對應的handler
- 如果沒有就是下一個 HandlerMapping
- 我們需要一些自定義的映射處理,我們也可以自己給容器中放HandlerMapping。自定義 HandlerMapping
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;}
3.2 普通參數與基本注解
3.2.1 注解
@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
@RestController
public class ParameterTestController {// car/2/owner/zhangsan@GetMapping("/car/{id}/owner/{username}")public Map<String,Object> getCar(@PathVariable("id") Integer id,@PathVariable("username") String name,@PathVariable Map<String,String> pv,@RequestHeader("User-Agent") String userAgent,@RequestHeader Map<String,String> header,@RequestParam("age") Integer age,@RequestParam("inters") List<String> inters,@RequestParam Map<String,String> params,@CookieValue("_ga") String _ga,@CookieValue("_ga") Cookie cookie){Map<String,Object> map = new HashMap<>();// map.put("id",id);
// map.put("name",name);
// map.put("pv",pv);
// map.put("userAgent",userAgent);
// map.put("headers",header);map.put("age",age);map.put("inters",inters);map.put("params",params);map.put("_ga",_ga);System.out.println(cookie.getName()+"===>"+cookie.getValue());return map;}@PostMapping("/save")public Map postMethod(@RequestBody String content){Map<String,Object> map = new HashMap<>();map.put("content",content);return map;}//1、語法: 請求路徑:/cars/sell;low=34;brand=byd,audi,yd//2、SpringBoot默認是禁用了矩陣變量的功能// 手動開啟:原理。對于路徑的處理。UrlPathHelper進行解析。// removeSemicolonContent(移除分號內容)支持矩陣變量的//3、矩陣變量必須有url路徑變量才能被解析@GetMapping("/cars/{path}")public Map carsSell(@MatrixVariable("low") Integer low,@MatrixVariable("brand") List<String> brand,@PathVariable("path") String path){Map<String,Object> map = new HashMap<>();map.put("low",low);map.put("brand",brand);map.put("path",path);return map;}// /boss/1;age=20/2;age=10@GetMapping("/boss/{bossId}/{empId}")public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){Map<String,Object> map = new HashMap<>();map.put("bossAge",bossAge);map.put("empAge",empAge);return map;}}
3.2.2 Servlet API
WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
ServletRequestMethodArgumentResolver 以上的部分參數
@Overridepublic boolean supportsParameter(MethodParameter parameter) {Class<?> paramType = parameter.getParameterType();return (WebRequest.class.isAssignableFrom(paramType) ||ServletRequest.class.isAssignableFrom(paramType) ||MultipartRequest.class.isAssignableFrom(paramType) ||HttpSession.class.isAssignableFrom(paramType) ||(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||Principal.class.isAssignableFrom(paramType) ||InputStream.class.isAssignableFrom(paramType) ||Reader.class.isAssignableFrom(paramType) ||HttpMethod.class == paramType ||Locale.class == paramType ||TimeZone.class == paramType ||ZoneId.class == paramType);}
3.2.3 復雜參數
Map、Model(map、model里面的數據會被放在request的請求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向攜帶數據)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
Map<String,Object> map, Model model, HttpServletRequest request 都是可以給request域中放數據,
request.getAttribute();
Map、Model類型的參數,會返回 mavContainer.getModel();—> BindingAwareModelMap 是Model 也是Map mavContainer.getModel(); 獲取到值的
2.2.4 自定義對象參數
可以自動類型轉換與格式化,可以級聯封裝
/*** 姓名: <input name="userName"/> <br/>* 年齡: <input name="age"/> <br/>* 生日: <input name="birth"/> <br/>* 寵物姓名:<input name="pet.name"/><br/>* 寵物年齡:<input name="pet.age"/>*/
@Data
public class Person {private String userName;private Integer age;private Date birth;private Pet pet;}@Data
public class Pet {private String name;private String age;}result
3.3 POJO封裝過程
- ServletModelAttributeMethodProcessor
3.4 參數處理原理
- HandlerMapping中找到能處理請求的Handler(Controller.method())
- 為當前Handler 找一個適配器 HandlerAdapter; RequestMappingHandlerAdapter
- 適配器執行目標方法并確定方法參數的每一個值
3.4.1 HandlerAdapter
0 - 支持方法上標注@RequestMapping
1 - 支持函數式編程的
xxxxxx
3.4.2 執行目標方法
// Actually invoke the handler.
//DispatcherServlet -- doDispatch
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mav = invokeHandlerMethod(request, response, handlerMethod); //執行目標方法//ServletInvocableHandlerMethod
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//獲取方法的參數值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
3.4.3 參數解析器-HandlerMethodArgumentResolver
確定將要執行的目標方法的每一個參數的值是什么; SpringMVC目標方法能寫多少種參數類型。取決于參數解析器。
- 當前解析器是否支持解析這種參數
- 支持就調用 resolveArgument
3.4.4 返回值處理器
3.4.5 如何確定目標方法每一個參數的值
============InvocableHandlerMethod==========================
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) {// Leave stack trace for later, exception may actually be resolved and handled...if (logger.isDebugEnabled()) {String exMsg = ex.getMessage();if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));}}throw ex;}}return args;}
5.1、挨個判斷所有參數解析器那個支持解析這個參數
@Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {result = resolver;this.argumentResolverCache.put(parameter, result);break;}}}return result;}
5.2、解析這個參數的值
調用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可
5.3、自定義類型參數 封裝POJO
ServletModelAttributeMethodProcessor 這個參數處理器支持 是否為簡單類型。
public static boolean isSimpleValueType(Class<?> type) {return (Void.class != type && void.class != type &&(ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||CharSequence.class.isAssignableFrom(type) ||Number.class.isAssignableFrom(type) ||Date.class.isAssignableFrom(type) ||Temporal.class.isAssignableFrom(type) ||URI.class == type ||URL.class == type ||Locale.class == type ||Class.class == type));}
@Override@Nullablepublic final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");String name = ModelFactory.getNameForParameter(parameter);ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);if (ann != null) {mavContainer.setBinding(name, ann.binding());}Object attribute = null;BindingResult bindingResult = null;if (mavContainer.containsAttribute(name)) {attribute = mavContainer.getModel().get(name);}else {// Create attribute instancetry {attribute = createAttribute(name, parameter, binderFactory, webRequest);}catch (BindException ex) {if (isBindExceptionRequired(parameter)) {// No BindingResult parameter -> fail with BindExceptionthrow ex;}// Otherwise, expose null/empty value and associated BindingResultif (parameter.getParameterType() == Optional.class) {attribute = Optional.empty();}bindingResult = ex.getBindingResult();}}if (bindingResult == null) {// Bean property binding and validation;// skipped in case of binding failure on construction.WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);if (binder.getTarget() != null) {if (!mavContainer.isBindingDisabled(name)) {bindRequestParameters(binder, webRequest);}validateIfApplicable(binder, parameter);if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {throw new BindException(binder.getBindingResult());}}// Value type adaptation, also covering java.util.Optionalif (!parameter.getParameterType().isInstance(attribute)) {attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);}bindingResult = binder.getBindingResult();}// Add resolved attribute and BindingResult at the end of the modelMap<String, Object> bindingResultModel = bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);return attribute;}
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web數據綁定器,將請求參數的值綁定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 將請求數據轉成指定的數據類型。再次封裝到JavaBean中
GenericConversionService:在設置每一個值的時候,找它里面的所有converter那個可以將這個數據類型(request帶來參數的字符串)轉換到指定的類型(JavaBean – Integer)
byte – > file
@FunctionalInterface
public interface Converter<S, T>
未來我們可以給WebDataBinder里面放自己的Converter; private static final class StringToNumber implements Converter<String, T>
自定義 Converter
//1、WebMvcConfigurer定制化SpringMVC的功能@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 不移除;后面的內容。矩陣變量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new Converter<String, Pet>() {@Overridepublic Pet convert(String source) {// 啊貓,3if(!StringUtils.isEmpty(source)){Pet pet = new Pet();String[] split = source.split(",");pet.setName(split[0]);pet.setAge(Integer.parseInt(split[1]));return pet;}return null;}});}};}
3.4.6 目標方法執行完成
將所有的數據都放在 ModelAndViewContainer
;包含要去的頁面地址View。還包含Model數據。
3.4.7 處理派發結果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
InternalResourceView:
@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);// Expose helpers as request attributes, if any.exposeHelpers(request);// Determine the path for the request dispatcher.String dispatcherPath = prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);if (rd == null) {throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +"]: Check that the corresponding file exists within your web application archive!");}// If already included or response already committed, perform include, else forward.if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including [" + getUrl() + "]");}rd.include(request, response);}else {// Note: The forwarded resource is supposed to determine the content type itself.if (logger.isDebugEnabled()) {logger.debug("Forwarding to [" + getUrl() + "]");}rd.forward(request, response);}}
暴露模型作為請求域屬性
// Expose the model object as request attributes.exposeModelAsRequestAttributes(model, request);
protected void exposeModelAsRequestAttributes(Map<String, Object> model,HttpServletRequest request) throws Exception {//model中的所有數據遍歷挨個放在請求域中model.forEach((name, value) -> {if (value != null) {request.setAttribute(name, value);}else {request.removeAttribute(name);}});}
4. 數據響應與內容協商
4.1 響應JSON
4.1.1 jackson.jar+@ResponseBody
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
web場景自動引入了json場景<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-json</artifactId><version>2.3.4.RELEASE</version><scope>compile</scope></dependency>
給前端自動返回json數據;
1、返回值解析器
try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}
@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}
RequestResponseBodyMethodProcessor
@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// Try even with null return value. ResponseBodyAdvice could get involved.// 使用消息轉換器進行寫出操作writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}
2、返回值解析器原理
- 注意:如果使用
@ResponseBody
會直接在響應體中寫入數據,不會再封裝為一個ModelAndView
對象返回(不會進行視圖渲染)。其原理是利用MessageConver
的實現類執行完畢后,會設置ModelAndViewContainer
中的isRequestHandled()
方法判斷是否已經處理了請求。
4.1.2 SpringMVC到底支持哪些返回值
ModelAndView
Model
View
ResponseEntity
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且為對象類型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;
4.1.3 HTTPMessageConverter原理
1、MessageConverter規范
HttpMessageConverter: 看是否支持將 此 Class類型的對象,轉為MediaType類型的數據。 例子:Person對象轉為JSON。或者 JSON轉為Person
2、默認的MessageConverter
最終 MappingJackson2HttpMessageConverter 把對象轉為JSON(利用底層的jackson的objectMapper轉換的)
4.2 內容協商
根據客戶端接收能力不同,返回不同媒體類型的數據。
4.2.1 引入xml依賴
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId>
</dependency>
4.2.2 postman分別測試返回json和xml
只需要改變請求頭中Accept字段。Http協議中規定的,告訴服務器本客戶端可以接收的數據類型。
4.2.3 開啟瀏覽器參數方式內容協商功能
為了方便內容協商,開啟基于請求參數的內容協商功能。
spring:contentnegotiation:favor-parameter: true #開啟請求參數內容協商模式
發請求: http://localhost:8080/test/person?format=json
http://localhost:8080/test/person?format=xml
確定客戶端接收什么樣的內容類型;
1、Parameter策略優先確定是要返回json數據(獲取請求頭中的format的值)
2、最終進行內容協商返回給客戶端json即可。
4.2.4 內容協商原理
1、 判斷當前響應頭中是否已經有確定的媒體類型。MediaType
2、 獲取客戶端(PostMan、瀏覽器)支持接收的內容類型。(獲取客戶端Accept請求頭字段)【application/xml】
+ contentNegotiationManager 內容協商管理器 默認使用基于請求頭的策略
+
+ HeaderContentNegotiationStrategy 確定客戶端可以接收的內容類型
3、 遍歷循環所有當前系統的 MessageConverter,看誰支持操作這個對象(Person)
4、找到支持操作Person的converter,把converter支持的媒體類型統計出來。
5、客戶端需要【application/xml】。服務端能力【10種、json、xml】
6、進行內容協商的最佳匹配媒體類型
7、用 支持 將對象轉為 最佳匹配媒體類型 的converter。調用它進行轉化 。
導入了jackson處理xml的包,xml的converter就會自動進來
WebMvcConfigurationSupport
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);if (jackson2XmlPresent) {Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();if (this.applicationContext != null) {builder.applicationContext(this.applicationContext);}messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));}
4.2.5 自定義 MessageConverter
實現多協議數據兼容。json、xml、x-guigu
@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}}}
有可能我們添加的自定義的功能會覆蓋默認很多功能,導致一些默認的功能失效。
大家考慮,上述功能除了我們完全自定義外?SpringBoot有沒有為我們提供基于配置文件的快速修改媒體類型功能?怎么配置呢?【提示:參照SpringBoot官方文檔web開發內容協商章節】
5. 視圖解析與模板引擎
視圖解析:SpringBoot默認不支持 JSP,需要引入第三方模板引擎技術實現頁面渲染。
5.1 視圖解析
5.1.1 視圖解析原理流程
自定義視圖解析器+自定義視圖; 大廠學院
5.2 模板引擎-Thymeleaf(略)
5.3 thymeleaf使用(略)
5.4 構建后臺管理系統(略)
6. 攔截器
6.1 HandlerInterceptor
/*** 登錄檢查* 1、配置好攔截器要攔截哪些請求* 2、把這些配置放在容器中*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {/*** 目標方法執行之前* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String requestURI = request.getRequestURI();log.info("preHandle攔截的請求路徑是{}",requestURI);//登錄檢查邏輯HttpSession session = request.getSession();Object loginUser = session.getAttribute("loginUser");if(loginUser != null){//放行return true;}//攔截住。未登錄。跳轉到登錄頁request.setAttribute("msg","請先登錄");
// re.sendRedirect("/");request.getRequestDispatcher("/").forward(request,response);return false;}/*** 目標方法執行完成以后* @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("postHandle執行{}",modelAndView);}/*** 頁面渲染以后* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("afterCompletion執行異常{}",ex);}
}
6.2 配置攔截器
/*** 1、編寫一個攔截器實現HandlerInterceptor接口* 2、攔截器注冊到容器中(實現WebMvcConfigurer的addInterceptors)* 3、指定攔截規則【如果是攔截所有,靜態資源也會被攔截】*/
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") //所有請求都被攔截包括靜態資源.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的請求}
}
6.3 攔截器原理
- 根據當前請求,找到 HandlerExecutionChain【可以處理請求的handler以及handler的所有 攔截器】
- 先來順序執行 所有攔截器的 preHandle 方法
- 如果當前攔截器prehandler返回為true。則執行下一個攔截器的preHandle
- 如果當前攔截器返回為false。直接 倒序執行所有已經執行了的攔截器的 afterCompletion;
- **如果任何一個攔截器返回false。直接跳出不執行目標方法 **
- **所有攔截器都返回True。執行目標方法 **
- **倒序執行所有攔截器的postHandle方法。 **
- 前面的步驟有任何異常都會直接倒序觸發 afterCompletion
- 頁面成功渲染完成以后,也會倒序觸發 afterCompletion
7. 文件上傳
7.1 頁面表單
<form method="post" action="/upload" enctype="multipart/form-data"><input type="file" name="file"><br><input type="submit" value="提交">
</form>
7.2 文件上傳代碼
/*** MultipartFile 自動封裝上傳過來的文件* @param email* @param username* @param headerImg* @param photos* @return*/@PostMapping("/upload")public String upload(@RequestParam("email") String email,@RequestParam("username") String username,@RequestPart("headerImg") MultipartFile headerImg,@RequestPart("photos") MultipartFile[] photos) throws IOException {log.info("上傳的信息:email={},username={},headerImg={},photos={}",email,username,headerImg.getSize(),photos.length);if(!headerImg.isEmpty()){//保存到文件服務器,OSS服務器String originalFilename = headerImg.getOriginalFilename();headerImg.transferTo(new File("H:\\cache\\"+originalFilename));}if(photos.length > 0){for (MultipartFile photo : photos) {if(!photo.isEmpty()){String originalFilename = photo.getOriginalFilename();photo.transferTo(new File("H:\\cache\\"+originalFilename));}}}return "main";}
7.3 自動配置原理
- 文件上傳自動配置類-MultipartAutoConfiguration-MultipartProperties
- 自動配置好了 StandardServletMultipartResolver 【文件上傳解析器
- 原理步驟
- 請求進來使用文件上傳解析器判斷(isMultipart)并封裝(resolveMultipart,返回MultipartHttpServletRequest)文件上傳請求
- 參數解析器來解析請求中的文件內容封裝成MultipartFile
- 將request中文件信息封裝為一個Map; MultiValueMap<String, MultipartFile> FileCopyUtils。實現文件流的拷貝
@PostMapping("/upload")public String upload(@RequestParam("email") String email,@RequestParam("username") String username,@RequestPart("headerImg") MultipartFile headerImg,@RequestPart("photos") MultipartFile[] photos)
8. 異常處理
8.1 錯誤處理
8.1.1 默認規則
- 默認情況下,Spring Boot提供/error處理所有錯誤的映射
- 對于機器客戶端,它將生成JSON響應,其中包含錯誤,HTTP狀態和異常消息的詳細信息。對于瀏覽器客戶端,響應一個“ whitelabel”錯誤視圖,以HTML格式呈現相同的數據
- 要對其進行自定義,添加View解析為error
- 要完全替換默認行為,可以實現
ErrorController
并注冊該類型的Bean定義,或添加ErrorAttributes類型的組件以使用現有機制但替換其內容。 - error/下的4xx,5xx頁面會被自動解析;
8.1.2 定制錯誤處理邏輯
-
自定義錯誤頁
- error/404.html error/5xx.html;有精確的錯誤狀態碼頁面就匹配精確,沒有就找 4xx.html;如果都沒有就觸發白頁
-
@ControllerAdvice+@ExceptionHandler 處理全局異常;底層是 ExceptionHandlerExceptionResolver 支持的
-
@ResponseStatus+自定義異常 ;底層是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底層調用 response.sendError(statusCode, resolvedReason);tomcat發送的/error
-
Spring底層的異常,如 參數類型轉換異常;DefaultHandlerExceptionResolver 處理框架底層的異常。
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
-
自定義實現 HandlerExceptionResolver 處理異常;可以作為默認的全局異常處理規則
-
ErrorViewResolver 實現自定義處理異常;
- response.sendError 。error請求就會轉給controller
- 你的異常沒有任何人能處理。tomcat底層 response.sendError。error請求就會轉給controller
- basicErrorController 要去的頁面地址是 ErrorViewResolver ;
springMVC 統一異常處理 返回JSON數據
引用自:https://blog.csdn.net/qq_38403662/article/details/91418281
需求
在后臺開發中,難免會存在一些異常,如果我們在controller中一個一個的去try catch處理,會很繁瑣,并且不好維護;如果在web.xml配置錯誤頁面,會導致返回一個試圖給前臺,對于前后端分離的不太友好,前臺無法解析,這明顯不是我們想要的,我們需要的是返回串JSON的錯誤碼給前臺;
@ControllerAdvice
從spring3.2開始,增加了新注解@ControllerAdvice,控制器增強的注解。其原理是使用AOP對Controller控制器進行增強(前置增強、后置增強、環繞增強,AOP原理請自行查閱);這樣我們就可以自行對控制器的方法進行調用前(前置增強)和調用后(后置增強)的處理。
如果要返回JSON格式,只需要創建一個ExceptionHandler的class 用@ControllerAdvice注解,里面通過@ExceptionHandler來注解了的方法處理數據,處理中將Exception轉換為Json格式返回即可。
代碼
package com.XXX.XXXimport com.XXXX.common.GenericResponse;
import com.XXXX.common.ServiceError;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;/*** 招生統一異常處理*/
@ControllerAdvice
public class XXXXExceptionHandler {private final Logger logger = LoggerFactory.getLogger(XXXXExceptionHandler.class);/*** 運行時異常* @param runtimeException* @return */@ExceptionHandler(RuntimeException.class)@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)@ResponseBodypublic GenericResponse runtimeExceptionHandler(RuntimeException runtimeException) {logger.error(runtimeException.getMessage());return GenericResponse.response(ServiceError.UN_KNOW_ERROR,"系統運行異常");}/*** 空指針異常* @param ex* @return*/@ExceptionHandler(NullPointerException.class)@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)@ResponseBodypublic GenericResponse nullPointerExceptionHandler(NullPointerException ex) {logger.error(ex.getMessage());return GenericResponse.response(ServiceError.UN_KNOW_ERROR,"系統空指針異常");}/*----- REQUEST ERROR -----*///400錯誤@ExceptionHandler({HttpMessageNotReadableException.class})@ResponseStatus(HttpStatus.BAD_REQUEST)@ResponseBodypublic GenericResponse requestNotReadable(HttpMessageNotReadableException ex){logger.error(ex.getMessage());return GenericResponse.response(ServiceError.UN_KNOW_ERROR,"參數格式錯誤(缺少分隔符或結束標簽)");}//400錯誤@ExceptionHandler({TypeMismatchException.class})@ResponseStatus(HttpStatus.BAD_REQUEST)@ResponseBodypublic GenericResponse requestTypeMismatch(TypeMismatchException ex){logger.error(ex.getMessage());return GenericResponse.response(ServiceError.UN_KNOW_ERROR,"參數類型不匹配");}//400錯誤@ExceptionHandler({MissingServletRequestParameterException.class})@ResponseStatus(HttpStatus.BAD_REQUEST)@ResponseBodypublic GenericResponse requestMissingServletRequest(MissingServletRequestParameterException ex){logger.error(ex.getMessage());return GenericResponse.response(ServiceError.UN_KNOW_ERROR,"缺少請求參數");}//405錯誤@ExceptionHandler({HttpRequestMethodNotSupportedException.class})@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)@ResponseBodypublic GenericResponse request405(){return GenericResponse.response(ServiceError.UN_KNOW_ERROR,"不支持該請求方式");}}
說明
- 需要注意的是該類一定要存放到能夠被spring掃描到的地方
- 其中GenericResponse 及 ServiceError是自定義的響應結果,可修改為自己定義的(因為各自的項目的響應結果都不一樣,這邊就不貼出來了)
- @ExceptionHandler表示需要處理的異常類型
- @ResponseStatus表示修改返回的http狀態
- @ResponseBody表示返回JSON、因為GenericResponse為一個實體對象,所以最終將對象
轉為JSON再返回給前臺
效果圖
后臺controller拋出異常,無需try catch
8.1.3 異常處理自動配置原理
- ErrorMvcAutoConfiguration 自動配置異常處理規則
- 容器中的組件:類型:DefaultErrorAttributes -> id:errorAttributes
- public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
- DefaultErrorAttributes:定義錯誤頁面中可以包含哪些數據。
- 容器中的組件:類型:BasicErrorController --> id:
basicErrorController(json+白頁 適配響應)- 處理默認 /error 路徑的請求;頁面響應 new ModelAndView(“error”, model);
- 容器中有組件 View->id是error;(響應默認錯誤頁)
- 容器中放組件 BeanNameViewResolver(視圖解析器);按照返回的視圖名作為組件的id去容器中找View對象。
- 容器中的組件:類型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
- 如果發生錯誤,會以HTTP的狀態碼 作為視圖頁地址(viewName),找到真正的頁面
- error/404、5xx.html
如果想要返回頁面;就會找error視圖 【StaticView】。(默認是一個白頁)
8.1.4 異常處理步驟流程
-
執行目標方法,目標方法運行期間有任何異常都會被catch、而且標志當前請求結束;并且用 dispatchException
-
進入視圖解析流程(頁面渲染?) processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
-
mv = processHandlerException;處理handler發生的異常,處理完成返回ModelAndView;
-
遍歷所有的 handlerExceptionResolvers,看誰能處理當前異常【HandlerExceptionResolver處理器異常解析器】
-
系統默認的 異常解析器
-
-
DefaultErrorAttributes先來處理異常。把異常信息保存到request域,并且返回null;
-
默認沒有任何系統默認的異常解析器能處理異常,所以異常會被拋出
-
如果沒有任何人能處理最終底層就會發送 /error 請求。會被底層的BasicErrorController處理。
-
解析錯誤視圖;遍歷所有的 ErrorViewResolver 看誰能解析。
-
默認的 DefaultErrorViewResolver ,作用是把響應狀態碼作為錯誤頁的地址,error/500.html
-
模板引擎最終響應這個頁面 error/500.html
-
9. Web原生組件注入(Servlet、Filter、Listener)
9.1 使用Servlet API
@ServletComponentScan(basePackages = “com.atguigu.admin”) :指定原生Servlet組件都放在那里
@WebServlet(urlPatterns = “/my”):效果:直接響應,沒有經過Spring的攔截器?
@WebFilter(urlPatterns={“/css/“,”/images/”})
@WebListener
推薦可以這種方式;
擴展:DispatchServlet 如何注冊進來
+ 容器中自動配置了 DispatcherServlet 屬性綁定到 WebMvcProperties;對應的配置文件配置項是 spring.mvc。
+ 通過 ServletRegistrationBean 把 DispatcherServlet 配置進來。
+ 默認映射的是 / 路徑。
Tomcat-Servlet;
多個Servlet都能處理到同一層路徑,精確優選原則
A: /my/
B: /my/1
9.2. 使用RegistrationBean
ServletRegistrationBean
, FilterRegistrationBean
, and ServletListenerRegistrationBean
@Configuration
public class MyRegistConfig {@Beanpublic ServletRegistrationBean myServlet(){MyServlet myServlet = new MyServlet();return new ServletRegistrationBean(myServlet,"/my","/my02");}@Beanpublic FilterRegistrationBean myFilter(){MyFilter myFilter = new MyFilter();
// return new FilterRegistrationBean(myFilter,myServlet());FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));return filterRegistrationBean;}@Beanpublic ServletListenerRegistrationBean myListener(){MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();return new ServletListenerRegistrationBean(mySwervletContextListener);}
}
10. 嵌入式Servlet容器
10.1 切換嵌入式Servlet容器
- 默認支持的webServer
Tomcat
,Jetty
,orUndertow
ServletWebServerApplicationContext 容器啟動尋找ServletWebServerFactory 并引導創建服務器
- 切換服務器
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions>
</dependency>
- 原理
- SpringBoot應用啟動發現當前是Web應用。web場景包-導入tomcat
- web應用會創建一個web版的ioc容器 ServletWebServerApplicationContext
- ServletWebServerApplicationContext 啟動的時候尋找 ServletWebServerFactory(Servlet 的web服務器工廠—> Servlet 的web服務器)
- SpringBoot底層默認有很多的WebServer工廠;TomcatServletWebServerFactory, JettyServletWebServerFactory, or UndertowServletWebServerFactory
- 底層直接會有一個自動配置類。ServletWebServerFactoryAutoConfiguration
- ServletWebServerFactoryAutoConfiguration導入了ServletWebServerFactoryConfiguration(配置類)
- ServletWebServerFactoryConfiguration 配置類 根據動態判斷系統中到底導入了那個Web服務器的包。(默認是web-starter導入tomcat包),容器中就有 TomcatServletWebServerFactory
- TomcatServletWebServerFactory 創建出Tomcat服務器并啟動;TomcatWebServer 的構造器擁有初始化方法initialize—this.tomcat.start();
- 內嵌服務器,就是手動把啟動服務器的代碼調用(tomcat核心jar包存在)
10.2 定制Servlet容器
- 實現 WebServerFactoryCustomizer
- 把配置文件的值和ServletWebServerFactory 進行綁定
- 修改配置文件 server.xxx
- 直接自定義 ConfigurableServletWebServerFactory
xxxxxCustomizer:定制化器,可以改變xxxx的默認規則
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {@Overridepublic void customize(ConfigurableServletWebServerFactory server) {server.setPort(9000);}}
11. 定制化原理
11.1 定制化的常見方式
- 修改配置文件;
- xxxxxCustomizer;
- 編寫自定義的配置類 xxxConfiguration;
- @Bean替換、增加容器中默認組件;視圖解析器
- Web應用 編寫一個配置類實現 WebMvcConfigurer 即可定制化web功能;
- @Bean給容器中再擴展一些組件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer
- @EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有規則全部自己重新配置; 實現定制和擴展功能
- 原理
- WebMvcAutoConfiguration 默認的SpringMVC的自動配置功能類。靜態資源、歡迎頁…
- 一旦使用 @EnableWebMvc 、。會 @Import(DelegatingWebMvcConfiguration.class)
- DelegatingWebMvcConfiguration 的 作用,只保證SpringMVC最基本的使用
- 把所有系統中的 WebMvcConfigurer 拿過來。所有功能的定制都是這些 WebMvcConfigurer 合起來一起生效
- 自動配置了一些非常底層的組件。RequestMappingHandlerMapping、這些組件依賴的組件都是從容器中獲取
- public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
- WebMvcAutoConfiguration 里面的配置要能生效 必須 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
- @EnableWebMvc 導致了 WebMvcAutoConfiguration 沒有生效。
- 原理
- … …
11.2 原理分析套路
場景starter - xxxxAutoConfiguration - 導入xxx組件 - 綁定xxxProperties – 綁定配置文件項
mponent;
@Component
public class CustomizationBean implements WebServerFactoryCustomizer {
@Override
public void customize(ConfigurableServletWebServerFactory server) {server.setPort(9000);
}
}
# 11. 定制化原理
## 11.1 定制化的常見方式
+ 修改配置文件;
+ xxxxxCustomizer;
+ 編寫自定義的配置類 xxxConfiguration;
+ @Bean替換、增加容器中默認組件;視圖解析器
+ Web應用 編寫一個配置類實現 WebMvcConfigurer 即可定制化web功能;
+ @Bean給容器中再擴展一些組件
```java
@Configuration
public class AdminWebConfig implements WebMvcConfigurer
- @EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有規則全部自己重新配置; 實現定制和擴展功能
- 原理
- WebMvcAutoConfiguration 默認的SpringMVC的自動配置功能類。靜態資源、歡迎頁…
- 一旦使用 @EnableWebMvc 、。會 @Import(DelegatingWebMvcConfiguration.class)
- DelegatingWebMvcConfiguration 的 作用,只保證SpringMVC最基本的使用
- 把所有系統中的 WebMvcConfigurer 拿過來。所有功能的定制都是這些 WebMvcConfigurer 合起來一起生效
- 自動配置了一些非常底層的組件。RequestMappingHandlerMapping、這些組件依賴的組件都是從容器中獲取
- public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
- WebMvcAutoConfiguration 里面的配置要能生效 必須 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
- @EnableWebMvc 導致了 WebMvcAutoConfiguration 沒有生效。
- 原理
- … …
11.2 原理分析套路
場景starter - xxxxAutoConfiguration - 導入xxx組件 - 綁定xxxProperties – 綁定配置文件項