五、Web開發

文章目錄

  • 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 攔截器原理

  1. 根據當前請求,找到 HandlerExecutionChain【可以處理請求的handler以及handler的所有 攔截器】
  2. 先來順序執行 所有攔截器的 preHandle 方法
    1. 如果當前攔截器prehandler返回為true。則執行下一個攔截器的preHandle
    2. 如果當前攔截器返回為false。直接 倒序執行所有已經執行了的攔截器的 afterCompletion;
  3. **如果任何一個攔截器返回false。直接跳出不執行目標方法 **
  4. **所有攔截器都返回True。執行目標方法 **
  5. **倒序執行所有攔截器的postHandle方法。 **
  6. 前面的步驟有任何異常都會直接倒序觸發 afterCompletion
  7. 頁面成功渲染完成以后,也會倒序觸發 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 【文件上傳解析器
    • 原理步驟
      1. 請求進來使用文件上傳解析器判斷(isMultipart)并封裝(resolveMultipart,返回MultipartHttpServletRequest)文件上傳請求
      2. 參數解析器來解析請求中的文件內容封裝成MultipartFile
      3. 將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());
      在這里插入圖片描述
  • 自定義實現 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,"不支持該請求方式");}}

說明

  1. 需要注意的是該類一定要存放到能夠被spring掃描到的地方
  2. 其中GenericResponse 及 ServiceError是自定義的響應結果,可修改為自己定義的(因為各自的項目的響應結果都不一樣,這邊就不貼出來了)
  3. @ExceptionHandler表示需要處理的異常類型
  4. @ResponseStatus表示修改返回的http狀態
  5. @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 異常處理步驟流程

  1. 執行目標方法,目標方法運行期間有任何異常都會被catch、而且標志當前請求結束;并且用 dispatchException

  2. 進入視圖解析流程(頁面渲染?) processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

  3. mv = processHandlerException;處理handler發生的異常,處理完成返回ModelAndView;

    1. 遍歷所有的 handlerExceptionResolvers,看誰能處理當前異常【HandlerExceptionResolver處理器異常解析器】
      在這里插入圖片描述

    2. 系統默認的 異常解析器
      在這里插入圖片描述

  4. DefaultErrorAttributes先來處理異常。把異常信息保存到request域,并且返回null;

  5. 默認沒有任何系統默認的異常解析器能處理異常,所以異常會被拋出

    1. 如果沒有任何人能處理最終底層就會發送 /error 請求。會被底層的BasicErrorController處理。

    2. 解析錯誤視圖;遍歷所有的 ErrorViewResolver 看誰能解析。
      在這里插入圖片描述

    3. 默認的 DefaultErrorViewResolver ,作用是把響應狀態碼作為錯誤頁的地址,error/500.html

    4. 模板引擎最終響應這個頁面 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
    • TomcatJetty,or Undertow
  • 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,所有規則全部自己重新配置; 實現定制和擴展功能
    • 原理
      1. WebMvcAutoConfiguration 默認的SpringMVC的自動配置功能類。靜態資源、歡迎頁…
      2. 一旦使用 @EnableWebMvc 、。會 @Import(DelegatingWebMvcConfiguration.class)
      3. DelegatingWebMvcConfiguration 的 作用,只保證SpringMVC最基本的使用
        • 把所有系統中的 WebMvcConfigurer 拿過來。所有功能的定制都是這些 WebMvcConfigurer 合起來一起生效
        • 自動配置了一些非常底層的組件。RequestMappingHandlerMapping、這些組件依賴的組件都是從容器中獲取
        • public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
      4. WebMvcAutoConfiguration 里面的配置要能生效 必須 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
      5. @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,所有規則全部自己重新配置; 實現定制和擴展功能
    • 原理
      1. WebMvcAutoConfiguration 默認的SpringMVC的自動配置功能類。靜態資源、歡迎頁…
      2. 一旦使用 @EnableWebMvc 、。會 @Import(DelegatingWebMvcConfiguration.class)
      3. DelegatingWebMvcConfiguration 的 作用,只保證SpringMVC最基本的使用
        • 把所有系統中的 WebMvcConfigurer 拿過來。所有功能的定制都是這些 WebMvcConfigurer 合起來一起生效
        • 自動配置了一些非常底層的組件。RequestMappingHandlerMapping、這些組件依賴的組件都是從容器中獲取
        • public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
      4. WebMvcAutoConfiguration 里面的配置要能生效 必須 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
      5. @EnableWebMvc 導致了 WebMvcAutoConfiguration 沒有生效。
  • … …

11.2 原理分析套路

場景starter - xxxxAutoConfiguration - 導入xxx組件 - 綁定xxxProperties – 綁定配置文件項

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/916455.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/916455.shtml
英文地址,請注明出處:http://en.pswp.cn/news/916455.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mysql 二進制安裝常見問題

1. mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory在centos9中升級了libncurses.so的版本為libncurses.so.6&#xff0c;所以找不到libncurses.so.5需要使用軟連接指向libncurses.so.6ln -s /lib6…

OpenLayers 綜合案例-點位聚合

看過的知識不等于學會。唯有用心總結、系統記錄&#xff0c;并通過溫故知新反復實踐&#xff0c;才能真正掌握一二 作為一名摸爬滾打三年的前端開發&#xff0c;開源社區給了我飯碗&#xff0c;我也將所學的知識體系回饋給大家&#xff0c;助你少走彎路&#xff01; OpenLayers…

測試老鳥整理,物流項目系統測試+測試點分析(一)

目錄&#xff1a;導讀 前言一、Python編程入門到精通二、接口自動化項目實戰三、Web自動化項目實戰四、App自動化項目實戰五、一線大廠簡歷六、測試開發DevOps體系七、常用自動化測試工具八、JMeter性能測試九、總結&#xff08;尾部小驚喜&#xff09; 前言 物流項目&#xf…

好的編程語言設計是用簡潔清晰的原語組合復雜功能

首先&#xff0c;函數命名要user friendly&#xff0c;比如最常用的控制臺輸入輸出&#xff0c;input scanf gets read readln readline print println writeline… 我專門詢問了chatgpt&#xff0c;讓它給出流行度百分比最高的組合&#xff08;ai干這個最在行&#xff09;&…

基于springboot的在線購票系統/在線售票系統

用戶&#xff1a;注冊&#xff0c;登錄&#xff0c;影院信息&#xff0c;即將上映&#xff0c;電影信息&#xff0c;新聞公告&#xff0c;取票管理&#xff0c;電影評價管理&#xff0c;我的收藏管理&#xff0c;個人中心管理員&#xff1a;登錄&#xff0c;個人中心&#xff0…

Spring Boot項目打包部署常見問題解決方案

問題一&#xff1a;JAR包缺少主清單屬性 問題描述 在使用 java -jar 命令啟動Spring Boot項目時&#xff0c;遇到以下錯誤&#xff1a; demo-service.jar中沒有主清單屬性問題原因 pom.xml 中 spring-boot-maven-plugin 配置不正確打包時跳過了主清單文件的生成主類&#xff08…

【分享】外國使館雷電綜合防護系統改造方案(一)

1防雷項目設計思想&#xff1a;1.1設計依據&#xff1a;依據中國GB標準與部委頒發的設計規范的要求&#xff0c;該建筑物和大樓內之計算機房等設備都必須有完整完善之防護措施&#xff0c;保證該系統能正常運作。這包括電源供電系統、不間斷供電系統&#xff0c;空調設備、電腦…

數據結構預備知識

在學習數據結構之前&#xff0c;有些知識是很有必要提前知道的&#xff0c;它們包括&#xff1a;集合框架、復雜度和泛型。本篇文章專門介紹這三個東西。1.集合框架1.1 什么是集合框架Java 集合框架(Java Collection Framework)&#xff0c;又被稱為容器&#xff0c;是定義在 j…

【C++】數字cmath庫常用函數

菜鳥傳送門&#xff1a;https://www.runoob.com/cplusplus/cpp-numbers.html 作者廢話&#xff1a;作為一個從業3年的JS人&#xff0c;現在重拾C&#xff0c;雖然眾多語言都有很多相似之處&#xff08;至少算法&#xff0c;數學運算&#xff0c;數據結構等等那些都是相同的&…

神經網絡(第二課第一周)

文章目錄神經網絡&#xff08;第二課第一周&#xff09;&#xff08;一&#xff09;神經網絡的內涵&#xff08;二&#xff09;如何構建神經元層1、tensorflow如何處理數據&#xff08;Tensorflow 是由 Google 開發的機器學習包。&#xff09;2、詳細的一些實驗代碼&#xff0c…

CCF-GESP 等級考試 2025年6月認證C++七級真題解析

1 單選題&#xff08;每題 2 分&#xff0c;共 30 分&#xff09;第1題 已知小寫字母 b 的ASCII碼為98&#xff0c;下列C代碼的輸出結果是&#xff08; &#xff09;。#include <iostream>using namespace std;int main() { char a b ^ 4; cout << a; …

【HarmonyOS】鴻蒙應用開發中常用的三方庫介紹和使用示例

【HarmonyOS】鴻蒙應用開發中常用的三方庫介紹和使用示例 截止到2025年&#xff0c;目前參考官方文檔&#xff1a;訪問 HarmonyOS三方庫中心 。梳理了以下熱門下載量和常用的三方庫。 上述庫的組合&#xff0c;可快速實現網絡請求、UI搭建、狀態管理等核心功能&#xff0c;顯著…

SpringBoot 獲取請求參數的常用注解

SpringBoot 提供了多種注解來方便地從 HTTP 請求中獲取參數以下是主要的注解及其用法&#xff1a;1. RequestParam用于獲取查詢參數(URL 參數)&#xff0c;適用于 GET 請求或 POST 表單提交。GetMapping("/user") public String getUser(RequestParam("id"…

【Linux篇章】Socket 套接字,竟讓 UDP 網絡通信如此絲滑,成為一招致勝的秘籍!

本篇文章將帶大家了解網絡通信是如何進行的&#xff08;如包括網絡字節序&#xff0c;端口號&#xff0c;協議等&#xff09; &#xff1b;再對socket套接字進行介紹&#xff1b;以及一些udp-socket相關網絡通信接口的介紹及使用&#xff1b;最后進行對基于udp的網絡通信&#…

GIF圖像格式

你可能已經知道&#xff0c;GIF 是一種光柵圖像文件格式&#xff0c;它在不損失圖像質量的前提下提供壓縮功能&#xff0c;并且支持動畫和透明度。 GIF 是“Graphics Interchange Format&#xff08;圖形交換格式&#xff09;”的縮寫。由于其良好的兼容性以及在不同應用程序和…

D3.js的力導向圖使用入門筆記

D3.js是一個用于數據可視化的JavaScript庫,廣泛應用于Web端的數據交互式圖形展示 中文文檔&#xff1a;入門 | D3 中文網 一、D3.js核心特點 1、核心思想 將數據綁定到DOM元素&#xff0c;通過數據動態生成/修改可視化圖形。 2、應用場景 交互式圖表&#xff1a;如動態條…

Zookeeper的分布式事務與原子性:深入解析與實踐指南

引言在分布式系統架構中&#xff0c;事務管理和原子性保證一直是極具挑戰性的核心問題。作為分布式協調服務的標桿&#xff0c;Apache Zookeeper提供了一套獨特而強大的機制來處理分布式環境下的原子操作。本文將深入探討Zookeeper如何實現分布式事務的原子性保證&#xff0c;分…

Lua(迭代器)

Lua 迭代器基礎概念Lua 迭代器是一種允許遍歷集合&#xff08;如數組、表&#xff09;元素的機制。迭代器通常由兩個部分組成&#xff1a;迭代函數和狀態控制變量。每次調用迭代函數會返回集合中的下一個元素。泛型 for 循環Lua 提供了泛型 for 循環來簡化迭代器的使用。語法如…

發布 VS Code 擴展的流程:以顏色主題為例

發布 VS Code 擴展的流程&#xff1a;以顏色主題為例 引言&#xff1a;您的 VS Code 擴展在市場中的旅程 Visual Studio Code (VS Code) 的強大擴展性是其廣受歡迎的核心原因之一&#xff0c;它允許開發者通過添加語言支持、調試器和各種開發工具來定制和增強其集成開發環境&…

C++ 多線程(一)

C 多線程&#xff08;一&#xff09;1.std中的thread API 介紹開啟一個線程獲取線程信息API交換兩個線程2.向線程里傳遞參數的方法第一種方式&#xff08;在創建線程的構造函數后攜帶參數&#xff09;第二種方式&#xff08;Lambda&#xff09;第三種方式&#xff08;成員函數&…