SpringMVC

1.什麼是SpringMVC

1.1、什麼是MVC

  • MVC是模型(Model)、視圖(View)、控制器(Controller)的簡寫,是一種軟件設計規範。
  • 是將業務邏輯、數據、顯示分離的方法來組織代碼。
  • MVC主要作用是降低了視圖與業務邏輯間的雙向偶合
  • MVC不是一種設計模式,MVC是一種架構模式。當然不同的MVC存在差異。

Model(模型):數據模型,提供要展示的數據,因此包含數據和行為,可以認為是領域模型或JavaBean組件(包含數據和行為),不過現在一般都分離開來:Value Object(數據Dao) 和 服務層(行為Service)。也就是模型提供了模型數據查詢和模型數據的狀態更新等功能,包括數據和業務。

View(視圖):負責進行模型的展示,一般就是我們見到的用戶界面,客戶想看到的東西。

Controller(控制器):接收用戶請求,委託給模型進行處理(狀態改變),處理完畢後把返回的模型數據返回給視圖,由視圖負責展示。也就是說控制器做了個調度員的工作。

最典型的MVC就是JSP + servlet + javabean的模式。

1.2、Spring概述

Spring MVC是Spring Framework的一部分,是基於Java實現MVC的輕量級Web框架。

查看官方文檔://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#spring-web

我們為什麼要學習SpringMVC呢?

Spring MVC的特點:

  1. 輕量級,簡單易學
  2. 高效 , 基於請求響應的MVC框架
  3. 與Spring兼容性好,無縫結合
  4. 約定優於配置
  5. 功能強大:RESTful、數據驗證、格式化、本地化、主題等
  6. 簡潔靈活

Spring的web框架圍繞DispatcherServlet [ 調度Servlet ] 設計。

DispatcherServlet的作用是將請求分發到不同的處理器。從Spring 2.5開始,使用Java 5或者以上版本的用戶可以採用基於註解形式進行開發,十分簡潔;

正因為SpringMVC好 , 簡單 , 便捷 , 易學 , 天生和Spring無縫集成(使用SpringIoC和Aop) , 使用約定優於配置 . 能夠進行簡單的junit測試 . 支持Restful風格 .異常處理 , 本地化 , 國際化 , 數據驗證 , 類型轉換 , 攔截器 等等……所以我們要學習 .

最重要的一點還是用的人多 , 使用的公司多 .

1.3、中心控制器

​ Spring的web框架圍繞DispatcherServlet設計。DispatcherServlet的作用是將請求分發到不同的處理器。從Spring 2.5開始,使用Java 5或者以上版本的用戶可以採用基於註解的controller聲明方式。

​ Spring MVC框架像許多其他MVC框架一樣, 以請求為驅動 , 圍繞一個中心Servlet分派請求及提供其他功能DispatcherServlet是一個實際的Servlet (它繼承自HttpServlet 基類)

SpringMVC的原理如下圖所示:

​ 當發起請求時被前置的控制器攔截到請求,根據請求參數生成代理請求,找到請求對應的實際控制器,控制器處理請求,創建數據模型,訪問數據庫,將模型響應給中心控制器,控制器使用模型與視圖渲染視圖結果,將結果返回給中心控制器,再將結果返回給請求者。

1.4、SpringMVC執行原理

圖為SpringMVC的一個較完整的流程圖,實線表示SpringMVC框架提供的技術,不需要開發者實現,虛線表示需要開發者實現。

簡要分析執行流程

  1. DispatcherServlet表示前置控制器,是整個SpringMVC的控制中心。用戶發出請求,DispatcherServlet接收請求並攔截請求。

    我們假設請求的url為 : //localhost:8080/SpringMVC/hello

    如上url拆分成三部分:

    //localhost:8080服務器域名

    SpringMVC部署在服務器上的web站點

    hello表示控制器

    通過分析,如上url表示為:請求位於服務器localhost:8080上的SpringMVC站點的hello控制器。

  2. HandlerMapping為處理器映射。DispatcherServlet調用HandlerMapping,HandlerMapping根據請求url查找Handler。

  3. HandlerExecution表示具體的Handler,其主要作用是根據url查找控制器,如上url被查找控制器為:hello。

  4. HandlerExecution將解析後的信息傳遞給DispatcherServlet,如解析控制器映射等。

  5. HandlerAdapter表示處理器適配器,其按照特定的規則去執行Handler。

  6. Handler讓具體的Controller執行。

  7. Controller將具體的執行信息返回給HandlerAdapter,如ModelAndView。

  8. HandlerAdapter將視圖邏輯名或模型傳遞給DispatcherServlet。

  9. DispatcherServlet調用視圖解析器(ViewResolver)來解析HandlerAdapter傳遞的邏輯視圖名。

  10. 視圖解析器將解析的邏輯視圖名傳給DispatcherServlet。

  11. DispatcherServlet根據視圖解析器解析的視圖結果,調用具體的視圖。

  12. 最終視圖呈現給用戶。

在這裡先聽一遍原理,不理解沒有關係,我們馬上來寫一個對應的代碼實現大家就明白了,如果不明白,那就寫10遍,沒有笨人,只有懶人!

2.實現SpringMVC(配置版)

  1. 新建一個Moudle,添加Web支持。
  2. 確定導入了SpringMVC的依賴
  3. 配置web.xml,註冊DispatcherServlet前端控制器,這是SpringMVC核心。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="//xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="//xmlns.jcp.org/xml/ns/javaee //xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--1.註冊前端控制器:DispatcherServlet,這是SpringMVC的核心-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 也可不配置參數,默認加載 /WEB-INF/springmvc-servlet.xml -->
        <!-- DispatcherServlet要綁定一個springmvc配置文件:【servlet-name】- servlet.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!--啟動級別-1-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- / 匹配接受用戶所有的請求;(不包括.jsp)-->
    <!-- /* 匹配接受用戶所有的請求;(包括.jsp)-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  1. 配置SpringMVC的配置文件:springmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="//www.springframework.org/schema/beans"
        xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="//www.springframework.org/schema/beans
        //www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1.添加處理映射器-->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!--2.添加處理器適配器-->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!--3.添加視圖解析器:DispatcherServlet給它的ModelAndView
        1.獲取了ModelAndView的數據
        2.解析ModelAndView的視圖名字
        3.拼接視圖名字,找到對應的視圖 /WEB-INF/jsp/hello.jsp
        4.將數據渲染到這個視圖上
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--前綴-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--後綴-->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--4.Handler-->
    <bean id="/hello" class="com.dzj.controller.HelloController" />

</beans>
  1. 創建Controller類
package com.dzj.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//注意:我們這裡先導入Controller接口
public class HelloController implements Controller {

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception{

        //模型和視圖
        ModelAndView mv = new ModelAndView();

        //業務代碼,假設這裡處理了一個業務,返回的結果為result
        String result = "HelloSpringMVC!";
        //封裝對象,放在ModelAanView中,Model
        mv.addObject("msg",result);
        
        //視圖跳轉封裝要跳轉的視圖,放在ModelAndView中,view
        mv.setViewName("hello"); // 自動拼接成:/WEB-INF/jsp/hello.jsp
        return mv;

    }
}

3.實現SpringMVC(註解版)

第一步:新建一個Moudle,添加web支持,建立包結構 com.dzj.controller

第二步:由於Maven可能存在資源過濾問題,我們將配置完善!

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

第三步:在pom.xml文件引入相關的依賴:主要有Spring框架核心庫、SpringMVC、servlet、JSTL等。如果在父依賴中已經引入了,就不需要再引入。注意:在項目結構下添加 lib文件夾,添加 Library Files

<!--導入依賴-->
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- //mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.9</version>
    </dependency>
    <!-- //mvnrepository.com/artifact/javax.servlet/servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
    <!-- //mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2.1-b03</version>
        <scope>provided</scope>
    </dependency>
    <!-- //mvnrepository.com/artifact/javax.servlet/jstl -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

<!--資源過濾-->
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

第四步:配置web.xml文件

  • 註冊DispatcherServlet
  • 關聯SpringMVC配置文件
  • 啟動級別為1
  • 映射路勁為 「 / 」 ,【不要用「 /* 」 ,會404】
<!--1.註冊前端控制器:DispatcherServlet,這是SpringMVC的核心-->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- 也可不配置參數,默認加載 /WEB-INF/springmvc-servlet.xml -->
    <!-- DispatcherServlet要綁定一個springmvc配置文件:【servlet-name】- servlet.xml-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <!--啟動級別-1-->
    <load-on-startup>1</load-on-startup>
</servlet>

<!-- / 匹配接受用戶所有的請求;(不包括.jsp)-->
<!-- /* 匹配接受用戶所有的請求;(包括.jsp)-->
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

第五步:添加Spring MVC配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="//www.springframework.org/schema/beans"
        xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
        xmlns:context="//www.springframework.org/schema/context"
        xmlns:mvc="//www.springframework.org/schema/mvc"
        xsi:schemaLocation="//www.springframework.org/schema/beans
        //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/context //www.springframework.org/schema/context/spring-context.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 自動掃描包,讓指定包下的註解生效,由IOC容器統一管理 -->
    <context:component-scan base-package="com.dzj.controller"/>
    <!-- 讓Spring MVC 不處理靜態資源 .css .js  .html .mp3 .mp4-->
    <mvc:default-servlet-handler />
    <!--
        支持mvc註解驅動
        在spring中一般採用@RequestMapping註解來完成映射關係;
        要想使@RequestMapping註解生效;
        必須想上下文中註冊DefaultAnnotationHandlerMapping和一個AnnotationMethodHandlerAdapter實例,
        這兩個實體分別在類級別和方法級別處理;
        而annotation-driver配置幫助我們自動完成上述兩個實例的注入。
    -->
    <mvc:annotation-driven/>
    <!-- 視圖解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
        <!--前綴-->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!--後綴-->
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

第六步:創建controller,HelloController.java

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello(Model model){
        //在模型中添加 msg 和值,可以在jsp中取出並渲染
        model.addAttribute("msg","Hello,SpringMVCAnnotation!");
        return "hello";   // hello 會被視圖解析器處理
    }

}

第七步:創建視圖層,hello.jsp

第八步:配置Tomcat運行

4.註解使用說明

@Controller

//被@Controller註解的類,代表這個類被Spring託管,此類中的方法如果返回值是String類型,
// 並且有具體的頁面可以跳轉,那麼就會被視圖解析器解析
@Controller
public class ControllerTest2 {

    @RequestMapping("/test2")
    public String test2(Model model){
        model.addAttribute("msg","ControllerTest2");
        return "test";
    }
}

@RequestMapping

@Controller
@RequestMapping("/c3")  //標註在類上,可理解為第一層級目錄
public class ControllerTest3 {
    @RequestMapping("/test2")  //標註在方法上,可理解為第二層級目錄
    public String test3(Model model){
        model.addAttribute("msg","ControllerTest3");
        return "test";
    }
}
@Controller
public class ControllerTest3 {

    @RequestMapping("/c3/test2")  //等價於上面的寫法
    public String test3(Model model){
        model.addAttribute("msg","ControllerTest3");
        return "test";
    }
}

5.RestFul風格

@Controller
public class RestFulController {

    // 原來的方式 ---> //localhost:8080/add?a=3&b=7
    // RestFul方式 ---> //localhost:8080/add/1/2
    // @RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.DELETE)
    
    @PostMapping("/add/{a}/{b}")
    public String test1(@PathVariable int a,@PathVariable String b, Model model){
        //a,b為前端傳遞過來的參數
        String res = a + b;
        model.addAttribute("msg","結果為1:" + res);
        return "test";
    }
    @GetMapping("/add/{a}/{b}")
    public String test2(@PathVariable int a,@PathVariable String b, Model model){
        //a,b為前端傳遞過來的參數
        String res = a + b;
        model.addAttribute("msg","結果為2:" + res);
        return "test";
    }
}

6.SpringMVC的結果跳轉方式

1.ModelAndView

設置ModelAndView對象,根據view的名稱,和視圖解析器跳轉到指定的頁面

頁面 = {視圖解析器前綴} + viewName + {視圖解析器後綴}

<!--視圖解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--前綴-->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!--後綴-->
    <property name="suffix" value=".jsp"/>
</bean>

對應的Controller類

public class HelloController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //模型和視圖
        ModelAndView mv = new ModelAndView();
        //業務代碼,假設這裡處理了一個業務,返回的結果為result
        String result = "HelloSpringMVC!";
        //封裝對象,放在ModelAanView中,Model
        mv.addObject("msg",result);
        //視圖跳轉封裝要跳轉的視圖,放在ModelAndView中,view
        mv.setViewName("hello"); // 自動拼接成:/WEB-INF/jsp/hello.jsp
        return mv;
    }
}

2.ServletAPI

@Controller
public class ModelTest1 {
    @RequestMapping("/m1")
    public String test1(HttpServletRequest req, HttpServletResponse resp){
        HttpSession session = req.getSession();
        System.out.println(session.getId());
        return "test";
    }
}

3.SpringMVC

不需要視圖解析器

@Controller
public class ModelTest1 {

    @RequestMapping("/m1")
    public String test1(Model model){
        model.addAttribute("msg","ModleTest1");
        //轉發,地址欄沒有發生變化
        return "/WEB-INF/jsp/test.jsp";  //注意使用全路徑
        // 也可寫成這樣 return "forward:/WEB-INF/jsp/test.jsp";
    }
}
@Controller
public class ModelTest1 {

    @RequestMapping("/m1")
    public String test1(Model model){
        model.addAttribute("msg","ModelTest1");
        //重定向,地址欄會發生變化
        return "redirect:/index.jsp";
    }
}

有視圖解析器

默認情況下為轉發,在前面加上redirect為重定向

@Controller
public class ModelTest1 {

    @RequestMapping("/m1")
    public String test1(Model model){
        model.addAttribute("msg","ModelTest1");
        //轉發
        return "test";
    }
    @RequestMapping("/m2")
    public String test2(Model model){
        model.addAttribute("msg","ModelTest1");
        //重定向
        return "redirect:/index.jsp";
    }
}

7.SpringMVC數據處理

1.處理提交數據

提交的域名稱和處理方法的參數一致的情況

@Controller
@RequestMapping("/user")
public class UserController {

    // //localhost:8080/user/t1?name=dengzhijiang
    @GetMapping("t1")
    public String Test(String name, Model model){
        //1.接受前端數據
        System.out.println("接受前端的數據為:" + name);
        //2.將業務處理後的結果返回給前端,Model
        model.addAttribute("msg",name);
        //3.視圖跳轉
        return "test";
    }
}

提交的域名稱和處理方法的參數不一致的情況

只需在參數前加上 @RequestParma,建議以後不管一不一樣都加上

@Controller
@RequestMapping("/user")
public class UserController {

    // //localhost:8080/user/t1?name=dengzhijiang
    @GetMapping("t1")
    public String Test(@RequestParam("username") String name, Model model){
        //1.接受前端數據
        System.out.println("接受前端的數據為:" + name);
        //2.將業務處理後的結果返回給前端,Model
        model.addAttribute("msg",name);
        //3.視圖跳轉
        return "test";
    }
}

前端接受的是一個對象

1.接受前端用戶傳遞的參數,判斷參數的名字,假設名字直接在方法上,可以直接使用
2.假設傳遞的是一個對象User,匹配對象User中的字段,如果名字一致則ok,否則匹配不到
3.如果使用對象的話,前端傳遞的參數名,和對象名屬性必須一致,否則就是null
@GetMapping("t2")
public String Test2(User user, Model model){
    // //localhost:8080/user/t2?id=11&name=dengzhijiang&age=22
    System.out.println(user);
    model.addAttribute("msg",user);
    //3.視圖跳轉
    return "test";
}

2.數據顯示到前端

第一種:通過ModelAndView

public class HelloController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        //模型和視圖
        ModelAndView mv = new ModelAndView();
        //業務代碼,假設這裡處理了一個業務,返回的結果為result
        String result = "HelloSpringMVC!";
        //封裝對象,放在ModelAanView中,Model
        mv.addObject("msg",result);
        //視圖跳轉封裝要跳轉的視圖,放在ModelAndView中,view
        mv.setViewName("hello"); // 自動拼接成:/WEB-INF/jsp/hello.jsp
        return mv;
    }
}

第二種:通過Model

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello(Model model){
        //在模型中添加 msg 和值,可以在jsp中取出並渲染
        model.addAttribute("msg","Hello,SpringMVCAnnotation!");
        return "hello";   // hello 會被視圖解析器處理
    }
}

第三種:通過ModelMap

3.亂碼問題

1.自定義過濾器處理亂碼

創建一個過濾器

public class EncodingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {

    }
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }
    public void destroy() {

    }
}

在web.xml中配置過濾器

<filter>
    <filter-name>encoding</filter-name>
    <filter-class>com.dzj.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
2.可以把Post方法改成Get方法
3.配置SpringMVC的亂碼過濾器
<!--2.配置SpringMVC的亂碼過濾器-->
<filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

8.JSON

  • JSON是JavaScript對象的字符串表示法,它使用文本表示表示一個JS對象的信息,本質是一個字符串。
var json = {"name":"鄧志江","age":3,"sex":"男"}  //這是一個JSON字符串
var obj = {name: '鄧志江', age: 3, sex: '男'}   //這是一個Javas對象
  • JSON 和 JavaScript對象的互轉
//JavaScript對象轉換JSON對象,使用 JSON.stringfy()方法
var json = JSON.stringify(user);
//JSON轉換成JavaScript對象,使用 JSON.parse()方法
var obj = JSON.parse(json);

9.Controller返回JSON數據

1.使用 jackson 方式

1.導入依賴

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.3</version>
</dependency>

2.JSON亂碼配置問題

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
        </bean>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="failOnEmptyBeans" value="false"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

3.實現對象轉換為 JSON 字符串

//@Controller
@RestController  //如果在類上標註了@RestController,就不會走視圖解析器
public class UserController {
    @RequestMapping("/j1")
    //@ResponseBody  //加了這個註解,就不會走仕途解析器,會直接返回一個字符串,配合@Controller使用
    public String json1() throws JsonProcessingException {
        //jackson里有一個ObjectMapper對象,可以把user對象轉換成json字符串
        ObjectMapper mapper = new ObjectMapper();
        User user = new User("鄧志江", 22, "男");
        String str = mapper.writeValueAsString(user);// 把user對象轉換為JSON字符串
        return str;
    }
}

4.實現集合類型轉換為 JSON 字符串

@RequestMapping("/j2")
public String json2() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    List<User> userList = new ArrayList<User>();
    User user1 = new User("鄧志江1號", 22, "男");
    User user2 = new User("鄧志江2號", 22, "男");
    User user3 = new User("鄧志江3號", 22, "男");
    User user4 = new User("鄧志江4號", 22, "男");
    User user5 = new User("鄧志江5號", 22, "男");

    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    userList.add(user4);
    userList.add(user5);

    String str = mapper.writeValueAsString(userList);// 把user對象轉換為JSON字符串
    return str;
}

5.實現日期類型轉換為 JSON 字符串

@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
    Date date = new Date();
    //把時間按照一定的格式顯示
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    //ObjectMapper將時間解析後的格式為:timestamp,時間戳
    return new ObjectMapper().writeValueAsString(sdf.format(date));
}

6.源碼思想

方法的重載,方法名相同,根據參數的不同調用不同的方法

public static String getJson(Object object){
    return getJson(object,"yyyy-MM-dd HH:mm:ss");
}

public static String getJson(Object object, String dateFormat){
    ObjectMapper mapper = new ObjectMapper();
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
    //把時間按照一定的格式顯示
    SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
    mapper.setDateFormat(sdf);
    try {
        String str = mapper.writeValueAsString(object);
        return str;
    } catch (JsonProcessingException e) {
        e.printStackTrace();
    }
    return null;
}

2.使用fastjson方式

1.添加 fastjson 依賴

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.78</version>
</dependency>

2.直接使用 JSON.toJSONString()實現轉換

@RequestMapping("/j4")
public String json4(){
    List<User> userList = new ArrayList<User>();
    User user1 = new User("鄧志江1號", 22, "男");
    User user2 = new User("鄧志江2號", 22, "男");
    User user3 = new User("鄧志江3號", 22, "男");
    User user4 = new User("鄧志江4號", 22, "男");
    User user5 = new User("鄧志江5號", 22, "男");
    userList.add(user1);
    userList.add(user2);
    userList.add(user3);
    userList.add(user4);
    userList.add(user5);
    return JSON.toJSONString(userList); //把集合轉化為JSON字符串
}

10.SpringMVC:Ajax技術

10.1 簡介

10.2 偽造Ajax

1. 新建一個Moudule

新建一個Moudule:springmvc-06-ajax,導入web支持

2. 編寫一個test.html

編寫一個test.html 使用 iframe 測試,感受頁面無刷新效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>iframe測試體驗頁面無刷新</title>
    <script type=text/javascript>
        function go() {
            var url = document.getElementById("url").value;
            document.getElementById("iframe1").src=url;
        }
    </script>
</head>
<body>
    <div>
        <p>請輸入請求地址:</p>
        <p>
            <input type="text" id="url" value="//www.baidu.com/" />
            <input type="button" value="提交" onclick="go()"/>
        </p>
    </div>
    <div>
        <iframe id="iframe1" style="width: 100%;height: 500px"></iframe>
    </div>

</body>
</html>

10.3 實現Ajax

1. 下載並導入jQuery
2. 添加靜態資源過濾

在applicationContext.xml中添加靜態資源過濾

<!--靜態資源過濾-->
<mvc:default-servlet-handler/>
3. 編寫一個前端頁面

實現:當失去焦點的時候,發起一個請求

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
    <%--引入jQuery--%>
    <script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
    <script>
      function a() {
          //通過post方式向後台發起請求
          $.post({
              url:"${pageContext.request.contextPath}/a1", //請求地址
              data:{"name":$("#username").val()},  //傳遞的數據
              success:function (data,status) {    //請求之後的回調函數,返回數據,data接受返回的數據
                  console.log("data="+data);
                  console.log("status="+status);// status 表示請求的狀態 200 300 400 500
          })
      }
    </script>
  </head>
  <body>
  <%--失去焦點的時候,發起一個請求--%>
  用戶名:<input type="text" id="username" onblur="a()">
  </body>
</html>
4. 後台接受數據返回結果

注意:方法中的參數要和前端ajax中傳遞的參數名一致

data:{"name":$("#username").val()}
@RestController
public class AjaxController {
    @RequestMapping("/a1")
    public void a1(String name, HttpServletResponse response) throws IOException {
        System.out.println("a=>"+name);
        if("kuangshen".equals(name)){
            response.getWriter().print("true");
        }else {
            response.getWriter().print("false");
        }
    }
}

小結:根據Ajax的地址,後台接收數據,並返回結果,並沒有進行重定向或者轉發的操作,因此就不會刷新頁面,實現了前端頁面的局部更新。

10.4 Ajax異步加載數據

1. 創建一個User類

注意:使用註解的話,記記得添加 lombok 依賴,並且把jar包添加到 lib 目錄中

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private int age;
    private String sex;
}
2. 創建前端頁面
body>
    <input id="btn" type="button" value="加載數據" />
    <table>
        <tr>
            <td>名稱</td>
            <td>年齡</td>
            <td>性別</td>
        </tr>
        <tbody id="content">

        </tbody>
    </table>
</body>
3. 通過Ajax想後台發起請求
<%-- 引入 jQuery --%>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
<script>
    window.onload=function(){
        $("#btn").click(function () {
            console.log("成功了沒??");
            /*
                簡寫的方式:$.post(url, param[可省略] , success)
             */
            $.post("${pageContext.request.contextPath}/a2",function (data) {
                console.log(data);
                var html = "";
                for (let i = 0; i < data.length; i++) {
                    html += "<tr>" +
                            "<td>" + data[i].name + "</td>" +
                            "<td>" + data[i].age + "</td>" +
                            "<td>" + data[i].sex + "</td>" +
                        "</tr>"
                }
                $("#content").html(html);
            })
        })
    }
</script>
4. 後台實現數據返回到前端
@RequestMapping("/a2")
public List<User> a2(){
    // 創建幾個對象,添加到集合中返回到前端
    List<User> userList = new ArrayList<User>();
    userList.add(new User("前端",1,"女"));
    userList.add(new User("後端",2,"男"));
    userList.add(new User("運維",3,"男"));
    return userList;
}

小結:前端通過 Ajax 先向後端發起請求,後台處理請求,並把結果返回到前端,前端把接收到的數據進行處理,顯示到頁面中。

10.5 Ajax驗證用戶信息體驗

1. 創建前端頁面login.jsp
<body>
<p>
    用戶名:<input type="text" id="userName" onblur="a1()">
    <span id="userId"></span>
</p>
<p>
    密碼:<input type="text" id="passWord" onblur="a2()">
    <span id="passWordId"></span>
</p>
</body>
2. 通過Ajax想後台發起請求
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.0.js"></script>
<script>
    function a1() {
        $.get({
            url:"${pageContext.request.contextPath}/a3",
            data:{"name":$("#userName").val()},
            success:function (data) {
                console.log(data);
                if(data.toString()==='ok'){
                    $("#userId").css("color","green");
                }else{
                    $("#userId").css("color","red");
                }
                $("#userId").html(data);
            }
        })
    }
    function a2() {
        $.get({
            url:"${pageContext.request.contextPath}/a3",
            data:{"password":$("#passWord").val()},
            success:function (data) {
                console.log(data);
                if(data.toString()==='ok'){
                    $("#passWordId").css("color","green");
                }else{
                    $("#passWordId").css("color","red");
                }
                $("#passWordId").html(data);
            }
        })
    }
</script>
3. 後台實現數據返回前端
@RequestMapping("/a3")
public String a3(String name,String password){
    String msg = "";
    if(name!=null){
        if("admin".equals(name)){
            msg = "ok";
        }else{
            msg = "用戶名有誤!";
        }
    }
    if(password!=null){
        if("123456".equals(password)){
            msg = "ok";
        }else{
            msg = "密碼有誤!";
        }
    }
    return msg;
}

11.攔截器

11.1 簡單實用攔截器

1.創建一個控制器
@RestController
public class TestController {
    @GetMapping("/t1")
    public String test1(){
        System.out.println("執行成功了");
        return "ok";
    }
}
  1. 創建一個攔截器
public class MyInterceptor implements HandlerInterceptor {

    //return true ,執行下一個攔截器,放行
    //return false ,不執行下一個攔截器
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("*************處理前*************");
        return false;
    }
    
    //下面兩個方法可寫可不寫,主要用於日誌返回
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("*************處理後*************");
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("*************清理*************");
    }
}
3. 配置攔截器
<!--攔截器配置-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--包括這個請求下面的所有請求-->
        <mvc:mapping path="/**"/>
        <bean class="com.dzj.config.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

11.2 登錄判斷驗證

1. 入口
<body>
  <h1>
    <a href="${pageContext.request.contextPath}/user/goLogin">登錄頁面</a>
    <a href="${pageContext.request.contextPath}/user/main">首頁</a>
  </h1>
</body>
2. 登錄頁
<body>
    <%-- 在web-inf下面所有的頁面或者資源,只能通過controller或者servlet進行訪問--%>
    <h1>登錄頁面</h1>
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        用戶名:<input type="text" name="username">
        密碼:<input type="text" name="password">
        <input type="submit" value="提交">
    </form>
</body>
3. 首頁
<body>
    <h1>首頁</h1>
    <span>${username}</span>
    <p>
        <a href="${pageContext.request.contextPath}/user/goOut">註銷</a>
    </p>
</body>
4. 新建控制器
@Controller
@RequestMapping("/user")
public class LoginController {

    @RequestMapping("/main")
    public String main(){
        return "main";
    }

    @RequestMapping("/goLogin")
    public String goLogin(){
        return "Login";
    }

    @RequestMapping("/login")
    public String login(String username, String password, HttpSession session, Model model){
        session.setAttribute("usernameInfo",username);
        model.addAttribute("username",username);
        return "main";
    }

    @RequestMapping("/goOut")
    public String goOut(HttpSession session){
        //移除節點
        session.removeAttribute("usernameInfo");
        return "main";
    }
}
5. 創建攔截器
public class LoginInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        // 放行,判斷什麼情況下登錄

        //登錄頁面也會放行
        if(request.getRequestURI().contains("goLogin")){
            return true;
        }
        //說明我在提交登錄
        if(request.getRequestURI().contains("login")){
            return true;
        }
		
        //註銷登錄
        if(request.getRequestURI().contains("goOut")){
            return true;
        }

        //第一次登錄也是沒有session的
        if((session.getAttribute("userLoginInfo")!=null)&&(request.getRequestURI().contains("main"))){
            return true;
        }
        //判斷什麼情況下沒有登陸
        request.getRequestDispatcher("/WEB-INF/jsp/Login.jsp").forward(request,response);
        return false;
    }
}
6. 配置攔截器
<mvc:interceptor>
    <!--包括user這個請求下面的所有請求-->
    <mvc:mapping path="/user/**"/>
    <bean class="com.dzj.config.LoginInterceptor"/>
</mvc:interceptor>

12.SpringMVC:文件上傳和下載

12.1 準備工作

12.2 文件上傳

1. 導入文件上傳的jar包

導入文件上傳的jar包,commons-fileupload,Maven會自動幫我們導入他的依賴包commons-io

<!-- 文件上傳-->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.4</version>
</dependency>
<!-- servlet-api導入高版本的 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>
2. 配置bean:multipartResolver

注意:這個 bean 的 id 必須為:multipartResolver,否則上傳文件會報404的錯誤!

<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 請求的編碼格式,必須和jSP的pageEncoding屬性一致,以便正確讀取表單的內容,默認為ISO-8859-1 -->
    <property name="defaultEncoding" value="UTF-8"/>
    <!-- 上傳文件大小上限,單位為位元組(10MB) -->
    <property name="maxUploadSize" value="10485760"/>
    <property name="maxInMemorySize" value="40960"/>
</bean>
3. 編寫前端頁面
<body>
    <form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
      <input type="file" name="file"/>
      <input type="submit" value="upload"/>
    </form>
  <a href="${pageContext.request.contextPath}/statics/1.png">下載圖片</a>
</body>

四、創建後台控制器

@RestController
public class FileController {

    //@RequestParam("file") 將name=file控件得到的文件封裝成ConmmonsMultipartFile對象
    //批量上傳CommonsMultipartFile則為數組即可

    /*
        採用file.Transto  來保存上傳的文件
     */
    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("file")CommonsMultipartFile file, HttpServletRequest request) throws IOException {

        //上傳保存路徑設置
        String path = request.getServletContext().getRealPath("/upload");
        File realPath = new File(path);
        if(!realPath.exists()){
            realPath.mkdir();
        }

        //上傳文件地址
        System.out.println("上傳文件保存地址:"+realPath);
        //通過CommonsMultipartFile的方法直接寫文件(注意這個時候)
        file.transferTo(new File(realPath+"/"+file.getOriginalFilename()));
        return "redirect:/index.jsp";

    }
}

12.3 文件下載

文件下載的步驟:

  • 設置 response 響應頭
  • 讀取文件 — InputStream
  • 寫出文件 — OutputStream
  • 執行操作
  • 關閉流(先開後關)
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response,HttpServletRequest request) throws Exception {
    //要下載的圖片地址
    String path = request.getServletContext().getRealPath("/upload");
    String fileName = "1.png";
    //1.設置response相應頭
    response.reset();//設置頁面不緩存,清空buffer
    response.setCharacterEncoding("UTF-8");//字符編碼
    response.setContentType("multipart/form-data");//二進制傳輸數據
    //設置響應頭
    response.setHeader("Content-Disposition","attachment;fileName="+ URLEncoder.encode(fileName,"UTF-8"));
    File file = new File(path,fileName);
    //2.讀取文件--輸入流
    InputStream input = new FileInputStream(file);
    //3.寫出文件--輸出流
    OutputStream out = response.getOutputStream();

    byte[] buff = new byte[1024];
    int index = 0;
    //4.執行 寫出操作
    while((index = input.read(buff))!=-1){
        out.write(buff,0,index);
        out.flush();
    }
    out.close();
    input.close();
    return null;
}
直接前端這樣也行
<a href="${pageContext.request.contextPath}/statics/1.png">下載圖片</a>