zoukankan      html  css  js  c++  java
  • Spring MVC 基础

    1. Spring MVC 概述

    1.1 基本概念

    MVC : Model + View + Controller (数据模型 + 视图 + 控制器)。
    三层架构 : Presentation tier + Application tier + Data tier (展现层 + 应用层 + 数据访问层)。

    MVC 和 三层架构 的关系
    MVC只存在 三层架构 的展现层,M 实际上是数据模型,是包含数据的对象。在Spring MVC 里,有一个专门的类叫 Model,用来和 V 之间的数据交互、传值;V 指的是视图页面,包含JSP、freeMarker等;C控制器(Spring MVC 的注解 @Controller 的类)

    三层架构是整个应用的架构,是由 Spring 框架负责管理的。一般项目结构中都有 Service层、Dao层,这两个反馈在应用层和数据访问层。

    2. Spring MVC 常用注解

    2.1 常用注解类型

    (1)@Controller
    @Controller 注解在类上,表明这个类是 Spring MVC 里的 Controller,将其声明为 Spring 的一个 Bean,Dispatcher Servlet 会自动扫描注解了此注解的类,并将 Web 请求映射到注解了 @RequestMapping 的方法上。这里特别指出,在声明普通 Bean 的时候,使用 @Component、@Service、@Repository 和 @Controller 是等同的,因为@Service、@Repository、@Controller 都组合了 @Component 元注解;但在Spring MVC 声明控制器 Bean 的时候,只能使用 @Controller。
    (2)@RequestMapping
    @RequestMapping 注解时用来映射 Web 请求(访问路径和参数)、处理类和方法的。@RequestMapping 可注解在类或方法上。注解在方法上的 @RequestMapping 路径会继承注解在类上的路径,@RequestMapping 支持 Servlet 的 request 和 response 作为参数,也支持对 request 和 response 的媒体类型进行配置。
    (3)@ResponseBody
    @ResponseBody 支持将返回值放在 response 体内,而不是返回一个页面。我们在很多基于 Ajax 的程序的时候,可以以此注解返回数据而不是页面;此注解可放置在返回值前或者方法上。
    (4)@RequestBody
    @RequestBody 允许 request 的参数在 request 体中,而不是在直接链接在地址后面。此注解放置在参数前。
    (5)@PathVariable
    @PathVariable 用来接受路径参数,如 /new/001,可接收 001 作为参数,此注解放置在参数前。
    (6)@RestController
    @RestController 是一个组合注解,组合了 @Controller 和 @ResponseBody,这就意味着当用户只开发一个和页面交互数据的控制的时候,需要使用此注解。

    2.2 示例

    (1)演示获取 request 对象参数和返回此对象到 response:

    public class DemoObj {
        private Long id;
        private String name;
    
        public DemoObj() { //1
            super();
        }
    
        public DemoObj(Long id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    代码解释

    • 注释1:jackson 对对象和 json 做转换时一定需要此空构造。

    (2)注解演示控制器

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Controller //1
    @RequestMapping("/anno") //2
    public class DemoAnnoController {
    
        @RequestMapping(produces = "text/plain;charset=UTF-8") //3
        public @ResponseBody String index(HttpServletRequest request) { //4
            return "url:" + request.getRequestURL() + " can access";
        }
    
        @RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8") //5
        public @ResponseBody
        String demoPathVar(@PathVariable String str,
                           HttpServletRequest request) {
            return "url:" + request.getRequestURL() + " can access, str: " + str;
        }
    
        @RequestMapping(value = "/requestParam", produces = "text/plain;charset=UTF-8") //6
        public @ResponseBody
        String passRequestParam(Long id, HttpServletRequest request) {
            return "url:" + request.getRequestURL() + " can access, id: " + id;
        }
    
        @RequestMapping(value = "/obj", produces = "application/json;charset=UTF-8") //7
        public @ResponseBody
        String passObj(DemoObj obj, HttpServletRequest request) {
            return "url:" + request.getRequestURL() + " can access, obj id: " + obj.getId() + "obj name: " + obj.getName();
        }
    
        @RequestMapping(value = { "/name1", "/name2" }, produces = "text/plain;charset=UTF-8") //9
        public @ResponseBody
        String remove(HttpServletRequest request) {
            return "url:" + request.getRequestURL() + " can access ";
        }
    
    }
    

    代码解释

    • 注释1:@Controller 注解声明此类是一个控制器。
    • 注释2:@RequestMapping("/anno") 映射此类的访问路径是/anno。
    • 注释3:此方法未标注路径,因此使用类级别的路径/anno;produces 可定制返回的 response 的媒体类型和字符集,或需返回值是 json 对象,则设置 produces = "application/json;charset=UTF-8"。
    • 注释4:演示可接受 HttpServletRequest 作为参数,当然也可以接受 HttpServletResponse 作为参数。此处的 @ResponseBody 用在返回值前面。
    • 注释5:演示接受路径参数,并在方法参数前结合 @PathVariable 使用,访问路径 /anno/pathvar/xx。
    • 注释6:演示常规的 request 参数获取,访问路径为 /anno/requestParam?id=1。
    • 注释7:演示解释参数到对象,访问路径为 /anno/obj?id=1&name=xx。
    • 注释8:@ResponseBody 也可以用在方法上。
    • 注释9:演示映射不同的路径到相同的方法,访问路径为/anno/name1 或 /anno/name2。

    3. Spring MVC 基本配置

    3.1 基础知识

    Spring MVC 的定制配置需要我们的配置类继承一个 WebMvcConfigurerAdapter 类,并在此类使用 @EnableWebMvc 注解,来开启对 Spring MVC 的配置支持,这样就可以重写这个类的方法,完成常用的配置。

    3.2 静态资源映射

    3.2.1 基本知识

    程序的静态文件(js、css、图片)等需要直接访问,这时可以通过在配置里重写 addResourceHandlers 方法来实现。

    3.3 拦截器配置

    3.3.1 基本知识

    拦截器(interceptor)实现对每一个请求处理前后进行相关的业务处理,类似于 Servlet 的 Filter。
    可让普通的 Bean 实现 HandlerInterceptor 接口或者继承 HandlerInterceptorAdapter 类来实现自定义拦截器。
    通过重写 WebMvcConfigurerAdapter 的 addInterceptors 方法来注册自定义的拦截器。

    3.3.2 示例

    (1)示例拦截器。

    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class DemoInterceptor extends HandlerInterceptorAdapter { //1
    
        @Override
        public boolean preHandle(HttpServletRequest request, //2
                                 HttpServletResponse response, Object handler) throws Exception {
            long startTime = System.currentTimeMillis();
            request.setAttribute("startTime",startTime);
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, //3
                               HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            long startTime = (long) request.getAttribute("startTime");
            request.removeAttribute("startTime");
            long endTime = System.currentTimeMillis();
            System.out.println("本次处理时间为:" + new long(endTime - startTime) + "ms");
            request.setAttribute("handlingTime", endTime - startTime);
        }
    }
    

    代码解释

    • 注释1:继承 HandlerInterceptorAdapter 类来实现自定义拦截器。
    • 注释2:重写 preHandle 方法,在请求发生前执行。
    • 注释3:重写 postHandle 方法,在请求完成后执行。

    (2)配置。

        @Bean //1
        public DemoInterceptor demoInterceptor() {
            return new DemoInterceptor();
        }
    
        public void addInterceptors(InterceptorRegistry registry) { //2
            registry.addInterceptor(demoInterceptor());
        }
    

    代码解释

    • 注释1:配置拦截器的 Bean。
    • 注释2:重写 addInterceptors 方法,注册拦截器。

    3.4 @ControllerAdvice

    3.4.1 基本知识

    通过 @ControllerAdvice,我们可以将对于控制器的全局配置放置在同一个位置,注解了 @Controller 的类的方法可使用 @ExceptionHandler、@InitBinder、@ModelAttribute 注解到方法上,这对所有注解了 @RequestMapping 的控制器内的方法有效。

    • @ExceptionHandler:用于全局处理控制器的异常。
    • @InitBinder:用来设置 WebDataBinder,WebDataBinder 用来自动绑定前台请求参数到 Model 中。
    • @ModelAttribute:@ModelAttribute 本来的作用是绑定键值对到 Model 里,此处是让全局的 @RequestMapping 都能获得在此处设置的键值对。

    3.4.2 示例

    (1)定制 ControllerAdvice。

    
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.InitBinder;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.context.request.WebRequest;
    import org.springframework.web.servlet.ModelAndView;
    
    @ControllerAdvice //1
    public class ExceptionHandlerAdvice {
        
        @ExceptionHandler(value = Exception.class) //2
        public ModelAndView exception(Exception exception, WebRequest request) {
            ModelAndView modelAndView = new ModelAndView("error"); // error 页面
            modelAndView.addObject("errorMessage", exception.getMessage());
            return modelAndView;
        }
    
        @ModelAttribute //3
        public void addAttributes(Model model) {
            model.addAttribute("msg", "额外信息"); //3
        }
    
        @InitBinder  //4
        public void initBinder(WebDataBinder webDataBinder) {
            webDataBinder.setDisallowedFields("id");
        }
    }
    

    代码解释

    • 注释1:@ControllerAdvice 声明一个控制器建言,@ControllerAdvice 组合了 @Component 注解,所以自动注册为 Spring 的 Bean。
    • 注释2:@ExceptionHandler 在此处定义全局处理,通过 @ExceptionHandler 的 value 属性可过滤拦截的条件。
    • 注释3:此处使用 @ModelAttribute 注解将键值对添加到全局,所有注解的 @RequestMapping 的方法可获得此键值对。
    • 注释4:通过 @InitBinder 注解定制 WebDataBinder。

    4. Spring MVC 高级配置

    4.1 文件上传配置

    4.1.1 基础知识

    文件上传是项目中常用的功能,Spring MVC 通过配置一个 MultipartResolver 来上传文件。
    在 Spring 的控制器中,通过 MultipartFile file 来接收文件,通过 MultipartFile[] files 接收多个文件上传。

    4.1.2 示例

    (1)添加文件上传依赖
    (2)上传 jsp 页面
    (3)添加转向到 upload 页面的 ViewController。

          @Override
          public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/index").setViewName("/index");
                registry.addViewController("/toUpload").setViewName("/upload");
          }
    

    (4)MultipartResolver 配置。

          @Bean
          public MultipartResolver multipartResolver() {
                CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
                multipartResolver.setMaxUploadSize(1000000);
                return multipartResolver;
          }
    

    (5)控制器。

    
    import org.apache.tomcat.util.http.fileupload.FileUtils;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.IOException;
    
    @Controller
    public class UploadController {
        @RequestMapping(value = "/upload", method = RequestMethod.POST)
        public @ResponseBody
        String upload(MultipartFile file) { //1
    
            try {
                FileUtils.writeByteArrayToFile(new File("e:/upload/" + file.getOriginalFilename()), file.getBytes()); //2
                return "OK";
            } catch (IOException e) {
                e.printStackTrace();
                return "wrong";
            }
        }
    }
    

    代码解释

    • 注释1:使用 MultipartFile file 接受上传的文件。
    • 注释2:使用 FileUtils。writeByteArrayToFile 快速写文件到磁盘。

    4.2 自定义 HttpMessageConverter

    4.2.1 基础知识

    HttpMessageConverter 是用来处理 request 和 response 里的数据的。 Spring 为我们内置了大量的 HttpMessageConverter,例如,MappingJackson2HttpMessageConverter、StringHttpMessageConverter 等。

    4.3 服务器端推送技术

    4.3.1 基础知识

    早期,人们喜欢使用Ajax向服务器轮询消息,是浏览器尽可能第一时间获得服务端的消息,因为这种方式的轮询频率不好控制,所以大大增加了服务端的压力。
    MVC的服务器端推送方案都是基于:当客户端向服务端发送请求,服务端会抓住这个请求不放,等有数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求,周而复始。这种方式的好处是减少了服务器的请求数量,大大减少了服务器的压力。

    4.3.2 SSE(Server Send Event 服务端发送事件)

    (1)演示控制器

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Random;
    
    @Controller
    public class SseController {
        
        @RequestMapping(value = "/push",produces = "text/event-stream") //1
        public @ResponseBody String push() {
            Random r = new Random();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "data:Testing 1,2,3" + r.nextInt() + "
    
    ";
        }
    }
    

    代码解释

    • 注释1:注意,这里使用输出的媒体类型为 test/event-stream,这是服务器端 SSE 的支持。

    (2)演示页面
    使用 JSP 编写
    (3)配置

    registry.addViewController("/see").setViewName("/see");
    

    4.3.3 Servlet 3.0+ 异步方法处理

    (1)开启异步方法支持:

          Dynamic servlet - servletContext.addServlet("dispatcher", new DispatcherServlet(ctx)) //3
          servlet.addMapping("/");
          servlet.setLoadOnStartup(1);
          servlet.setAsyncSupported(true); //1
    

    代码解释

    • 注释1:开启异步方法支持。

    (2)演示控制器:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.context.request.async.DeferredResult;
    
    @Controller
    public class AysncController {
        @Autowired
        PushService pushService; //1
    
        @RequestMapping("/defer")
        @ResponseBody
        public DeferredResult<String> deferredCall() { //2
            return pushService.getAsyncUpdate();
        }
    }
    
    

    代码解释
    异步任务的实现是通过控制器从另外一个线程返回一个 DeferredResult,这里的 DeferredResult 是从 pushService 中获得的。

    • 注释1:定时任务,定时更新 DeferredResult。
    • 注释2:返回给客户端 DeferredResult。

    (3)定时任务:

    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;
    import org.springframework.web.context.request.async.DeferredResult;
    
    @Service
    public class PushService {
        private DeferredResult<String> deferredResult; //1
        
        public DeferredResult<String> getAsyncUpdate() { //1
            deferredResult = new DeferredResult<String>();
            return deferredResult;
        }
        
        @Scheduled(fixedDelay = 5000)
        public void refresh() { //1
            if (deferredResult != null) {
                deferredResult.setResult(new Long(System.currentTimeMillis()).toString());
            }
        }
    }
    

    代码解释

    • 注释1:在 PushService 里产生 DeferredResult 给控制器使用,通过 @Scheduled 注解的方法定时更新 DeferredResult。

    (4)演示界面

    <body>
    <script type="text/javascript" src="assets/js/jquery.js"></script>
    <script type="text/javascript">
    
          deferred(); //1
          
          function deferred(){
                $.get('defer',function(data){
                      console.log(data); //2
                      deferred(); //3
                });
          }
    </script>
    </body>
    

    代码解释
    此处代码使用的是 jQuery 的 Ajax 请求,所以没有浏览器兼容性的问题。

    • 注释1:页面打开就向后台发送请求。
    • 注释2:在控制台输出服务端推送的数据。
    • 注释3:一次请求完成后再向后台发送请求。

    (5)配置。
    在 MyMvcConfig 上开始计划任务的支持,使用 @EnableScheduling:

    @Configuration
    @EnableWebMvc
    @ComponentScan("com.wisely.highlight_springmvc4")
    public class MyMvcConfig extends WebMvcConfigurerAdapter{
    
    }
    

    添加 viewController:

    registry.addViewController("/async").setViewName("/async");
    
  • 相关阅读:
    ubuntu如何设置dns?
    docker服务无法启动如何处理?
    yocto编译时报错"fontconfig-2.12.1/src/fcmatch.c:324:63: error: ‘PRI_CHAR_WIDTH_STRONG' undeclared here (not in a function); did you mean ‘PRI_WIDTH_STRONG’?"
    git如何将当前仓库的远程分支推到一个新的仓库中?
    linux下如何制作ubuntu系统启动盘(sd)?
    yocto编译时报错"glibc/locale/xlocale.h:39:4: note: previous declaration of '__locale_t' was here"
    19、异常处理
    18、文件与目录
    17、python对内存的使用
    16、正则表达式
  • 原文地址:https://www.cnblogs.com/john1015/p/13841424.html
Copyright © 2011-2022 走看看