五、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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;成员函数&…