3、Web 功能大全及源码解析

该栏目会系统的介绍SpringBoot的知识体系,共分为基础部分、源代码和综合实例等模块



SpringMVC 自动配置

  • 静态资源访问:包括index.html、Favicon及其其他静态资源
  • 消息转换器:HttpMessageConverters
  • 将请求数据绑定到JavaBean:ConfigurableWebBindingInitializer
  • 格式转换器:Converter,GenericConverter,Formatter
  • 视图解析器:BeanNameViewResolver

静态资源访问

1、概述

  • :当被访问的静态资源放在 /static/public/resources/META-INF/resources 可直接根据访问路径被访问

2、访问

  • : 当前根路径 / + [ 访问前缀 ] + 静态资源名
spring:
  mvc:
  	# 修改静态资源前缀,访问:当前项目 + static-path-pattern + 静态资源名
    static-path-pattern: /res/**  # 这个会导致html页面和favicon.ico不能被访问
  resources:
  	# 修改静态资源路径
    static-locations: [classpath:/path/]

3、支持欢迎页

  • : 将 index.html 放在静态资源路径下(例如:/static/index.html)

4、支持自定义Favicon

  • : 将 favicon.ico 放在静态资源路径下(例如:/static/favicon.ico)

5、访问原理

静态资源访问原理

  • SpringBoot 自动配置加载 WebMvcAutoConfiguration类
  • WebMvcAutoConfiguration类 里有个 WebMvcAutoConfigurationAdapter内部类 用于加载相关资源
  • WebMvcAutoConfigurationAdapter内部类 只有一个有参构造函数,其构造所需参数对象都从容器中获取
  • WebMvcAutoConfigurationAdapter内部类 的 addResourceHandlers() 用来添加资源处理默认规则
  • WebMvcAutoConfiguration类 里添加 WelcomePageHandlerMapping组件 用来处理欢迎页面

请求处理

1、请求访问案例

  • 自定义处理器
@RestController
public class UserController {

    @GetMapping("user")
    public String getUser() {
        return "get";
    }

    @PostMapping("user")
    public String postUser() {
        return "post";
    }

    @DeleteMapping("user")
    public String deleteUser() {
        return "delete";
    }

    @PutMapping("user")
    public String putUser() {
        return "put";
    }

}
  • 配置参数
# 启动隐藏方法过滤处理 
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true
  • 访问页面
<!--网页表单没有PUT/DELETE方式,所以需要提交个隐藏属性_method-->
<!DOCTYPE html>
<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <title>欢迎页面</title>
	</head>
	<body>
	    <form action="/user" method="get">
	        <button type="submit">Get请求</button>
	    </form>
	
	    <form action="/user" method="post">
	        <button type="submit">post请求</button>
	    </form>
	
	    <form action="/user" method="post">
	        <input name="_method" value="DELETE" type="hidden"/>
	        <button type="submit">delete请求</button>
	    </form>
	
	    <form action="/user" method="post">
	        <input name="_method" value="PUT" type="hidden"/>
	        <button type="submit">put请求</button>
	    </form>
	</body>
</html>

2、参数类型

  • 相关注解
注解功能
@PathVariable从Rest请求路径中获取参数
@RequestHeader请求头
@RequestAttribute获取request域属性
@RequestParam请求参数
@MatrixVariable矩阵变量
@CookieValue获取Cookie的参数
@RequestBody请求体
// 请求参数注解测试
@RestController
public class RequestParamController {

    // 获取路径中的参数
    @GetMapping("/getVariable/{id}")
    public void getPathVariable(
            // 获取路径中单个指定参数
            @PathVariable("id") String id,
            // 获取所有参数
            @PathVariable Map<String, String> params) {
        System.out.println(params);
    }

    // 获取请求头
    @GetMapping("/getHeader")
    public void getRequestHeader(
            // 获取路径中单个指定请求头
            @RequestHeader("User-Agent") String userAgent,
            // 获取所有请求头
            @RequestHeader Map<String, String> headers) {
        System.out.println(headers);
    }

    // 获取请求参数
    @GetMapping("/getParam")
    public void getParam(
            // 获取指定请求参数
            @RequestParam("name") String name,
            // 获取多值请求参数
            @RequestParam("inters") List<String> inters,
            // 获取所有请求参数
            @RequestParam Map<String, String> params) {
        System.out.println(params);
    }

    // 获取cookie
    @GetMapping("/getCookie")
    public void getCookie(
            // 获取指定cookie值
            @CookieValue("JSESSIONID") String gc,
            // 获取指定cookie对象
            @CookieValue("JSESSIONID") Cookie cookie
    ) {
        System.out.println(cookie);
    }

    // 获取请求体
    @PostMapping("/getRequestBody")
    public void getRequestBody(@RequestBody User user) {
        System.out.println(user);
    }

    // 获取请求域中的参数
    @GetMapping("/getRequestAttribute")
    public void getRequestAttribute(@RequestAttribute("code") String code) {
        System.out.println(code);
    }

    // 获取矩阵变量的值 如:http://localhost:8888/getMatrix1/cell;code=200;var=666
    @GetMapping("/getMatrix1/{path}")
    public void getMatrix1(@MatrixVariable("code") String code,
                           @MatrixVariable("var") String var) {
        System.out.println(code + "--" + var);
    }

    // 获取矩阵变量的值 如:http://localhost:8888/getMatrix2/cell;code=200/goto;code=300
    @GetMapping("/getMatrix2/{path1}/{path2}")
    public void getMatrix2(
    			@MatrixVariable(value = "code", pathVar = "path1") String code1,
    			@MatrixVariable(value = "code", pathVar = "path2") String code2) {
        System.out.println(code1 + "--" + code2);
    }
    
}


// 启动矩阵变量功能
@Configuration
public class WebConfig {

    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper pathHelper = new UrlPathHelper();
                // 不移除;后面的内容,才能使矩阵变量功能才能生效
                pathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(pathHelper);
            }
        };
    }
    
}
  • 相关对象
对象描述
ServletRequest请求域对象
MultipatRequest文件上传对象
HttpSessionSession域对象PushBuilder
InputStream字节输入流对象
Reader字符输入流
HttpMethod请求方法对象
MapMap集合对象
Model模型
RedirectAttributes用于重定向携带数据
ServletResponseservlet响应对象
SessionStatussession状态
自定义对象用户自定义对象
@RestController
@RequestMapping("/object/")
public class RequestParamObject {

    /**
     * 使用ServletRequest对象
     */
    @GetMapping("getServletRequest")
    public void getServletRequest(ServletRequest request, ServletResponse response) throws Exception {
        // 请求转发
        request.setAttribute("code", "200");
        request.getRequestDispatcher("/param/getAttribute").forward(request, response);
    }

    /**
     * 使用HttpSession对象
     */
    @GetMapping("getSession")
    public void getSession(HttpSession session) {
        System.out.println("session对象:" + session);
    }

    /**
     * 使用SessionStatus对象
     */
    @GetMapping("getSessionStatus")
    public void getSessionStatus(SessionStatus status) {
        System.out.println("session状态对象:" + status);
    }

    /**
     * 使用ServletResponse对象
     */
    @GetMapping("getServletResponse")
    public void getServletResponse(ServletResponse response) throws IOException {
        final PrintWriter writer = response.getWriter();
        writer.write("Hello Java");
    }

    /**
     * 使用HttpMethod对象
     */
    @GetMapping("getHttpMethod")
    public void getHttpMethod(HttpMethod method) {
        System.out.println("请求方法对象:" + method);
    }

    /**
     * 使用字节输入流对象
     */
    @GetMapping("getInputStream")
    public void getInputStream(InputStream inputStream) {
        System.out.println("字节输入流对象:" + inputStream);
    }

    /**
     * 使用字符输入流对象
     */
    @GetMapping("getReader")
    public void getReader(Reader reader) {
        System.out.println("字符输入流对象:" + reader);
    }

    /**
     * 使用文件上传对象
     */
    @GetMapping("getMultipartFile")
    public void getMultipartFile(MultipartFile multipartFile) {
        System.out.println("文件上传对象:" + multipartFile);
    }

    /**
     * 使用Map对象
     */
    @GetMapping("getMapObject")
    public void getMapObject(@RequestParam Map<String, String> map) {
        System.out.println("map对象:" + map);
    }

    /**
     * 使用Model对象
     */
    @GetMapping("getModel")
    public void getModel(Model model) {
        System.out.println("Model对象:" + model);
    }

    /**
     * 使用重定向数据对象
     */
    @GetMapping("getRedirectAttributes")
    public void getRedirectAttributes(RedirectAttributes redirectAttributes) {
        System.out.println("redirectAttributes对象:" + redirectAttributes);
    }

}

3、请求访问规定

  • SpringBoot 启动时自动配置 WebMvcAutoConfiguration 类,在该类中注入了 OrderedHiddenHttpMethodFilter 过滤器组件,在该过滤器的过滤方法中规定了 RESTFUL 风格的请求规则
// SpringBoot启动时自动配置 WebMvcAutoConfiguration 类,在该类里添加会添加
// OrderedHiddenHttpMethodFilter 组件
public class WebMvcAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
	public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
		
		// 点击进入其父类 HiddenHttpMethodFilter
		public class HiddenHttpMethodFilter extends OncePerRequestFilter {

			protected void doFilterInternal(){
				// 主要功能:会检查表单提交的方式是否包含 _method = PUT/DELETE,如果有,
				//         则将原生请求转换为 RequestWrapper 类,并传递给其他请求
			}
		}
		
	}	

}

4、请求映射

  • 在 DispatcherServlet 类中的 doDispatch() 方法处理请求,最终通过 RequestMappingHandlerMapping 类,获取能处理该请求的处理器
// 注:SpringBoot 默认加载 RequestMappingHandlerMapping类,该类
// 保存了所有 @RequestMapping 和 handler 的映射规则

// DispatcherServlet类 重写了父类的 doService()方法,该方法在处理请求的相关方法时被调用
public class DispatcherServlet extends FrameworkServlet {

	protected void doService() {
		// 调用该方法处理请求映射
		doDispatch(request, response);
	}

	protected void doDispatch() {
		// 最终调用该方法去获取请求对应的处理器
		mappedHandler = getHandler(processedRequest);
	}

}

5、请求参数绑定

  • 根据处理器获取处理器适配器, 使用处理器适配器遍历所有参数解析器,将处理器所需的参数与请求中携带的参数进行绑定
// 注:在 DispatcherServlet 类的 doDispatch() 方法中通过 getHandler() 方法已经获取了对应的处理器

public class DispatcherServlet extends FrameworkServlet {

	protected void doDispatch() {
		// 根据获得的 Handler 对象,遍历所有处理器适配器,判断是否有支持处理该 Handler 对象的
		// HandlerAdapter对象 (处理器适配器),默认有四种
        	// RequestMappingHandlerAdapter:支持方法上标注@RequestMapping 
			// HandlerFunctionAdapter:支持函数式编程的
			// HttpRequestHandlerAdapter:HTTP请求处理器适配器
			// SimpleControllerHandlerAdapter:简单控制器处理器适配器
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        
        // 调用处理器适配器的 handle() 方法执行目标方法,点击进入 HandlerAdapter 接口,然后进入
        // AbstractHandlerAdapter -> RequestMappingHandlerAdapter类 的 handleInternal() 方法
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	}
	
}

public class RequestMappingHandlerAdapter {

    protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        
        // 对目标方法封装参数解析器(ArgumentResolver),默认加载26种
        // 返回值处理器(ReturnValueHandler),默认15种
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.argumentResolvers != null) {
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
        }
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }	

        // 执行和处理请求,调用 ServletInvocableHandlerMethod 类的 invokeAndHandle() 方法
        invocableMethod.invokeAndHandle(webRequest, mavContainer);	  
    }
    
}

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    
    public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        // 将请求的参数与处理器所需的参数进行绑定,最终获取处理结果,点击进入 InvocableHandlerMethod 类
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    }
}

public class InvocableHandlerMethod extends HandlerMethod {
    
    public Object invokeForRequest(NativeWebRequest request,@Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
        // 获取方法参数值,该方法使用循环遍历参数解析器,将请求的数据与处理器对应的参数进行绑定
    	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        // 反射调用目标方法
        return doInvoke(args);
    }
    
    // 通过遍历 26个 参数解析器,将处理器所需的参数一一匹配赋值
    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;
			}
            // 遍历26个参数解析器,判断处理器的参数上是否有 该参数处理器支持处理的 注解
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
                // 根据找到的参数处理器调用其 resolveArgument() 方法处理参数,一般根据参数名从 请求域 中获取参数值
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, 
								this.dataBinderFactory);
			}
			catch (Exception ex) {
				if (logger.isDebugEnabled()) {
					String exMsg = ex.getMessage();
					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
						logger.debug(formatArgumentError(parameter, exMsg));
					}
				}
				throw ex;
			}
		}
		return args;
	}
    
}

6、响应处理

6.1 原理分析

  • 调用能处理 @ResponseBody 注解的返回值处理器,返回值处理器遍历所有的消息转换器(Jackson2HttpMessageConverter),将对象转换为json对象
// web 开发场景中已经引入了 jackson.jar, 在方法中使用 @ResponseBody 注解可以响应 json 数据

public class RequestMappingHandlerAdapter {

    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        // 获取 invocableMethod 对象,该对象中封装了返回值处理器
        ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        if (this.returnValueHandlers != null) {
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
        }

        // invocableMethod 对象调用invokeAndHandle() 方法去执行和处理请求,
        // 最终使用 RequestResponseBodyMethodProcessor 类
        invocableMethod.invokeAndHandle(webRequest, mavContainer);
	}
    
}

// RequestResponseBodyMethodProcessor 类能处理 @ResponseBody 注解的返回值
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    public 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);

        // 使用消息转换器进行写出操作,点击进入 AbstractMessageConverterMethodProcessor 类
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
}

// 遍历的所有消息转换器,将对象转换为 json
public abstract class AbstractMessageConverterMethodProcessor extends 
	AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler {
 
    protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        
        // 内容协商:浏览器请求时会在请求头(Accept)中规则了需要什么内容,但服务器会根据自己响应的能力
        // 与浏览器请求的内容进行协商
        MediaType selectedMediaType = null;
		MediaType contentType = outputMessage.getHeaders().getContentType();
        
        // 遍历所有消息转换器,最后是 Jackson2HttpMessageConverter 支持将 对象 -> MediaType(application/json)
        for (HttpMessageConverter<?> converter : this.messageConverters) {
        }
    }
}

6.2 自定义消息转换器

/**
 * 响应消息实体
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseMessage implements Serializable {

    private int code;

    private String message;

}

/**
 * 消息响应转换器
 */
public class ResponseMessageConverter implements HttpMessageConverter<ResponseMessage> {

    @Override
    public boolean canRead(Class<?> aClass, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class<?> aClass, MediaType mediaType) {
        return aClass.isAssignableFrom(ResponseMessage.class);
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-near");
    }

    @Override
    public ResponseMessage read(Class<? extends ResponseMessage> aClass, 
     	  	HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(ResponseMessage responseMessage, MediaType mediaType, 
    		HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException {
        String message = responseMessage.getCode() + ";" + responseMessage.getMessage();
        httpOutputMessage.getBody().write(message.getBytes());
    }
    
}

/**
 * MVC配置
 */
@Configuration
public class WebConfig {

    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new ResponseMessageConverter());
            }
        };
    }
}

/**
 * 响应消息处理器
 */
@RestController
@RequestMapping("/response/")
public class ResponseMessageController {

    @GetMapping("responseMessage")
    public ResponseMessage responseMessage() {
        return new ResponseMessage(200, "Success!!");
    }

}

7、视图解析

7.1 使用 Thymeleaf 框架

  • 引入 Thymeleaf 场景
  • 自动配置相关功能 SpringTemplateEngine、ThymeleafViewResolver
  • 页面配置名称空间

7.2 原理解析

// 在 DispatcherServlet 类的 doDispatch() 方法调用 invokeHandlerMethod() 方法返回 modelAndView 对象
// 处理器返回的是String类型,在处理结果时由 ViewNameMethodReturnValueHandler类 处理,
// 它将数据和视图地址包装在了 ModelAndViewContainer 对象中

public class DispatcherServlet extends FrameworkServlet {

	protected void doDispatch() {
        // 进行页面渲染逻辑,最终会调用 render()
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}
    
    // 根据处理器响应的 String 返回值使用 ContentNegotiationViewResolver 得到视图对象(定义了页面的渲染逻辑)
    protected void render(ModelAndView mv, HttpServletRequest request, 
    		HttpServletResponse response) throws Exception {
       
    }
	
}

拦截器

1、概述

  • :用于对请求的预处理和后处理

2、原理解析

  • 在 DispatcherServlet 类的 doDispatch() 方法调用 getHandler() 获取 HandlerExecutionChain 对象,该对象保存了处理请求的处理器和拦截器
  • 先来顺序执行所有拦截器的 preHandle() 方法,如果当前拦截器prehandler()方法返回为 true。则执行下一个拦截器的 preHandle() 方法,如果当前拦截器返回为 false。直接倒序执行所有已经执行了的拦截器的 afterCompletion() 方法
  • 所有拦截器都返回 true,则执行目标方法,目标完方法执行完后,倒序执行所有拦截器的 postHandle() 方法
  • 前面的任何步骤有异常都会直接倒序触发 afterCompletion() 方法,页面成功渲染完成以后,也会倒序触发 afterCompletion() 方法
    拦截器原理图

3、案例

/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 */
public class LoginInterceptor implements HandlerInterceptor {

    // 目标方法执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
    		Object handler) throws Exception {
        final HttpSession session = request.getSession();
        String loginName = (String) session.getAttribute("loginName");
        if (loginName != null) {
            return true;
        }
        request.setAttribute("msg","请先登录");
        request.getRequestDispatcher("/").forward(request, response);
        return false;
    }

    // 目标方法执行完成以后
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
    		Object handler, ModelAndView modelAndView) throws Exception {

    }

    // 页面渲染以后
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
     		Object handler, Exception ex) throws Exception {
    }
}

////////////////////////////////////////

/**
 * 拦截器配置
 */
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/", "/login", "/css/**", "/html/**", "/images/**", "/js/**");
    }
}

文件上传

1、原理分析

  • SpringBoot 自动加载 MultipartAutoConfiguration 类,该类自动配置好了文件上传解析器(StandardServletMultipartResolver 类)
  • 使用文件上传解析器判断并封装文件上传请求(MultipartHttpServletRequest 类)
  • 参数解析器来解析请求中的文件内容封装成 MultipartFile 对象
  • 将文件信息封装成一个 MultiValueMap<String, MultipartFile>,使用 FileCopyUtils 工具类实现文件流的拷贝

2、案例

/**
 * 文件上传处理器
 */
@RestController
public class FileUploadController {

    /**
     * 文件上传
     */
    @GetMapping("/upload")
    public String upload(@RequestPart("titleFile") MultipartFile titleFile,
                         @RequestPart("lives") MultipartFile[] lives) {
        // 单个图片上传
        if (!titleFile.isEmpty()) {
            try {
                final String fileName = titleFile.getOriginalFilename();
                titleFile.transferTo(new File("D:\\" + fileName));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 多个图片上传
        if (lives.length > 0) {
            for (MultipartFile multipartFile : lives) {
                try {
                    final String fileName = multipartFile.getOriginalFilename();
                    multipartFile.transferTo(new File("D:\\" + fileName));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "main";
    }
    
}

/////////////////////////////////////

// 文件上传配置
spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB

异常处理

1、异常处理规则

  • 一旦控制器中报出了异常,SpringBoot 会以 /error的 ur l发送给 BasicExceptionController 控制器,然后跳转到默认显示异常的全局错误页面来展示异常信息
  • 没有使用 ThymeLeaf 模板,SpringBoot 会到静态资源下 /error 文件夹下寻找错误提示页面(400.html、5xx.html)
  • 使用 ThymeLeaf 模板时,SpringBoot 会自动到 templates/error/ 文件夹下寻找错误提示页面

2、异常处理原理

  • SpringBoot自动加载ErrorMvcAutoConfiguration类配置异常处理规则
  • 容器中加载DefaultErrorAttributes类,定义错误页面中可以包含哪些数据
  • 容器中加载BasicErrorController类,处理错误信息,并响应错误页面或json数据
  • 容器中加载DefaultErrorViewResolver类,寻找templates/error下静态文件夹下的错误提示页面(4xx、5xx等)进行响应

3、自定义全局异常处理

/**
 * 全局异常处理器类
 */
@ControllerAdvice
public class GlobalExceptionController {

    // 标志了该注解才能被ExceptionHandlerExceptionResolver异常处理器异常解析器发现,并将异常信息传递给它进行处理
    @ExceptionHandler
    public String handlerException(Model model, Exception e) {
        model.addAttribute("message", e.getMessage());
        return "error"; // 跳转到模板页面
    }

}

// 在templates/error.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>系统内部异常</h1>
    <h2 th:text="${message}"></h2>
</body>
</html>

原生 Servlet 组件

1、相关注解

  • @ServletComponentScan:扫描原生Servlet组件
  • @WebServlet:注解servlet组件
  • @WebFilter:注解filter组件
  • @WebListener:注解listener组件

2、案例

//////////////////// 使用注解方式 ////////////////////////////////////

/**
 * 定义Servlet
 */
@WebServlet(urlPatterns = "/handler")
public class HandlerServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        System.out.println("处理请求...");
        resp.getWriter().write("Hello Servlet");
    }

}

// 在启动类中添加Servlet组件扫描
@ServletComponentScan(basePackages = {"com.itnear.componet"})

//////////////////// 使用 RegistrationBean 方式 ////////////////////////////////////

/**
 * 配置自定义Servlet组件
 */
@Configuration
public class WebServletConfig {

    @Bean
    public ServletRegistrationBean<HttpServlet> handlerServlet() {
        WebServlet webServlet = new WebServlet();
        return new ServletRegistrationBean<>(webServlet, "/web");
    }

}

/**
 * 定义Server
 */
public class WebServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        System.out.println("处理请求");
        resp.getWriter().write("Hello Web Servlet");
    }

}

Web 容器

  • SpringBoot 应用启动发现当前是 web 应用,web 应用会创建一个 web 版的 IOC 容器(ServletWebServerApplicationContext)
  • 该容器启动时会加载 web 服务器工厂(ServletWebServerFactory)
  • 因为我们引入的是 tomcat 的场景,所以该 web 服务工厂为 TomcatServletWebServerFactory ,接着创建 Tomcat 服务器并启动
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值