ServletContainerInitializer接口
在web容器启动时提供给第三方做一些初始化的工作的接口,例如servlet或者filtes等,每个框架要使用ServletContainerInitializer就必须在对应的jar包创建META-INF/service目录,并且建javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类。
@HandlesTypes(value = MyHandlesType.class)注解
在ServletContainerInitializer实现类中加上该注解,就能够拿到对应的类的所有子类的class文件信息
SpringMVC 没有web.xml方式基于注解方式实现启动
package com.mayikt.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* @Description:
* @Author: ChenYi
* @Date: 2020/07/09 23:24
**/
@Configuration
@ComponentScan("com.mayikt.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
package com.mayikt.config;
import org.springframework.web.SpringServletContainerInitializer;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
/**
* @Description:
* @Author: ChenYi
* @Date: 2020/07/09 23:35
**/
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
webApplicationContext.register(SpringMvcConfig.class);
ServletRegistration.Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(webApplicationContext));
dynamic.addMapping("/");
dynamic.setLoadOnStartup(1);
}
}
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
- Spring-Web依赖Jar包,SpringServletContainerInitializer实现初始化.
- @HandlesTypes(WebApplicationInitializer.class)
- MyWebApplicationInitializer继承WebApplicationInitializer类
- Serlvet容器在初始化的时候会加载到这个SpringServletContainerInitializer,然后会找WebApplicationInitializer子类,然后回调onStartup方法进行初始化
拦截器和过滤器的区别
相同点:
- 拦截器和过滤器都是基于aop技术,对方法实现增强,都可以拦截请求方
法。
不同点:
- 过滤器属于Servlet研发的,而拦截器属于SpringMVC研发的
- 过滤器属于拦截Web请求,而拦截器不仅可以拦截请求,还可以拦截普通方法
- 过滤器会比拦截器先执行,拦截器封装的方法比过滤器拦截使用起来更加简单
自定义拦截器
实现HandlerInterceptor接口
package com.mayikt.interceptor;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Description:
* @Author: ChenYi
* @Date: 2020/07/13 08:17
**/
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println(">>>>>preHandle<<<<");
String token = request.getParameter("token");
if (StringUtils.isEmpty(token)) {
response.setStatus(500);
response.getWriter().println("token 无效");
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println(">>>>>postHandle<<<<");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println(">>>>>afterCompletion<<<<");
}
}
- preHandle在业务处理器处理请求之前被调用;
- postHandle在业务处理器处理请求执行完成后,生成视图之前执行;
- afterCompletion在DispatcherServlet完全处理完请求后被调用,可用于清理资源等 。
DispatchServlet
DispatchServlet与Servlet的关系
类图
DispatcherServlet继承FrameworkServlet继承HttpServlet
流程:
- 先走HttpServlet的service方法,判断是get请求还是post请求,然后执行doGet和doPost方法
- 执行FrameworkServlet中的doGet或者doPost方法,执行processRequest方法
- 执行DispatcherServlet中的doService方法
- 执行DispatcherServlet中的doDispatch方法
- 先检查是否是文件上传的格式
- 执行getHandler获取HandlerExecutionChain对象,里面有handler对象和拦截器Interceptors,handler里面存的有controller的类和请求目标的方法 ,也就是 请求url映射路径对应的控制层具体的方法
- getHandlerAdapter获取处理适配器 RequestMappingHandlerAdapter
10.执行applyPreHandle →preHandle() ,执行拦截器的前置方法如果返回为true的话。
handle()执行实际的目标方法, 返回modeAndView对象
执行applyPostHandle→postHandle() 方法,执行拦截器的后置方法
执行processDispatchResult中的render()方法渲染视图层内容
执行triggerAfterCompletion→执行拦截器中的afterCompletion方法,释放资源
控制层容器初始化
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); //初始化上传文件解析器(或者是多部分请求解析器)
initLocaleResolver(context);//初始化本地化解析器
initThemeResolver(context);//初始化主题解析器
initHandlerMappings(context);//初始化处理器映射器
initHandlerAdapters(context);//初始化处理器适配器
initHandlerExceptionResolvers(context);//初始化处理器异常解析器
initRequestToViewNameTranslator(context);//初始化请求到视图名翻译器
initViewResolvers(context);//初始化视图解析器
initFlashMapManager(context);//初始化重定向数据管理器
}
SpringMVC的适配器
- 继承Controller方式所使用的适配器:SimpleControllerHandlerAdapter
- HTTP请求处理器适配器:HttpRequestHandlerAdapter
- 注解方式(@Controller)的处理器适配器:RequestMappingHandlerAdapter
参考:蚂蚁课堂