1 拦截器
1.1 HandlerInterceptor
public interface HandlerInterceptor {
//页面处理之前
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
//页面处理以后
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
//post完成以后
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
1.2 实现登陆拦截和静态资源放行
1. 编写一个拦截器实现HandlerInterceptor
2. 拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
3. 指定拦截规则
- 编写拦截器
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 {
//登陆检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if (loginUser!=null){
//放行
return true;
}
//拦截
response.sendRedirect("/");
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 {
}
/**
* 页面渲染以后
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
- 拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
//注册到容器中
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
//指定拦截规则
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/","/login","/css/**","/js/**","/fonts/**");
}
}
1.3 拦截器原理
- 根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有拦截器】
- 先来按照顺序执行所有拦截器的preHandle方法
- 如果当前拦截器prehandler返回为true,则执行下一个拦截器的prehandle
- 如果当前拦截器返回false,直接倒序执行所有执行的所有拦截器的afterComletion
- 如何任何一个拦截器返回false。直接跳出不执行目标方法
- 所有拦截器都返回True,才执行完以后。
- 倒序执行所有拦截器的postHandle方法
- 前面的步骤有任何异常都会直接倒叙触发afterComletion
- 页面成功渲染完成以后,也会倒叙触发afterComletion
触发的也只是,已经执行过preHandle方法的拦截器的afterComletion
2 文件上传
2.1 后端测试代码
public class FormTestController {
@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
@RequestParam("username") String username,
//单文件的上传
@RequestPart("headerImg") MultipartFile headerImg,
//多文件的上传
@RequestPart("photos") MultipartFile[] photos) throws IOException {
if (!headerImg.isEmpty()){
//文件存储到本地固定目录
headerImg.transferTo(new File("D:\\WdGame\\"+headerImg.getOriginalFilename()));
}
if (photos.length>0){
for(MultipartFile photo : photos){
if (!photo.isEmpty()){
photo.transferTo(new File("D:\\WdGame\\"+photo.getOriginalFilename()));
}
}
}
return "index";
}
}
2.2 配置信息
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
2.3 自动配置原理
文件上传自动配置类 MultipartAutoConfiguration—>MultipartProperties
-
自动配置好了 StandardServletMultipartResolver 【文件上传解析器】
-
原理步骤
- 请求今来使用文件上传解析器判断(isMultipart)并且封装(resolveMulipart,返回MultipartHttpServletRequest)文件上传请求
- 参数解析器来解析请求中的文件内容封装成MultipartFile
- 将request中的文件信息封装成一个map (接受参数,List)
FileCopyUtils:实现文件流的拷贝
3 异常处理
3.1 错误处理
- 默认情况下,SrpingBoot提供了/error处理所有得错误得映射
- 对于己写客户端,他将胜场JSON相应,其中包含错误,HTTP状态和一场消息得详细信息,对于浏览客户端,响应一个"whitelabel"错误视图,以HTML格式呈现相同的数据
- 要对其进行自定义,添加View解析为error
- 要完全替换默认行为,可以实现ErrorController并注册该类型的Bean定义,或添加ErrorAttributes类型的组件已使用现有的机制代替其内容
- error/下的4xx,5xx页面会被自动解析
3.2定制错误处理逻辑
-
自定义错误页
- error/404.html,error/5xx.html
-
@ControllerAdvice+@ExceptionHandler处理异常(推荐):返回值是String 也是ModelView,底层是ExceptionHandlerExceptionResolver
-
@ResponseStatus+自定义异常;底层是ResponseStatusExceptionResolver ,把responsertatus注解的信息封装成ModelAndView返回,底层用response.sendError(StatusCode,resolveReason);也就是tomcat返回的/error
-
Spring底层的异常,如参数类型转换异常;DefaultHandlerExceptionResolver处理框架底层的异常,最后也会落脚到response.sendError
-
ErrorViewResolver实现自定义处理异常
- response.sendError。error请求就会转给controller
- 你的异常没有任何人能处理,tomcat底层response.sendError。error请求就会转给controller
- basicErrorController要去的页面地址是ErrorViewResolver
-
实现HandlerExceptionResolver处理异常
3.3 异常处理自动配置原理
-
ErrorMvcAutoConfiguration 自动配置异常处理规则
-
容器中的组件:类型:DefaultErrorAttributes->id:errorAttributes
-
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered
-
DefaultErrorAttributes:定义错误页面中应该包含哪些数据
-
-
容器中的组件: 类型:BasicErrorController->id:basicErrorController (json+白夜 适配响应)
- 处理默认/error路径的请求;页面响应: new ModelView(“error”,model)
- 容器中有组件View->id是error(响应默认错误页)
- 容器中放组件BeanNameResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象。
-
容器中的组件:DefaultErrorViewResolver->id:conventionErrorViewResolver
- 如果发生错误,会以HTTP的状态码作为视图页地址(ViewName),找到真正的页面
- error/404,5xx.html页面
-
如果想要返回页面;就会找到error视图【staticView】(默认是一个白页)
3.4 异常处理流程
-
执行目标方法,目标方法运行期间又任何异常都会被catch,而且标志当前请求结束;并且用dispatchException
-
进行视图解析流程(页面渲染?)
processDispatchResult(processeRequest,response,mapperHandler,mv,dispatchException)
-
mv=precessHandlerException:处理handler发生的异常,处理完成返回ModelAndView
-
遍历所有的handlerExceptionResolvers 看谁能处理当前异常[HandlerExceptionResolver处理器异常解析器]
-
默认的异常解析器:DefaultErrorAttributes和HadnlerExceptionResolverCompsite
-
DefaultErrorAttributes先来处理异常。把异常信息保存到request域,并且返回null;
-
默认没有任何人能处理异常,所以异常会被抛出
-
如果没有任何人能处理,最终底层就会发送/error请求,会被底层的BasicErrorController处理
-
解析错误视图,遍历所有的ErrorViewResokver看谁能解析
-
默认的DefaultErrorViewResolver 作用是把响应状态码作为错误页的地址拼接成,error/500.html
-
模板引擎最终响应这个页面 error/500.html
-
-
-
4 Web原生组件注入(Servlet,Filter,Listener)
4.1 使用Servlet API
@WebServlet(urlPatterns = "/my")
public class Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
//主类申明Servlet需要扫描的Servlet包
@ServletComponentScan(basePackages = "com.atguigu.admin")
@SpringBootApplication
public class Boot05WebAdminApplication {
public static void main(String[] args) {
SpringApplication.run(Boot05WebAdminApplication.class, args);
}
}
@ServletComponentScan(basePackages = “com.atguigu.admin”):指定原生Servlet组件都放在哪里
@WebServlet(urlPatterns = “/my”):效果:直接影响,没有Spring的拦截器?
此时系统中存在两个Servlet
1.MyServlet—>/my Tomcat处理
2.DispatcherServlet—>SpringMVC所带有的 Spring流程
扩展:DispatchServlet是如果注册:
-
容器中自动配置了DispatcherServlet属性绑定到WebMvcProperties;对应的配置文件的配置项是spring.mvc
-
通过ServletRegistrationBean把DispatcherServlet配置进来
-
默认映射的是/路径
-
同样遵循精确有限原则
/my请求提交锅来,由Tomcat处理
Tomcat-Servlet
多个Servlet都能处理到同一层路径,精确优先原则
4.2 使用Filter API
@WebFilter(urlPatterns = {"/css/*","/images/*"})
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
}
@Override
public void destroy() {
}
}
4.3 Listener
//注解
@WebListener
public class MyServletContextListenter implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
4.4 RegistrationBean
@Configuration
public class MyRegisticConfig {
@Bean
public ServletRegistrationBean myServlet(){
Servlet myServlet = new Servlet();
return new ServletRegistrationBean(myServlet,"/my","/my02");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
return filterRegistrationBean;
}
public ServletListenerRegistrationBean myListenter(){
MyServletContextListenter myServletContextListenter = new MyServletContextListenter();
return new ServletListenerRegistrationBean(myServletContextListenter);
}
}
5. 嵌入式Servlet容器
5.1 切换嵌入式Servlet容器
-
默认支持的WebServer
- Tomcat,Jetty,or Undertow
- ServletWebServerApplicationContext 容器启动寻找ServletWebServerFactory 并且创建服务器
-
切换服务器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
-
原理
-
SpriingBoot应用启动发现当前是Web应用。Web场景包-导入Tomcat
-
web应用会创建一个Web的IOC容器ServletWebServerApplicationContext
-
ServletWebServerApplicationContext 启动的时候 启动ServletWebServerFactory工厂
-
SpringBoot底层默认支持的很多的WebServer工厂:
TomcatServletWebServerFactory,JettyServletWebServerFactory,or underServletWebServerFactory
-
底层直接会有一个自动配置类。ServletWebServerFactoryAutoConfiguration
-
ServletWebServerFactoryAutoConfiguration导入了ServletWebServerFactoryAutoConfiguration(配置类)
-
ServletWebServerFactoryAutoConfiguration 配置类 根据动态判断系统中到底导入了那个Web服务器的包。(默认是导入web-starter,因此导入了tomcat包),容器中就有TomcatServletWebServerFactory。
-
TomcatServletWebServerFactory创建出tomcat服务器并且启动;TomcatWebServer的构造器拥有初始化方法initialize
-
只能由一个服务器,否则就会抛出异常
-
内嵌服务器,就是手动把启动服务器的代码调用(tomcat核心jar包存在)
-
5.2 定制Servlet容器
- 实现WebServerFactoryCustomizer
- 修改配置文件server.xxx
- 直接自定义configuServletWebServerFactory
xxxxCustomizer:定制化器,可以改变xxxx的默认规则
5.3 定制化的常见方式
-
修改配置文件
-
xxxxxCustomizer
-
编写自定义的配置类 xxxConfiguration + @Bean替换,增加容器中默认组件;视图解析器
-
web应用实现WebMvcConfigurer即可定制化web功能(推荐)
-
@EnableWebMvc+WebMvcConfigurer-@Bean 可以全面接管SpringMVC,所有规则全部自己重新部署慎重使用;实现定制和扩展功能
-
原理
-
-
WebMVCAutoConfiguration的默认的SpringMVC的自动配置类,静态资源,欢迎页
-
一旦使用@EnableWebMvc,导入DelegatingWebMvcConfiguration.class
-
DelegatingWebMvcConfiguration的作用
- 把所有系统中的WebMvcConfiguration。所有功能的定制都是这些WebMvcConfiguration所有都生效
- 自动配置了一些非常底层的组件。RequestMapping,HandlerMappinng 这些组件依赖的组件都是从容器中获取
-
WebMvcAutoConfiguration里面的配置要能生效 必须
@ConditionalOnMissingBean(WebMvcConfigutationSupport.class)
DelegatingWebMvcConfiguration.class 继承 WebMvcConfigutationSupport.class
-
EnableWebMvc导致了WebMvcAutoConfiguration
-
-
5.4 原理分析套路
场景starter-xxxxAutoConfiguration-导入xxx组件—绑定xxxProperties—绑定配置文件项