# 04.SpringBoot-Web开发3
# 1.拦截器
# 1.实现
1.编写一个拦截器实现HandlerInterceptor接口
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
//目标方法执行之前执行
@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 user = session.getAttribute("loginUser");
if(user==null){
session.setAttribute("msg", "请先登录");
response.sendRedirect("/");
return false;
}
return true;
}
//目标方法执行之后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("postHandle执行{}",modelAndView);
}
//页面渲染之后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("afterCompletion执行异常{}",ex);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2.拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors()),指定拦截规则(注意,如果是拦截所有,静态资源也会被拦截】
@Configuration
public class registerInterceptor implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**","/error");
}
}
2
3
4
5
6
7
8
9
# 2.原理

1.根据当前请求,找到 HandlerExecutionChain(可以处理请求的handler以及handler的所有拦截器)
2.先顺序执行所有拦截器的prehandle()方法。
如果当前拦截器
prehandle()返回true,继续执行下一个拦截器的此方法如果返回
false,直接倒序执行所有已经执行了的afterCompletion()方法
3.如果任何一个拦截器返回false,直接跳出不执行目标方法
4.所有拦截器返回true,执行目标方法
5.执行完目标方法开始倒序执行所有拦截器的postHandle方法
6.前面步骤有任何异常,都会直接倒叙触发已经执行了的拦截器的afterCompletion()
7.页面渲染成功也会倒叙触发afterCompletion
DispatcherServlet中涉及到HandlerInterceptor的地方:
public class DispatcherServlet extends FrameworkServlet {
...
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;
...
//该方法内调用HandlerInterceptor的preHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
//该方法内调用HandlerInterceptor的postHandle()
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//该方法内调用HandlerInterceptor接口的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//该方法内调用HandlerInterceptor接口的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
...
}
}
private void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, Exception ex) throws Exception {
if (mappedHandler != null) {
//该方法内调用HandlerInterceptor接口的afterCompletion方法
mappedHandler.triggerAfterCompletion(request, response, ex);
}
throw ex;
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
...
if (mappedHandler != null) {
//该方法内调用HandlerInterceptor接口的afterCompletion方法
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class HandlerExecutionChain {
...
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
//HandlerInterceptor的preHandle方法
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
//HandlerInterceptor接口的postHandle方法
interceptor.postHandle(request, response, this.handler, mv);
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
//HandlerInterceptor接口的afterCompletion方法
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 2.文件上传
# 1.实现
参考表单
<form role="form" th:action="@{/upload}" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="exampleInputEmail1">邮箱</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">名字</label>
<input type="text" name="username" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<div class="form-group">
<label for="exampleInputFile">头像</label>
<input type="file" name="headerImg" id="exampleInputFile">
</div>
<div class="form-group">
<label for="exampleInputFile">生活照</label>
<input type="file" name="photos" multiple>
</div>
<div class="checkbox">
<label>
<input type="checkbox"> Check me out
</label>
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
controller
@GetMapping("/form_layouts")
public String form() {
return "form/form_layouts";
}
@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()){
String headOldName = headerImg.getOriginalFilename();
//获取前缀
String suffix=headOldName.substring(headOldName.lastIndexOf("."));
//拼接新文件名
String newHeadName= UUID.randomUUID().toString()+suffix;
//保存文件
headerImg.transferTo(new File("D:\\images"+File.separator + newHeadName));
}
for (MultipartFile photo : photos) {
if (!photo.isEmpty()){
String photoOldName = photo.getOriginalFilename();
//获取前缀
assert photoOldName != null;
String photoSuffix=photoOldName.substring(photoOldName.lastIndexOf("."));
//拼接新文件名
String newPhotoName= UUID.randomUUID().toString()+photoSuffix;
//保存文件
photo.transferTo(new File("D:\\images" +File.separator + newPhotoName));
}
}
return "main";
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
文件上传相关的配置类:
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
文件大小相关配置项:
server:
port: 8888
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
2
3
4
5
6
7
# 2.原理
文件的自动配置类MultipartAutoConfiguration配置了文件上传解析器StandardServletMultipartResolver
public class MultipartAutoConfiguration {
private final MultipartProperties multipartProperties;
public MultipartAutoConfiguration(MultipartProperties multipartProperties) {
this.multipartProperties = multipartProperties;
}
@Bean
@ConditionalOnMissingBean({MultipartConfigElement.class, CommonsMultipartResolver.class})
public MultipartConfigElement multipartConfigElement() {
return this.multipartProperties.createMultipartConfig();
}
@Bean(
name = {"multipartResolver"}
)
@ConditionalOnMissingBean({MultipartResolver.class})
public StandardServletMultipartResolver multipartResolver() {
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
return multipartResolver;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class StandardServletMultipartResolver implements MultipartResolver {
private boolean resolveLazily = false;
private boolean strictServletCompliance = false;
public StandardServletMultipartResolver() {
}
public void setResolveLazily(boolean resolveLazily) {
this.resolveLazily = resolveLazily;
}
public void setStrictServletCompliance(boolean strictServletCompliance) {
this.strictServletCompliance = strictServletCompliance;
}
public boolean isMultipart(HttpServletRequest request) {
return StringUtils.startsWithIgnoreCase(request.getContentType(), this.strictServletCompliance ? "multipart/form-data" : "multipart/");
}
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
public void cleanupMultipart(MultipartHttpServletRequest request) {
if (!(request instanceof AbstractMultipartHttpServletRequest) || ((AbstractMultipartHttpServletRequest)request).isResolved()) {
try {
Iterator var5 = request.getParts().iterator();
while(var5.hasNext()) {
Part part = (Part)var5.next();
if (request.getFile(part.getName()) != null) {
part.delete();
}
}
} catch (Throwable var4) {
Throwable ex = var4;
LogFactory.getLog(this.getClass()).warn("Failed to perform cleanup of multipart items", ex);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
流程分析
public class DispatcherServlet extends FrameworkServlet {
@Nullable
private MultipartResolver multipartResolver;
private void initMultipartResolver(ApplicationContext context) {
...
//这个就是配置类配置的StandardServletMultipartResolver文件上传解析器
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
...
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;//最后finally的回收flag
...
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//做预处理,如果有上传文件
//就将普通的 HttpServletRequest
// 包装为 MultipartHttpServletRequest(支持文件参数解析)
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
...
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
...
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
....
finally {
...
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
...
return this.multipartResolver.resolveMultipart(request);
...
}
}
protected void cleanupMultipart(HttpServletRequest request) {
if (this.multipartResolver != null) {
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
if (multipartRequest != null) {
this.multipartResolver.cleanupMultipart(multipartRequest);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());跳到以下的类
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
...
mav = invokeHandlerMethod(request, response, handlerMethod);
...
return mav;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {//关注点
//设置参数解析器
//用其中的RequestPartMethodArgumentResolver进行后续解析
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
...
invocableMethod.invokeAndHandle(webRequest, mavContainer);
...
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
...
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
...
}
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
...
return doInvoke(args);//反射调用
}
@Nullable
protected Object doInvoke(Object... args) throws Exception {
Method method = getBridgedMethod();
ReflectionUtils.makeAccessible(method);
return method.invoke(getBean(), args);
...
}
//处理得出multipart参数,准备稍后的反射调用(@PostMapping标记的上传方法)
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
...
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;
}
//关注点1
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//关注点2
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
...
}
}
return args;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class RequestPartMethodArgumentResolver extends AbstractMessageConverterMethodArgumentResolver {
//对应上面代码关注点1
@Override
public boolean supportsParameter(MethodParameter parameter) {
//标注@RequestPart的参数
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return true;
}
else {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
return false;
}
return MultipartResolutionDelegate.isMultipartArgument(parameter.nestedIfOptional());
}
}
//对应上面代码关注点2
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest request, @Nullable WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
RequestPart requestPart = parameter.getParameterAnnotation(RequestPart.class);
boolean isRequired = ((requestPart == null || requestPart.required()) && !parameter.isOptional());
String name = getPartName(parameter, requestPart);
parameter = parameter.nestedIfOptional();
Object arg = null;
//封装成MultipartFile类型的对象作参数
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
arg = mpArg;
}
...
return adaptArgumentIfNecessary(arg, parameter);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public final class MultipartResolutionDelegate {
...
@Nullable
public static Object resolveMultipartArgument(String name, MethodParameter parameter, HttpServletRequest request)
throws Exception {
MultipartHttpServletRequest multipartRequest =
WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class);
boolean isMultipart = (multipartRequest != null || isMultipartContent(request));
if (MultipartFile.class == parameter.getNestedParameterType()) {
if (!isMultipart) {
return null;
}
if (multipartRequest == null) {
multipartRequest = new StandardMultipartHttpServletRequest(request);
}
return multipartRequest.getFile(name);
}
else if (isMultipartFileCollection(parameter)) {
if (!isMultipart) {
return null;
}
if (multipartRequest == null) {
multipartRequest = new StandardMultipartHttpServletRequest(request);
}
List<MultipartFile> files = multipartRequest.getFiles(name);
return (!files.isEmpty() ? files : null);
}
else if (isMultipartFileArray(parameter)) {
if (!isMultipart) {
return null;
}
if (multipartRequest == null) {
multipartRequest = new StandardMultipartHttpServletRequest(request);
}
List<MultipartFile> files = multipartRequest.getFiles(name);
return (!files.isEmpty() ? files.toArray(new MultipartFile[0]) : null);
}
else if (Part.class == parameter.getNestedParameterType()) {
if (!isMultipart) {
return null;
}
return request.getPart(name);
}
else if (isPartCollection(parameter)) {
if (!isMultipart) {
return null;
}
List<Part> parts = resolvePartList(request, name);
return (!parts.isEmpty() ? parts : null);
}
else if (isPartArray(parameter)) {
if (!isMultipart) {
return null;
}
List<Part> parts = resolvePartList(request, name);
return (!parts.isEmpty() ? parts.toArray(new Part[0]) : null);
}
else {
return UNRESOLVABLE;
}
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 3.错误处理
# 1.默认错误处理机制
默认情况下,Spring Boot提供
/error处理所有错误的映射机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
{
"timestamp": "2020-11-22T05:53:28.416+00:00",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/asadada"
}
2
3
4
5
6
7
要对其进行自定义,添加
View解析为error要完全替换默认行为,可以实现
ErrorController并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。/templates/error/下的4xx,5xx页面会被自动解析
# 2.各种组件的功能
ErrorMVCAutoConfiguration自动配置异常处理规则容器中的组件:
DefaultErrorAttributes->id:errorAttributespublic class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered其中定义了一些错误页面包含的东西(具体错误细节,状态码,错误路径等)
容器中的组件:
BasicErrorController->id:basicErrorController(json+白页 适配响应)处理默认/error的请求 ,server.error.path不配置默认为空(
@RequestMapping({"${server.error.path:${error.path:/error}}"})页面响应:(
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response))
![]()
- 上图响应默认的视图名称为error,正好容器中自动配置了组件View->id是error(响应默认错误页也就是白页),容器中又存放了组件
BeanNameViewResolver(视图解析器);可按照返回的视图名作为id查找view
@Configuration( proxyBeanMethods = false ) @ConditionalOnProperty( prefix = "server.error.whitelabel", name = {"enabled"}, matchIfMissing = true ) @Conditional({ErrorTemplateMissingCondition.class}) protected static class WhitelabelErrorViewConfiguration { private final StaticView defaultErrorView = new StaticView(); protected WhitelabelErrorViewConfiguration() { } @Bean( name = {"error"} ) @ConditionalOnMissingBean( name = {"error"} ) public View defaultErrorView() { return this.defaultErrorView; } @Bean @ConditionalOnMissingBean public BeanNameViewResolver beanNameViewResolver() { BeanNameViewResolver resolver = new BeanNameViewResolver(); resolver.setOrder(2147483637); return resolver; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33容器中的组件:DefaultErrorViewResolver->id:conventionErrorViewResolver
- 如果发送错误,会以HTTP的状态码作为视图页地址(viewName),找到真正的页面比如(error/404,5xx.html)
# 3.异常处理步骤
1.执行目标方法,目标方法期间有任何异常都会被catch,而且标志当前请求结束,并且用dispatchException包装起来
2.进入视图解析流程(页面渲染)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//对方法进行处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
Exception ex = var20;
//包装异常
dispatchException = ex;
} catch (Throwable var21) {
Throwable err = var21;
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//对应2.视图解析流程
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
Exception ex = var22;
this.triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
} catch (Throwable var23) {
Throwable err = var23;
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
3.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);方法内处理发送的异常,处理完返回ModelAndView
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
//处理异常
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
下面是处理异常的具体流程分析
1.遍历所有的
handlerExceptionResolvers,看谁能处理当前异常@Nullable protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception { request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); ModelAndView exMv = null; if (this.handlerExceptionResolvers != null) { Iterator var6 = this.handlerExceptionResolvers.iterator(); while(var6.hasNext()) { HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next(); exMv = resolver.resolveException(request, response, handler, ex); if (exMv != null) { break; } } } if (exMv != null) { if (exMv.isEmpty()) { //将异常信息存入作用域 request.setAttribute(EXCEPTION_ATTRIBUTE, ex); return null; } else { if (!exMv.hasView()) { String defaultViewName = this.getDefaultViewName(request); if (defaultViewName != null) { exMv.setViewName(defaultViewName); } } if (this.logger.isTraceEnabled()) { this.logger.trace("Using resolved error view: " + exMv, ex); } else if (this.logger.isDebugEnabled()) { this.logger.debug("Using resolved error view: " + exMv); } WebUtils.exposeErrorRequestAttributes(request, ex, this.getServletName()); return exMv; } } else { //抛出 throw ex; } } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
442.系统默认的异常解析器
![]()
1.
DefaultErrorAttributes先来处理异常。把异常信息保存到request域,并且返回null;public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered { ... public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { this.storeErrorAttributes(request, ex); return null; } private void storeErrorAttributes(HttpServletRequest request, Exception ex) { request.setAttribute(ERROR_ATTRIBUTE, ex);//把异常信息保存到request域 } ... }1
2
3
4
5
6
7
8
9
10
11
12
132.默认没有人能处理异常,所以异常会被抛出
1.底层最终发送/error请求,被底层的
BasicErrorController处理@Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController { @RequestMapping(produces = MediaType.TEXT_HTML_VALUE) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); Map<String, Object> model = Collections .unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); //如果/template/error内没有4**.html或5**.html, //modelAndView为空,最终还是返回viewName为error的modelAndView return (modelAndView != null) ? modelAndView : new ModelAndView("error", model); } ... }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
182.解析错误视图;遍历所有的
ErrorViewResolver(在上述代码的resolveErrorView方法中),看谁能解析3.默认的
DefaultErrorViewResolver,作用是把响应状态码作为错误页的地址,error/5xx.html![]()
4.响应error/5xx.html
# 4.异常处理原理
自定义错误页
- error/404.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页
@ControllerAdvice+@ExceptionHandler处理全局异常;底层是ExceptionHandlerExceptionResolver支持的
//处理所有controller发生的异常
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
//异常处理器和处理的异常类型
public String handleException(Exception e){
log.error("异常是:{}",e.getMessage());
return "login";
}
}
2
3
4
5
6
7
8
9
10
11
Dispatcher类中由于之前没有配置异常处理解析器,所以上面提到了默认没有执行所以exMv一直为空,现在由自己配置的异常处理解析器处理执行,返回页面
(后续自己配置的其他异常处理解析器也在这执行)
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) throws Exception {
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
Iterator var6 = this.handlerExceptionResolvers.iterator();
while(var6.hasNext()) {
HandlerExceptionResolver resolver = (HandlerExceptionResolver)var6.next();
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@ResponseStatus+自定义异常;底层是ResponseStatusExceptionResolver,把responsestatus注解的信息拿过来底层调用response.sendError(statusCode, resolvedReason);告诉tomcat发送/error,后续是/error请求的处理逻辑
实现
@ResponseStatus(value= HttpStatus.FORBIDDEN,reason = "用户数量太多")
public class UserTooManyException extends RuntimeException {
public UserTooManyException(){
}
public UserTooManyException(String message){
super(message);
}
}
2
3
4
5
6
7
8
9
10
@Controller
public class TableController {
@GetMapping("/dynamic_table")
public String dynamic_table(@RequestParam(value="pn",defaultValue = "1") Integer pn,Model model){
//表格内容的遍历
List<User> users = Arrays.asList(new User("zhangsan", "123456"),
new User("lisi", "123444"),
new User("haha", "aaaaa"),
new User("hehe ", "aaddd"));
model.addAttribute("users",users);
if(users.size()>3){
throw new UserTooManyException();//抛出自定义异常
}
return "table/dynamic_table";
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Spring自家异常如
org.springframework.web.bind.MissingServletRequestParameterException,DefaultHandlerExceptionResolver处理Spring自家异常。- 也会执行
response.sendError(400, ex.getMessage());结束当前请求,tomcat发送/error请求
- 也会执行
自定义实现
HandlerExceptionResolver处理异常;可以作为默认的全局异常处理规则
实现
@Order(value = Ordered.HIGHEST_PRECEDENCE) //优先级,数字越小优先级越高
@Component
public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
response.sendError(511,"我喜欢的错误");
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
ErrorViewResolver实现自定义处理异常(大多数情况都不会重写,因为未处理过的底层都会被它处理)response.sendError(),error请求就会转给controller。你的异常没有任何人能处理,tomcat底层调用
response.sendError(),error请求就会转给controller。basicErrorController要去的页面地址是ErrorViewResolver解析的 。
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
...
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status,
Map<String, Object> model) {
//这里用到ErrorViewResolver接口
for (ErrorViewResolver resolver : this.errorViewResolvers) {
ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
if (modelAndView != null) {
return modelAndView;
}
}
return null;
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 5.原生组件注入
# 1.原生注解与Servlet注入
- 原生注解方式注入
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hello,myServlet");
}
}
2
3
4
5
6
7
@Slf4j
@WebFilter(urlPatterns={"/css/*","/images/*"})
public class MyWebFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("MyWebFilter初始化完成");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("MyWebFilter开始执行");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
log.info("MyWebFilter销毁");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Slf4j
@WebListener
public class MyWebListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
log.info("MyWebListener监听到项目初始化完成");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
log.info("MyWebListener监听到项目销毁");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
加上servlet扫描包注解
@ServletComponentScan("com.demo.boot.servlet")
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.demo.boot") //三个注解相当于上面的SpringBootApplication
public class WebAdminApplication {
public static void main(String[] args) {
//返回IOC容器
ConfigurableApplicationContext run = SpringApplication.run(WebAdminApplication.class, args);
}
}
2
3
4
5
6
7
8
9
10
11
# 2.Spring方式注入
ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean
@Configuration
public class MyRegister {
@Bean
public ServletRegistrationBean myServlet(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my");
}
@Bean
public FilterRegistrationBean myFilter(){
//return new FilterRegistrationBean(myFilter,myServlet());
MyWebFilter myWebFilter = new MyWebFilter();
FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>(myWebFilter);
filterFilterRegistrationBean.setUrlPatterns(Arrays.asList("/css/*","/images/*"));
return filterFilterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
MyWebListener myWebListener = new MyWebListener();
return new ServletListenerRegistrationBean(myWebListener);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 3.DispatcherServlet注入原理
DispatcherServletAutoConfiguration配置类
@AutoConfigureOrder(Integer.MIN_VALUE)
@AutoConfiguration(
after = {ServletWebServerFactoryAutoConfiguration.class}
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({DispatcherServlet.class})
public class DispatcherServletAutoConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
public DispatcherServletAutoConfiguration() {
}
@Configuration(
proxyBeanMethods = false
)
@Conditional({DispatcherServletRegistrationCondition.class})
@ConditionalOnClass({ServletRegistration.class})
@EnableConfigurationProperties({WebMvcProperties.class})
@Import({DispatcherServletConfiguration.class})
protected static class DispatcherServletRegistrationConfiguration {
protected DispatcherServletRegistrationConfiguration() {
}
@Bean(
name = {"dispatcherServletRegistration"}
)
@ConditionalOnBean(
value = {DispatcherServlet.class},
name = {"dispatcherServlet"}
)
//注册DispatcherServlet的类
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
registration.setName("dispatcherServlet");
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
@Configuration(
proxyBeanMethods = false
)
@Conditional({DefaultDispatcherServletCondition.class})
@ConditionalOnClass({ServletRegistration.class})
@EnableConfigurationProperties({WebMvcProperties.class})
protected static class DispatcherServletConfiguration {
protected DispatcherServletConfiguration() {
}
@Bean(
name = {"dispatcherServlet"}
)
//创建DispatcherServlet的类
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 = {"multipartResolver"}
)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
return resolver;
}
...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
所以当没有注册MyServlet,自动注册的DispatcherServlet会拦截/my请求,当注册MyServlet时,根据精准匹配原则由我们自己注册的Servlet会拦截处理请求。
注:当使用注解注册时,必须要加Servlet包扫描,否则不会扫描到注解,也就不会注册
# 6.嵌入式Servlet容器
# 1.原理
默认支持的WebServer
Tomcat,Jetty, orUndertow。ServletWebServerApplicationContext容器启动寻找ServletWebServerFactory并引导创建服务器。
原理:
SpringBoot应用启动发现当前是Web应用,web场景包-导入tomcat。
web应用会创建一个web版的IOC容器
ServletWebServerApplicationContext。ServletWebServerApplicationContext启动的时候寻找ServletWebServerFactorySpringBoot底层默认有很多的WebServer工厂(
ServletWebServerFactoryAutoConfiguration``ServletWebServerFactoryConfiguration内创建的Bean),如:TomcatServletWebServerFactory
JettyServletWebServerFactory
UndertowServletWebServerFactory
底层会有一个自动配置类
ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryConfigurationServletWebServerFactoryConfiguration根据动态判断系统中到底导入了那个Web服务器的包。(默认是web-starter导入tomcat包),容器中就有TomcatServletWebServerFactoryTomcatServletWebServerFactory创建出Tomcat服务器并启动;TomcatWebServer的构造器拥有初始化方法initialize——this.tomcat.start();内嵌服务器,与以前手动把启动服务器相比,改成现在使用代码启动(tomcat核心jar包存在)。
Spring Boot默认使用Tomcat服务器,若需更改其他服务器,则修改工程pom.xml:
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
总结:SpringBoot 启动 Web 应用时,会先识别到 Web 场景依赖并导入 Tomcat 相关包,随后创建 Web 专属的 IOC 容器 ServletWebServerApplicationContext;该容器启动时会寻找 ServletWebServerFactory 类型的 Bean,而底层的自动配置类 ServletWebServerFactoryAutoConfiguration 会导入 ServletWebServerFactoryConfiguration,后者会根据项目中实际引入的 Web 服务器依赖(默认是 Tomcat),动态创建对应的服务器工厂 Bean(如 TomcatServletWebServerFactory);最终由 TomcatServletWebServerFactory 创建并启动 Tomcat 服务器(核心是通过 TomcatWebServer 的初始化方法调用 tomcat.start()),实现了将 Web 服务器内嵌化,通过代码而非手动方式启动服务器的核心逻辑。

# 2.定制化Servlet容器
实现
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>把配置文件的值和
ServletWebServerFactory进行绑定@Component public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> { @Override public void customize(ConfigurableWebServerFactory factory) { factory.setPort(8081); } }1
2
3
4
5
6
7
修改配置文件 server.xxx
直接自定义
ConfigurableServletWebServerFactory/** * 直接自定义ConfigurableServletWebServerFactory Bean * 优先级:自定义Bean > WebServerFactoryCustomizer > 配置文件(server.xxx) */ @Configuration public class WebServerConfig { @Bean public ConfigurableServletWebServerFactory webServerFactory() { // 1. 创建Tomcat服务器工厂(也可替换为Jetty/Undertow) TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); // 2. 基础配置(和定制器模式一致) factory.setPort(9999); // 端口 factory.setContextPath("/app"); // 上下文路径 factory.setSessionTimeout(60 * 60); // 会话超时1小时 factory.setUriEncoding(java.nio.charset.StandardCharsets.UTF_8); // 编码 // 3. Tomcat专属配置(连接器属性) factory.addConnectorCustomizers(connector -> { // 设置Tomcat连接器的URI编码 connector.setURIEncoding("UTF-8"); // 设置连接超时时间(毫秒) connector.setConnectionTimeout(30000); // 设置是否开启压缩 connector.setCompression("on"); // 设置压缩的最小响应大小(字节) connector.setCompressionMinSize(2048); }); // 4. 错误页面配置(示例) factory.addErrorPages(new org.springframework.boot.web.server.ErrorPage( org.springframework.http.HttpStatus.NOT_FOUND, "/404.html") ); return factory; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38ConfigurableServletWebServerFactory的实现类TomcatServletWebServerFactory
JettyServletWebServerFactory
UndertowServletWebServerFactory
# 7.定制化原理-SpringBoot定制化组件的几种方式
# 1.修改配置文件
# 2.xxxxxCustomizer
比如上面的容器定制化案例
# 3.1xxxConfiguration+@Bean
编写自定义的配置类 xxxConfiguration + @Bean替换/增加容器中默认组件
@Configuration
public class MyConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("_m");
return hiddenHttpMethodFilter;
}
}
2
3
4
5
6
7
8
9
# 4.WebMvcConfigurer+@Bean
Web应用 编写一个配置类实现 WebMvcConfigurer 即可定制化web功能 + @Bean给容器中再扩展一些组件
@Configuration
public class registerInterceptor implements WebMvcConfigurer {
}
2
3
# 5.@EnableWebMvc + WebMvcConfigurer — @Bean
上面这三个可以全面接管SpringMVC,所有规则全部自己重新配置; 实现定制和扩展功能
/**
* 全面接管Spring MVC配置
* @EnableWebMvc:关闭Spring Boot自动配置,完全自定义
* WebMvcConfigurer:定制MVC核心规则
*/
@Configuration
@EnableWebMvc // 关键:开启全面接管,关闭自动配置
public class CustomWebMvcConfig implements WebMvcConfigurer {
// ======================== 1. 自定义拦截器(示例:登录校验拦截器) ========================
// 步骤1:定义拦截器
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 模拟登录校验逻辑:检查请求头中的token
String token = request.getHeader("token");
if (token == null || !"admin123".equals(token)) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"msg\":\"未登录,请先登录\"}");
return false;
}
return true;
}
}
// 步骤2:注册拦截器(通过WebMvcConfigurer重写方法)
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册登录拦截器,指定拦截路径和排除路径
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/api/**") // 拦截所有/api开头的请求
.excludePathPatterns("/api/login") // 排除登录接口
.excludePathPatterns("/api/register"); // 排除注册接口
}
// ======================== 2. 静态资源配置(替代默认的classpath:/static/) ========================
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 自定义静态资源映射:访问/static/** 映射到classpath:/my-static/
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/my-static/")
.setCachePeriod(3600); // 静态资源缓存时间(秒)
// 映射本地磁盘静态资源:访问/images/** 映射到D:/images/
registry.addResourceHandler("/images/**")
.addResourceLocations("file:D:/images/");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
原理:
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 没有生效。
原理分析套路
场景starter - xxxxAutoConfiguration - 导入xxx组件 - 绑定xxxProperties - 绑定配置文件项。


