SpringBoot2核心技术-核心功能-02-Web开发-05

拦截器

1、HandlerInterceptor 接口

/**
 * 登录检查
 * 1、配置好拦截器要拦截哪些请求
 * 2、把这些配置放在容器中
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 目标方法执行之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public 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
     */
    @Override
    public 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
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行异常{}",ex);
    }
}

2、配置拦截器

/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  //所有请求都被拦截包括静态资源
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); //放行的请求【哪些请求不拦截】
    }
}

 

3、拦截器原理

1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有拦截器】

2、先来顺序执行 所有拦截器的 preHandle方法

  • 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
  • 2、如果当前拦截器返回为false。直接    倒序执行所有已经执行了的拦截器的  afterCompletion;
  • 2.1
  • //在执行triggerAfterCompletion的时候,倒序执行已经执行过的拦截器的afterCompletion方法【interceptorIndex】
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
    		throws Exception {
    
    	HandlerInterceptor[] interceptors = getInterceptors();
    	if (!ObjectUtils.isEmpty(interceptors)) {
    		for (int i = this.interceptorIndex; i >= 0; i--) {
    			HandlerInterceptor interceptor = interceptors[i];
    			try {
    				interceptor.afterCompletion(request, response, this.handler, ex);
    			}
    			catch (Throwable ex2) {
    				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    			}
    		}
    	}
    }
    
    //在执行applyPreHandle的时候,这个时候会把已经执行过的拦截器链的interceptorIndex给记录下来
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    	HandlerInterceptor[] interceptors = getInterceptors();
    	if (!ObjectUtils.isEmpty(interceptors)) {
    		for (int i = 0; i < interceptors.length; i++) {
    			HandlerInterceptor interceptor = interceptors[i];
    			if (!interceptor.preHandle(request, response, this.handler)) {
    				triggerAfterCompletion(request, response, null);
    				return false;
    			}
    			this.interceptorIndex = i;
    		}
    	}
    	return true;
    }

     

3、如果任何一个拦截器返回false。直接跳出不执行目标方法

4、所有拦截器都返回True。执行目标方法

5、倒序执行所有拦截器的postHandle方法。

//倒序执行所有连接器链的postHandle方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
		throws Exception {

	HandlerInterceptor[] interceptors = getInterceptors();
	if (!ObjectUtils.isEmpty(interceptors)) {
		for (int i = interceptors.length - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = interceptors[i];
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
}

6、前面的步骤有任何异常都会直接倒序触发 afterCompletion

7、页面成功渲染完成以后,也会倒序触发 afterCompletion

 

image.png

4、调用链

org.springframework.web.servlet.DispatcherServlet的doDispatch方法
//找到执行的目标方法及过滤器链
mappedHandler = getHandler(processedRequest);
//调用拦截器的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
	return;
}

// Actually invoke the handler 执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

//调用拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
//调用拦截器的afterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);

也就是说调用链
(1)preHandle
(2)handle执行目标方法
(3)postHandle
(4)afterCompletion

image.png

 

7、文件上传

1、页面表单


 

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";
    }

3、自动配置原理

文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties

  • 自动配置好了 StandardServletMultipartResolver   【文件上传解析器】
  • 原理步骤
    • 1、请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求
    • 2、参数解析器来解析请求中的文件内容封装成MultipartFile
    • 3、将request中文件信息封装为一个Map;MultiValueMap

FileCopyUtils。实现文件流的拷贝

    @PostMapping("/upload")
    public String upload(@RequestParam("email") String email,
                         @RequestParam("username") String username,
                         @RequestPart("headerImg") MultipartFile headerImg,
                         @RequestPart("photos") MultipartFile[] photos)

SpringBoot2核心技术-核心功能-02-Web开发-05_第1张图片

8、异常处理

1、错误处理

1、默认规则

  • 默认情况下,Spring Boot提供/error处理所有错误的映射
  • 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
  • SpringBoot2核心技术-核心功能-02-Web开发-05_第2张图片SpringBoot2核心技术-核心功能-02-Web开发-05_第3张图片
  • 要对其进行自定义,添加View解析为error
  • 要完全替换默认行为,可以实现 ErrorController 并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。
  • error/下的4xx,5xx页面会被自动解析;
    • SpringBoot2核心技术-核心功能-02-Web开发-05_第4张图片

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());
    • SpringBoot2核心技术-核心功能-02-Web开发-05_第5张图片
  • 【DefaultHandlerExceptionResolver】
    系统底层默认能够处理的异常包含:
    
    if (ex instanceof HttpRequestMethodNotSupportedException) {
    	return handleHttpRequestMethodNotSupported(
    			(HttpRequestMethodNotSupportedException) ex, request, response, handler);
    }
    else if (ex instanceof HttpMediaTypeNotSupportedException) {
    	return handleHttpMediaTypeNotSupported(
    			(HttpMediaTypeNotSupportedException) ex, request, response, handler);
    }
    else if (ex instanceof HttpMediaTypeNotAcceptableException) {
    	return handleHttpMediaTypeNotAcceptable(
    			(HttpMediaTypeNotAcceptableException) ex, request, response, handler);
    }
    else if (ex instanceof MissingPathVariableException) {
    	return handleMissingPathVariable(
    			(MissingPathVariableException) ex, request, response, handler);
    }
    else if (ex instanceof MissingServletRequestParameterException) {
    	return handleMissingServletRequestParameter(
    			(MissingServletRequestParameterException) ex, request, response, handler);
    }
    else if (ex instanceof ServletRequestBindingException) {
    	return handleServletRequestBindingException(
    			(ServletRequestBindingException) ex, request, response, handler);
    }
    else if (ex instanceof ConversionNotSupportedException) {
    	return handleConversionNotSupported(
    			(ConversionNotSupportedException) ex, request, response, handler);
    }
    else if (ex instanceof TypeMismatchException) {
    	return handleTypeMismatch(
    			(TypeMismatchException) ex, request, response, handler);
    }
    else if (ex instanceof HttpMessageNotReadableException) {
    	return handleHttpMessageNotReadable(
    			(HttpMessageNotReadableException) ex, request, response, handler);
    }
    else if (ex instanceof HttpMessageNotWritableException) {
    	return handleHttpMessageNotWritable(
    			(HttpMessageNotWritableException) ex, request, response, handler);
    }
    else if (ex instanceof MethodArgumentNotValidException) {
    	return handleMethodArgumentNotValidException(
    			(MethodArgumentNotValidException) ex, request, response, handler);
    }
    else if (ex instanceof MissingServletRequestPartException) {
    	return handleMissingServletRequestPartException(
    			(MissingServletRequestPartException) ex, request, response, handler);
    }
    else if (ex instanceof BindException) {
    	return handleBindException((BindException) ex, request, response, handler);
    }
    else if (ex instanceof NoHandlerFoundException) {
    	return handleNoHandlerFoundException(
    			(NoHandlerFoundException) ex, request, response, handler);
    }
    else if (ex instanceof AsyncRequestTimeoutException) {
    	return handleAsyncRequestTimeoutException(
    			(AsyncRequestTimeoutException) ex, request, response, handler);
    }

     

  • 自定义实现 HandlerExceptionResolver 处理异常;可以作为默认的全局异常处理规则
    • SpringBoot2核心技术-核心功能-02-Web开发-05_第6张图片
  • ErrorViewResolver  实现自定义处理异常;
    • response.sendError 。error请求就会转给controller
    • 你的异常没有任何人能处理。tomcat底层 response.sendError。error请求就会转给controller
    • basicErrorController 要去的页面地址是 ErrorViewResolver  ;

@ResponseStatus(value= HttpStatus.FORBIDDEN,reason = "用户数量太多")
public class UserTooManyException extends RuntimeException {

    public  UserTooManyException(){

    }
    public  UserTooManyException(String message){
        super(message);
    }
}

 

3、异常处理自动配置原理

  • ErrorMvcAutoConfiguration  自动配置异常处理规则
    • 容器中的组件:类型:DefaultErrorAttributes -> id:errorAttributes
      • public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
      • DefaultErrorAttributes:定义错误页面中可以包含哪些数据。
      • SpringBoot2核心技术-核心功能-02-Web开发-05_第7张图片
      • SpringBoot2核心技术-核心功能-02-Web开发-05_第8张图片
    • 容器中的组件:类型: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】。(默认是一个白页)

image.png写出去json

SpringBoot2核心技术-核心功能-02-Web开发-05_第9张图片 错误页

4、异常处理步骤流程

1、执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException

2、进入视图解析流程(页面渲染?)

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

3、mv = processHandlerException;处理handler发生的异常,处理完成返回ModelAndView;

  • 1、遍历所有的 handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器
  • image.png
  • 2、系统默认的  异常解析器;
  • SpringBoot2核心技术-核心功能-02-Web开发-05_第10张图片
    • 1、DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;
    • 2、默认没有任何人能处理异常,所以异常会被抛出
      • 1、如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理
      • 2、解析错误视图;遍历所有的  ErrorViewResolver  看谁能解析。
      • image.png
      • 3、默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html
      • 4、模板引擎最终响应这个页面 error/500.html

9、Web原生组件注入(Servlet、Filter、Listener)

1、使用Servlet API

官方文档地址:【https://docs.spring.io/spring-boot/docs/2.4.3/reference/html/spring-boot-features.html#boot-features-embedded-container-servlets-filters-listeners-scanning】

SpringBoot2核心技术-核心功能-02-Web开发-05_第11张图片

@ServletComponentScan(basePackages = "com.atguigu.admin") :指定原生Servlet组件都放在那里

 

@WebServlet(urlPatterns = "/my"):效果:直接响应,没有经过Spring的拦截器?【思考:为什么没有】

@WebFilter(urlPatterns={ "/css/*","/images/*"})

@WebListener

推荐可以这种方式;

扩展:DispatchServlet 如何注册进来

  • 容器中自动配置了  DispatcherServlet  属性绑定到 WebMvcProperties;对应的配置文件配置项是 spring.mvc。
  • 通过 ServletRegistrationBean 把 DispatcherServlet  配置进来。
  • 默认映射的是 / 路径。
  • 下面源代码有详细说明:DispatcherServletAutoConfiguration 自动配置
//DispatcherServlet自动配置
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

	@Configuration(proxyBeanMethods = false)
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	protected static class DispatcherServletConfiguration {
		//配置bean=dispatcherServlet
		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
			dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
			return dispatcherServlet;
		}

		@Bean
		@ConditionalOnBean(MultipartResolver.class)
		@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
		public MultipartResolver multipartResolver(MultipartResolver resolver) {
			// Detect if the user has created a MultipartResolver but named it incorrectly
			return resolver;
		}

	}

    //创建一个Servlet
	@Configuration(proxyBeanMethods = false)
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
				WebMvcProperties webMvcProperties, ObjectProvider multipartConfig) {
			//dispatcherServlet就是上面定义的dispatcherServlet
			//servlet的路径是通过spring.mvc.servlet.path读取出来的。默认的path=/
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
					webMvcProperties.getServlet().getPath());
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());//默认是-1,容器启动的时候,就启动
			multipartConfig.ifAvailable(registration::setMultipartConfig);
			return registration;
		}

	}
}

SpringBoot2核心技术-核心功能-02-Web开发-05_第12张图片

Tomcat-Servlet;

多个Servlet都能处理到同一层路径,精确优选原则

A: /my/

B: /my/1

2、使用RegistrationBean

ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean

@Configuration
public class MyRegistConfig {

    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();

        return new ServletRegistrationBean(myServlet,"/my","/my02");
    }


    @Bean
    public FilterRegistrationBean myFilter(){

        MyFilter myFilter = new MyFilter();
//        return new FilterRegistrationBean(myFilter,myServlet());
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
        return filterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener(){
        MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
        return new ServletListenerRegistrationBean(mySwervletContextListener);
    }
}

 

10、嵌入式Servlet容器

官方文档地址:https://docs.spring.io/spring-boot/docs/2.4.3/reference/html/spring-boot-features.html#boot-features-embedded-container-application-context

1、切换嵌入式Servlet容器

  • 默认支持的webServer
    • Tomcat, Jetty, or Undertow
    • ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并引导创建服务器
  • 切换服务器

image.png


    org.springframework.boot
    spring-boot-starter-web
    
        
            org.springframework.boot
            spring-boot-starter-tomcat
        
    

 

 

  • 原理
    • 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包存在)
  • SpringBoot2核心技术-核心功能-02-Web开发-05_第13张图片
  • @Configuration(proxyBeanMethods = false)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @ConditionalOnClass(ServletRequest.class)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @EnableConfigurationProperties(ServerProperties.class)
    //通过@Import导入进来的
    @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
    		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
    		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
    		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
    public class ServletWebServerFactoryAutoConfiguration {
    
    }
    
    
    org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration
    
    @Configuration(proxyBeanMethods = false)
    class ServletWebServerFactoryConfiguration {
         
    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    	static class EmbeddedTomcat {
    
    		@Bean
    		TomcatServletWebServerFactory tomcatServletWebServerFactory(
    				ObjectProvider connectorCustomizers,
    				ObjectProvider contextCustomizers,
    				ObjectProvider> protocolHandlerCustomizers) {
    			TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    			factory.getTomcatConnectorCustomizers()
    					.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
    			factory.getTomcatContextCustomizers()
    					.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
    			factory.getTomcatProtocolHandlerCustomizers()
    					.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
    			return factory;
    		}
    
    	}
    
    	/**
    	 * Nested configuration if Jetty is being used.
    	 */
    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
    	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    	static class EmbeddedJetty {
    
    		@Bean
    		JettyServletWebServerFactory JettyServletWebServerFactory(
    				ObjectProvider serverCustomizers) {
    			JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
    			factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
    			return factory;
    		}
    
    	}
    
    	/**
    	 * Nested configuration if Undertow is being used.
    	 */
    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    	static class EmbeddedUndertow {
    
    		@Bean
    		UndertowServletWebServerFactory undertowServletWebServerFactory(
    				ObjectProvider deploymentInfoCustomizers,
    				ObjectProvider builderCustomizers) {
    			UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    			factory.getDeploymentInfoCustomizers()
    					.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
    			factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
    			return factory;
    		}
    
    	}
    
    } 

     

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 {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

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  没有生效。
  • ... ...

 

2、原理分析套路

场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties -- 绑定配置文件项

你可能感兴趣的:(spring-boot)