首页 > 编程语言 > 详细

SpringBoot-11-扩展MVC

时间:2020-09-19 13:20:36      阅读:52      评论:0      收藏:0      [点我收藏+]

1、SpringMVC自动配置

  • SpringBoot为整合SpringMVC框架实现Web开发,主要提供了以下自动化配置的功能特性

    • 内置了两个视图解析器

      • ContentNegotiatingViewResolver

      • BeanNameViewResolver

      • 视图解析器自动配置原理:在ContentNegotiatingViewResolver.class中找到了resolveViewName方法,里面最主要的内容是

        • 获取候选的视图

        • 得到最好的视图

        @Nullable
        public View resolveViewName(String viewName, Locale locale) throws Exception {
           RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
           Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
           List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
           if (requestedMediaTypes != null) {
               //获取候选的视图
               List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
               //得到最好的视图
               View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
               if (bestView != null) {
                   return bestView;
              }
          }
        ?
           String mediaTypeInfo = this.logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : "";
           if (this.useNotAcceptableStatusCode) {
               if (this.logger.isDebugEnabled()) {
                   this.logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);
              }
        ?
               return NOT_ACCEPTABLE_VIEW;
          } else {
               this.logger.debug("View remains unresolved" + mediaTypeInfo);
               return null;
          }
        }
        • 获取候选的视图代码如下:

          private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {
             List<View> candidateViews = new ArrayList();
             if (this.viewResolvers != null) {
                 Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
                 Iterator var5 = this.viewResolvers.iterator();
          ?
                 //遍历所有的viewResolver
                 while(var5.hasNext()) {
                     ViewResolver viewResolver = (ViewResolver)var5.next();
                     View view = viewResolver.resolveViewName(viewName, locale);
                     if (view != null) {
                         //把遍历出来的视图解析器添加到候选视图解析器当中
                         candidateViews.add(view);
                    }
          ?
                     Iterator var8 = requestedMediaTypes.iterator();
          ?
                     while(var8.hasNext()) {
                         MediaType requestedMediaType = (MediaType)var8.next();
                         List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
                         Iterator var11 = extensions.iterator();
          ?
                         while(var11.hasNext()) {
                             String extension = (String)var11.next();
                             String viewNameWithExtension = viewName + ‘.‘ + extension;
                             view = viewResolver.resolveViewName(viewNameWithExtension, locale);
                             if (view != null) {
                                 //把遍历出来的视图解析器添加到候选视图解析器当中
                                 candidateViews.add(view);
                            }
                        }
                    }
                }
            }
             if (!CollectionUtils.isEmpty(this.defaultViews)) {
                 candidateViews.addAll(this.defaultViews);
            }
          ?
             //把视图返回
             return candidateViews;
          }
      • 总结:

        Spring Boot视图解析器自动配置原理为:实现了ViewResolver的类,都可以被看做视图解析器,SpringBoot会把用户配置的视图解析器和Spring Boot自己的组合起来,选择最好的那个使用。

     

    • 支持静态资源以及WebJars

    • 自动注册了类型转换器和格式化器

    • 自动注册了消息代码解析器

    • 支持静态项目首页index.html

    • 支持定制应用图标favicon.ico:新版本没有了这个功能

    • 自动初始化Web数据绑定器ConfigurableWebBindingInitializer

 

2、SpringMVC扩展

2.1、自定义格式

  • 在WebMvcAutoConfiguration.class中发现这一段代码:

    说明我们可以在全局配置文件中配置格式,默认格式为:yyyy-MM-dd HH:mm:ss

    @Bean
    public FormattingConversionService mvcConversionService() {
       
       //可以从配置文件properties中获取format
       Format format = this.mvcProperties.getFormat();
       WebConversionService conversionService = new WebConversionService((new DateTimeFormatters()).dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));
       this.addFormatters(conversionService);
       return conversionService;
    }
  • 在全局配置文件中配置格式

    spring:
    mvc:
      format:
        date-time: yyyy-MM-dd HH:mm:ss

     

2.2、注册视图管理器

@Configuration
public class MyMvcConfig implements WebMvcConfigurer{
?
   @Override
   public void addViewControllers(ViewControllerRegistry registry) {
       
       //请求toLogin映射路径会自动映射到test.html页面
       registry.addViewController("/toLogin").setViewName("test");
  }
}

 

3.3、注册自定义拦截器

  • 自定义拦截类

    @Component
    public class MyInterceptor implements HandlerInterceptor {
    ?
       @Override
       //用户请求/admin路径时,判断用户是否登录
       public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    ?
           //获取请求路径
           String requestURI = request.getRequestURI();
           //获取用户登录名
           Object loginUser = request.getSession().getAttribute("loginUser");
    ?
           //如果用户未登录,跳转到登录页面
           if (requestURI.startsWith("/admin") && null == loginUser){
               response.sendRedirect("toLogin");
               return false;
          }
           return true;
      }
    ?
       @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 {}
    }
  • 注册自定义的拦截器

    在自定义配置类MyMvcConfig中,重写addInterceptors方法

    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
    ?
       @Autowired
       private MyInterceptor myInterceptor;
       @Override
       public void addInterceptors(InterceptorRegistry registry) {
           //拦截除了login.thml之外的所有请求
           registry.addInterceptor(myInterceptor)
                  .addPathPatterns("/**")
                  .excludePathPatterns("/login.thml");
      }
    }

 

4.4、Servlet

  • 使用组件方式整合Servlet

    • 创建自定义Servlet类

      @Component
      public class MyServlet extends HttpServlet {
         @Override
         protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             this.doPost(req, resp);
        }
      ?
         @Override
         protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             resp.getWriter().write("hello,MyServlet!");
        }
      }
    • 创建Servlet组件配置类

      @Configuration
      public class ServletConfig {
         //注册Servlet组件
         @Bean
         public ServletRegistrationBean getServlet(MyServlet myServlet){
             ServletRegistrationBean registrationBean = new ServletRegistrationBean(myServlet, "/myServlet");
             return registrationBean;
        }
      }
  • 使用路径扫描方式整合Servlet

    • 创建自定义Servlet类

      @WebServlet("/myServlet")
      @Component
      public class MyServlet extends HttpServlet {
         @Override
         protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             this.doPost(req, resp);
        }
      ?
         @Override
         protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             resp.getWriter().write("hello,MyServlet!");
        }
      }
    • 在主启动程序类上添加 @ServletComponentScan 注解,开启注解方式的Servlet组件扫面支持

     

4.5、过滤器:Filter

  • 使用组件方式整合Filter

    • 创建自定义Filter

      @Component
      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 {
            System.out.println("Hello,MyFilter");
            filterChain.doFilter(servletRequest,servletResponse);
        }
      ?
        @Override
        public void destroy() { }
      }
    • 向Servlet组件配置类中注册自定义Filter

      @Configuration
      public class ServletConfig {
      ?
         @Bean
         public FilterRegistrationBean getFilter(MyFilter myFilter){
             FilterRegistrationBean registrationBean = new FilterRegistrationBean(myFilter);
             registrationBean.setUrlPatterns(Arrays.asList("/toLogin","/myFilter"));
             return registrationBean;
        }
      }
  • 使用路径扫描方式整合Filter

    • 创建自定义Filter

      @WebFilter(value = {"/toLogin","/myFilter"})
      @Component
      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 {
             System.out.println("Hello,MyFilter");
             filterChain.doFilter(servletRequest,servletResponse);
        }
      ?
         @Override
         public void destroy() { }
      }
    • 在主启动程序类上添加 @ServletComponentScan 注解,开启注解方式的Servlet组件扫面支持

 

4.6、监听器:Listener

  • 使用组件方式整合Listener

    • 创建自定义Listener类

      @Component
      public class MyListener implements ServletContextListener {
         @Override
         public void contextInitialized(ServletContextEvent sce) {
             System.out.println("Initialized...");
        }
      ?
         @Override
         public void contextDestroyed(ServletContextEvent sce) {
             System.out.println("Destroyed...");
        }
      }
    • 向Servlet组件配置类中注册自定义Listener

      @Configuration
      public class ServletConfig {
      ?
         @Bean
         public ServletListenerRegistrationBean getListener(MyListener myListener){
             ServletListenerRegistrationBean registrationBean = new ServletListenerRegistrationBean(myListener);
             return registrationBean;
        }
      }
  • 使用路径扫描方式整合Listener

    • 创建自定义Listener类

      @WebListener
      @Component
      public class MyListener implements ServletContextListener {
         @Override
         public void contextInitialized(ServletContextEvent sce) {
             System.out.println("Initialized...");
        }
      ?
         @Override
         public void contextDestroyed(ServletContextEvent sce) {
             System.out.println("Destroyed...");
        }
      }
    • 在主启动程序类上添加 @ServletComponentScan 注解,开启注解方式的Servlet组件扫面支持

 

4.7、文件上传

  • 导入JQuery依赖

  • 前端页面

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
       <meta charset="UTF-8">
       <meta http-equiv="Content-Type" content="text/html";charset="UTF-8">
       <title>动态添加文件上传列表</title>
       <script src="/webjars/jquery/3.4.1/jquery.js"></script>
    </head>
    <body>
    ?
       <div th:if="${uploadStatus}" style="color: lightcoral" th:text="${uploadStatus}">上传成功</div>
    ?
       <form th:action="@{/uploadFile}" method="post" enctype="multipart/form-data">
          上传文件:<input type="button" value="添加文件" onclick="add()">
    ?
           <div id="file" style="margin-top: 10px" th:value="文件上传区域"></div>
    ?
           <input id="submit" type="submit" value="上传" style="display: none;margin-top: 10px">
       </form>
    ?
       <script type="text/javascript">
           //动态添加上传按钮
           function add() {
               let innerDiv = "<div>";
               innerDiv += "<input type=‘file‘ name=‘fileUpload‘ required=‘required‘>" +
                       "<input type=‘button‘ value=‘删除‘ onclick=‘remove(this)‘>";
               innerDiv += "</div>";
    ?
               $("#file").append(innerDiv);
               $("#submit").css("display","block")
          }
    ?
           //删除当前行
           function remove(obj) {
               $(obj).parent().remove();
               if($("#file div").length == 0){
                   $("#submit").css("display","none");
              }
          }
       </script>
    ?
    </body>
    </html>
  • 配置文件上传

    spring:
    thymeleaf:
      cache: false
    ?
    servlet:
      multipart:
        max-file-size: 10MB
        max-request-size: 50MB
  • Controller

    @Controller
    public class FileController {
       //跳转到文件上传页面
       @GetMapping("/toUpload")
       public String toUpload(){
           return "upload";
      }
    ?
       //文件上传管理
       @PostMapping("/uploadFile")
       public String uploadFile(MultipartFile[] files, Model model){
           //默认文件上传成功,并返回状态信息
           model.addAttribute("uploadStatus","上传成功");
    ?
           for (MultipartFile file:files){
               //获取文件名以及后缀名
               String filename = file.getOriginalFilename();
               //重新生成文件名
               filename = UUID.randomUUID() + "_" + filename;
               //指定文件上传本地存储目录,不存在则提前创建
               String dirPath = "D:/zzz_file/";
               File filePath = new File(dirPath);
               if (filePath.exists()){
                   filePath.mkdirs();
              }
               try{
                   file.transferTo(new File(dirPath + filename));
              } catch (IOException e) {
                   e.printStackTrace();
                   model.addAttribute("uploadStatus","上传失败:" + e.getMessage());
              }
          }
           //携带状态信息回调到文件上传页面
           return "upload";
      }
    }

     

4.8、文件下载

  • 英文名文件下载

    • 1、添加文件下载工具依赖

      <dependency>
         <groupId>commons-io</groupId>
         <artifactId>commons-io</artifactId>
         <version>2.6</version>
      </dependency>
    • 2、文件下载页面

      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
         <meta charset="UTF-8">
         <title>Title</title>
      </head>
      <body>
      ?
         <div style="margin-bottom: 10px">
            文件下载列表
         </div>
      ?
         <table>
             <tr>
                 <td>001.jpg</td>
                 <td><a th:href="@{/download(filename=‘001.jpg‘)}">下载文件</a></td>
             </tr>
             <tr>
                 <td>我的简历</td>
                 <td><a th:href="@{/download(filename=‘我的简历.doc‘)}">下载文件</a> </td>
             </tr>
         </table>
      ?
      </body>
      </html>
    • 3、Controller

      @Controller
      public class FileController {
      ?
         //跳转到文件下载页面
         @GetMapping("/toDownload")
         public String toDownload(){
             return "download";
        }
      ?
         //文件下载管理
         @GetMapping("/download")
         public ResponseEntity<byte[]> fileDownload(String filename){
             //指定要下载的文件根路径
             String dirPath = "D:/testFace/";
             //创建该文件对象
             File file = new File(dirPath + File.separator +filename);
             //设置响应头
             HttpHeaders headers = new HttpHeaders();
             //通知浏览器以下载方式打开
             headers.setContentDispositionFormData("attachment",filename);
             //定义以流的形式下载返回文件数据
             headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
             try{
                 return new ResponseEntity<>(FileUtils.readFileToByteArray(file),headers, HttpStatus.OK);
            } catch (IOException e) {
                 e.printStackTrace();
                 return new ResponseEntity<byte[]>(e.getMessage().getBytes(),HttpStatus.EXPECTATION_FAILED);
            }
        }
      }

       

  • 中文名文件下载

    在上一步的下载中文名文件时,中文文字下载时全部不显示,解决此问题的方法如下:

    • Controller

      @Controller
      public class FileController {
      ?
         //跳转到文件下载页面
         @GetMapping("/toDownload")
         public String toDownload(){
             return "download";
        }
      ?
         //文件下载管理
         @GetMapping("/download")
         public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,String filename) throws UnsupportedEncodingException {
             //指定要下载的文件根路径
             String dirPath = "D:/testFace/";
             //创建该文件对象
             File file = new File(dirPath + File.separator +filename);
             //设置响应头
             HttpHeaders headers = new HttpHeaders();
             //通知浏览器以下载方式打开(下载前对文件名进行转码)
             filename = getFilename(request,filename);
             headers.setContentDispositionFormData("attachment",filename);
             //定义以流的形式下载返回文件数据
             headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
             try{
                 return new ResponseEntity<>(FileUtils.readFileToByteArray(file),headers, HttpStatus.OK);
            } catch (IOException e) {
                 e.printStackTrace();
                 return new ResponseEntity<byte[]>(e.getMessage().getBytes(),HttpStatus.EXPECTATION_FAILED);
            }
        }
      ?
         //根据不同的浏览器进行编码设置,返回编码后的文件名
         private  String getFilename(HttpServletRequest request,String filename) throws UnsupportedEncodingException {
             //IE不同版本User-Agent中出现的关键词
             String[] IEBrowserKeyWords = {"MSIE","Trident","Edge"};
             //获取请求头代理信息
             String userAgent = request.getHeader("User-Agent");
             for (String KeyWord:IEBrowserKeyWords){
                 if (userAgent.contains(KeyWord)){
                     //IE内核浏览器,统一为UTF-8编码显示,并对转换的 + 进行更正
                     return URLEncoder.encode(filename,"UTF-8").replace("+","");
                }
            }
             //火狐等其他浏览器统一为ISO-8859-1编码显示
             return new String(filename.getBytes("UTF-8"),"ISO-8859-1");
        }
      }

SpringBoot-11-扩展MVC

原文:https://www.cnblogs.com/LittleSkinny/p/13695432.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!