SpringMVC 基礎

這是我參與8月更文挑戰的第13天,活動詳情查看:8月更文挑戰css

介紹

  1. SpringMVC 是 Spring 框架集中的用以 Web 開發的 MVC 框架
  2. SpringMVC 框架是以請求爲驅動,圍繞 servlet 設計,將請求發給控制器,而後經過模型對象,分派器來展現請求結果視圖。其中核心類是 DispatcherServlet,它是一個 servlet,頂層是實現的 servlet 接口
  3. 優勢
    • 輕量級、簡單易學
    • 高效、基於請求響應的 MVC 框架
    • 與 Spring 無縫結合
    • 約定優於配置
    • 功能強大:Restful、數據驗證、格式化、本地化等
  4. 流程
    • 用戶發送請求至前端控制器 DispatcherServlet
    • DispatcherServlet 收到請求調用 HandlerMapping 處理器映射器
    • 處理器映射器找到具體的處理器(根據xml配置、註解進行),生成處理器對象及處理器攔截器返回給 DispatcherServlet
    • DispatcherServlet 調用 HandlerAdapter 處理器適配器
    • HandlerAdapter通過適配調用具體的處理器(Controller)
    • Controller執行完成返回 ModelAndView
    • HandlerAdapter 將 controller 執行結果 ModelAndView 返回給 DispatcherServlet
    • DispatcherServlet 將 ModelAndView 傳給 ViewReslover 視圖解析器
    • ViewReslover 解析後返回具體 View
    • DispatcherServlet 根據 View 進行渲染視圖(即將模型數據填充至視圖中)
    • DispatcherServlet 響應用戶

基本使用

  1. maven 建立 webapp 程序html

    一些依賴前端

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
    </dependencies>
    複製代碼
  2. web.xml 配置java

    <web-app>
        <!--前端控制器-->
        <servlet>
            <servlet-name>app</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>app</servlet-name>
            <!--若是使用 /*,會匹配 .jsp-->
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    複製代碼
  3. springmvc-servlet.xml 配置web

    <!--處理器映射器-->
    <!--BeanNameUrlHandlerMapping 是 SpringMVC 提供的處理器映射器的一種,按照 bean 的 name 屬性匹配-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
    <!--處理器適配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
    <!--視圖解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--將視圖位置設置在 WEB-IBF 下的 jsp 目錄中-->
    	<property name="prefix" value="/WEB-INF/jsp" />
        <property name="suffix" value=".jsp" />
    </bean>
    複製代碼
  4. HelloController.javaspring

    public class HelloController implements Controller {
    
        @Override
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
            ModelAndView mv = new ModelAndView();
            // 轉發到 WEB-INF/jsp/hello.jsp
            mv.setViewName("/hello");
            return mv;
        }
    }
    複製代碼
  5. 配置處理器json

    在 spingmvc-servlet.xml 中配置api

    <!--處理器-->
    <bean name="/hello" class="controller.HelloController"></bean>
    複製代碼
  6. 視圖文件數組

    在 webapp/WEB-INF 目錄下,新建 jsp 目錄,建立 hello.jsp服務器

  7. 啓動服務器,訪問 /hello 進行測試,成功轉發到視圖 hello.jsp

使用註解

  1. 配置開啓註解

    <!--開啓自動掃描-->
    <context:component-scan base-package="..." />
    <!--啓用默認的靜態資源處理器,html、js、css、mp3 等-->
    <mvc:default-servlet-handler />
    <!--啓用 MVC 註解支持,如 @RequestMapping-->
    <mvc:annotation-driven>
        <!--設置消息轉化器默認編碼,解決前端拿到數據亂碼-->
    	<mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="defaultCharset" value="UTF-8" />
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    複製代碼

    mvc:annotation-driven 幫咱們注入了 DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerMapping 兩個 bean

  2. 經常使用註解介紹

    @Controller 將類標註爲控制器,不用在繼承 Controller 接口了

    @RequestMapping 標註進入該控制器的匹配路徑,能夠用以類和方法

    @ResponseBody 將返回內容轉爲 JSON 字符串,寫入輸入流返回給前端

    @RequestParam 做用在方法參數中,從請求查詢串中解析參數內容

    @RequestBody 與 @RequestParam 相似,從請求體中解析參數內容

    @RestController 擁有 @Controller 功能,並將全部方法標註爲 @ResponseBody

    @Controller
    public class HelloController {
    
        // 匹配請求路徑
        // 相似:@GetMapping、@PostMapping...
        @RequestMapping("/hello")
        public String hello(Model model) {
            model.addAttribute("msg", "來自 HelloController.hello 的消息");
            // 沒使用 @ResponseBody,會走視圖解析器
            // 若是使用 @ResponseBody,則直接返回字符串
            return "hello";
        }
    }
    複製代碼

接受請求

  1. @RequestParam

    @ResponseBody
    @RequestMapping("/hello")
     public String hello(@RequestParam(value = "username", required = false) String name, @RequestParam(required = false, defaultValue = "1") Integer age) {
         return name + " " + age;
     }
    複製代碼

    value 用於定義有前端傳來的要解析的鍵名

    required 表示是否必須攜帶該數據,默認爲 true 時,沒有該數據沒法正確匹配

    defaultValue 用於在沒有解析到對應數據時賦予初值

    @RequestParam 使用場景

    可解析請求查詢串內容和請求類型爲 x-www-form-urlencoded 或 form-data 時的請求體內容

    1. 使用基本類型或 String 接收時,根據名稱匹配鍵值對
    2. 使用 String 接收時,若是查詢串和請求體內的同名鍵值對會自動以逗號拼接
    3. 使用 List 或數組接收時,可用一個鍵名對應多個逗號分隔的值,來綁定到 List 或數組
    4. 使用 List 或數組接收時,若是查詢串和請求體內的同名鍵值對會自動合併,能夠用 List、數組接收
    5. 使用 Map 接收時,可對查詢串和請求體內的鍵值對生成 Map
    6. 不可使用實體類接收
  2. @RequestBody

    @ResponseBody
    @RequestMapping("/hello")
    public User hello(@RequestBody User user) {
        return user;
    }
    複製代碼

    required 表示是否必須攜帶該數據,默認爲 true 時,沒有該數據沒法正確匹配

    @RequestBody 只能解析請求體中的內容,且請求類型爲 appliction/json

    使用 MappingJackson2HttpMessageConverter 進行解析和轉換,支持自動綁定實體類或 Map

  3. 不使用 @RequestParam 和 @RequestBody

    相似於 @RequestParam,能夠綁定實體類,不能夠接收 List、Map,通常不推薦使用

返回響應

  1. ModelAndView

    ModelAndView 轉發到 hello.jsp 並攜帶信息

    @RequestMapping("/hello")
    public ModelAndView hello() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("hello");
        modelAndView.addObject("msg", "轉發到 hello.jsp");
        return modelAndView;
    }
    複製代碼

    ModelAndView 構造方法

    ModelAndView mv = new ModelAndView("redirect:/404.htm"); // 重定向
    ModelAndView mv = new ModelAndView("forward:/404.htm"); // 請求轉發
    複製代碼

    重定向的方法還有返回 String, 如 return "redirect:/404.html",配合在參數中接收 Model 做爲參數,傳遞信息

    @RequestMapping("/hello")
    public String hello(Model model) {
       model.addAttribute("msg", "消息~");
       return "hello";
    }
    複製代碼

    Model是每次請求中都存在的默認參數,利用其 addAttribute() 方法傳遞內容到頁面中

    同時,咱們總可以在 Controller 的方法參數中使用 HttpServletRequest 和 HttpServletResponse 對象

    @RequestMapping("/hello")
    public String hello( HttpServletRequest request, HttpServletResponse response) {
        request.setAttribute("msg", "消息~");
        response.addCookie(new Cookie("c", "cookie~"));
        return "hello";
    }
    複製代碼
  2. @ResponseBody

    @RespnseBody 將方法結果寫入字符串,返回給前端

    @ResponseBody
    @RequestMapping(value = "/hello")
    public User hello(@RequestBody User user) {
        return user;
    }
    複製代碼

    返回結果是一個對象會通過轉換生成 JSON 格式的字符串,返回給前端

    默認使用 Jackson 進行處理,pom 引入 Jackson 後,自動調用

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.11.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.11.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.11.2</version>
    </dependency>
    複製代碼

文件上傳

  1. springmvc 中由 MultipartFile 接口實現文件上傳

  2. MultipartFile 接口有兩個繼承實現類,CommonsMultipartFile,StandardMultipartFile

    pom 中引入

    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.3</version>
    </dependency>
    複製代碼
  3. 在 Controller 方法參數中使用 MultipartFile 接收

    @ResponseBody
    @RequestMapping(value = "/ipload")
    public String uploadExcel(@RequestParam("file") MultipartFile file, HttpServletRequest req) throws Exception {
        if (file == null) return ;
        String fileName = file.getOriginalFilename();
        String path = req.getServletContext().getRealPath("/upload/");
       // 獲取原文件名
    	String fileName = file.getOriginalFilename();
    	// 建立文件實例
    	File filePath = new File(path, fileName);
    	// 若是文件目錄不存在,建立目錄
    	if (!filePath.getParentFile().exists()) {
    		filePath.getParentFile().mkdirs();
    	}
    	// 寫入文件
    	file.transferTo(filePath);
        return "success";
    }
    複製代碼

功能加強

  1. 攔截器

    自定義攔截器 Interceptor 實現 HandlerInterceptor 接口

    public class MyInterceptor implements HandlerInterceptor {
        // 進入接口方法前調用
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                // 經過 handler 能夠獲取要訪問的目標接口及方法
                // 進行相關權限判斷,返回 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 {
    
        }
    }
    複製代碼
    <!-- 配置攔截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 配置攔截器做用的路徑 -->
            <mvc:mapping path="/**" />
            <!--攔截器方法-->
            <bean class="cade.interceptor.MyInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
    複製代碼
  2. @ControllerAdvice

    經過 @ControllerAdvice 配合 @ExceptionHandler 能夠對控制層的異常進行處理

    @ControllerAdvice 能夠指定 basePackages,防止影響全局(如 swagger 不能正常使用)

    @ControllerAdvice
    public class ExceptionHandler {
    
        @ExceptionHandler(Exception.class)
        @ResponseBody
        public String handleEx(Exception e) {
            return "發生了異常:"+ e.getMessage();
        }
    }
    複製代碼
  3. 繼承 ResponseBodyAdvice 接口

    對 body 進行統一處理,如返回固定格式

    @ControllerAdvice
    public class ResponseHandler implements ResponseBodyAdvice {
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            // 返回 true,beforeBodyWrite 方法才生效
            return true;
        }
    
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
            // 對 body 進行統一處理,如返回固定格式
            return body;
        }
    }
    複製代碼
  4. 使用 AOP

    註解實現對接口方法的權限驗證

    @Aspect
    @Component
    public class RoleHandler {
    
        // 切入點:全部帶有 HasRole 註解的方法
        @Pointcut("@annotation(cade.annotation.HasRole)")
        public void needAuthMethod() {
    
        }
    
        @Before("needAuthMethod()")
        public void before(JoinPoint jp) {
           // 
        }
    }
    複製代碼

    AOP 中獲取 Method 對象和其上的註解

    MethodSignature signature = (MethodSignature) jp.getSignature();
    Method method = signature.getMethod();
    HasRole hasRole = method.getAnnotation(HasRole.class);
    複製代碼

    AOP 中獲取 requset、response 對象

    ServletRequestAttributes attributes = 
        (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    HttpServletRequest request = attributes.getRequest();
    HttpServletResponse response = attributes.getResponse();
    複製代碼

    AOP 獲取任意 Bean

    // 方法一
    // 自動注入依賴
    @Autowired
    ApplicationContext context;
    // context.getBean() 獲取任意 Bean
    
    // 方法二
    @Autowired
    ServletContext servletContext;
    // 也能夠是使用 request.getServletContext() 獲取
    // WebApplicationContextUtils.getWebApplicationContext(servletContext) 獲取 WebAppclicationContext
    // getBean() 獲取任意 Bean
    複製代碼
相關文章
相關標籤/搜索