天天看點

Spring MVC資料處理

作者:Iliuhu

類型轉換器

<!--注解驅動,配置通路靜态資源-->
<mvc:annotation-driven conversion-service="conversionService"/>

<!--配置自定義類型轉換器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.demo.converters.MyStringToDateConverter" />
        </set>
    </property>
</bean>           
package com.demo.converters;

import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 定義類型轉換器 需要明确 源類型 和 目标類型
 * 在convert方法自定義類型轉換的實作
 * 在spring配置檔案中配置自定義類型轉換器
 */
public class MyStringToDateConverter implements Converter<String, Date> {

    @Override
    public Date convert(String source) {
        try {
            if (!StringUtils.isEmpty(source)) {
                if (source.split("-").length == 3) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                    return sdf.parse(source);
                } else if(source.split("/").length == 3) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
                    return sdf.parse(source);
                } else {
                    throw new RuntimeException("日期轉換錯誤:" + source);
                }
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}           

資料格式化

  • @NumberFormat ===> 用在javabean的屬性或方法參數上
  • @DateTimeFormat ===> 用在javabean的屬性或方法參數上
<%--spring标簽庫--%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
...

<%--spring:eval 用來顯示格式化後的資料--%>
salary: <spring:eval expression="user.salary"></spring:eval>           
package com.demo.controllers.entity;

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

import java.util.Date;

@Data
public class User {
    private Integer id;
    private String username;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday; // 2023-03-03
    @NumberFormat(style = NumberFormat.Style.CURRENCY)
    private Double balance; // ¥1000
    private String[] hobbies;
    @NumberFormat(pattern = "#,###.##")
    private Double salary; // 1,000.01
    @NumberFormat(style = NumberFormat.Style.PERCENT)
    private Double taskCount; // 96%
}           

資料校驗

Hibernate Validator實作了JSR349驗證注解規範的技術,在javaBean屬性上标注
Spring MVC資料處理
Spring MVC資料處理
  • 基于原生html form表單實作方式
<form action="${pageContext.request.contextPath}/user" method="post">
    id:<input name="id" type="text" /> ${errors.id}<br />
    <input type="submit" value="送出" />
</form>           
package com.demo.controllers.entity;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

@Data
public class User {
    @NotNull
    @Min(1)
    private Integer id;
}           
package com.demo.controllers;

import com.demo.controllers.entity.User;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 在需要驗證的javaBean的屬性上面加入對應的驗證注解
 * 在需要驗證的處理方法的對應javaBean參數上加上@Valid
 * 在需要驗證的處理方法參數中加入BindingResult,代表自己處理錯誤,這樣就不會顯示錯誤頁面了
 * 将錯誤資訊循環通過map存入到request域中
 * 在jsp通過${errors.id}擷取對應的錯誤資訊
 */
@Controller
public class UserController {

    @PostMapping("/user")
    public String add(@Valid User user, BindingResult result, Model model) {

        // 将錯誤資訊取出來,輸出到jsp頁面
        // 判斷目前是否出現了錯誤
        if(result.hasErrors()) {
            // 存放錯誤資訊,有利于在jsp中分别取出錯誤資訊
            Map<String, String> errors = new HashMap<>(); // key: 錯誤資訊的屬性名,value: 錯誤資訊

            // 擷取所有的錯誤資訊 包含 錯誤的屬性,錯誤資訊
            List<FieldError> fieldErrors = result.getFieldErrors();

            for (FieldError fieldError : fieldErrors) {
                errors.put(fieldError.getField(), fieldError.getDefaultMessage());
            }

            model.addAttribute("errors", errors);

            //如果驗證失敗将請求轉發到添加頁面
            return "user/add";
        }
        System.out.println(user);
        return "show";
    }
}           
  • 基于Spring form标簽庫的實作方式(靜态資料)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
    <title>添加使用者</title>
</head>
<body>
    <h1>添加使用者</h1>
		<%-- Spring的form标簽庫method是支援put和delete--%>
    <form:form action="${pageContext.request.contextPath}/spring/user" method="post" modelAttribute="user">
        id: <form:input path="id"></form:input><form:errors path="id"></form:errors>
    </form:form>
</body>
</html>
           
/**
 * 在jsp中導入spring-form标簽庫
 * 在form标簽上一定要加上modelAttribute
 * 加上對應的form标簽,必須都要以<form:開頭
 * @param user
 * @param result
 * @param model
 * @return
 */
@PostMapping("/form/user")
public String springformAdd(@Valid User user, BindingResult result, Model model) {

    if (result.hasErrors()) {
        return "user/add";
    }

    System.out.println(user);

    return "show";
}

/**
 * 添加一個顯示jsp的處理方法,一定要傳入一個空的User到model中
 * @param user
 * @return
 */
@GetMapping("/user/add")
public String addView(User user) {
    return "user/add";
}           
  • 基于Spring form标簽庫的實作方式(動态資料)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
    <title>添加使用者</title>
</head>
<body>
    <h1>添加使用者</h1>

    <form:form action="${pageContext.request.contextPath}/spring/user" method="post" modelAttribute="user">
        hobbies:
        <%--靜态資料源--%>
        <form:checkbox path="hobbies" value="唱歌"></form:checkbox>
        <form:label path="hobbies">唱歌</form:label>
        <form:checkbox path="hobbies" value="跳舞"></form:checkbox>
        <form:label path="hobbies">跳舞</form:label> <br>

        <%--動态資料源--%>
        <form:checkboxes path="hobbies" items="${list}"></form:checkboxes>

        <select>
            <option value="1">北京</option>
            <option value="2">上海</option>
        </select>
    </form:form>
</body>
</html>           
/**
     * 初始化頁面的資料
     *    調用其它的方法之前,都要調用此方法
     */
    @ModelAttribute
    public void initData(Model model) {
        // 初始化資料
        List<String> list = Arrays.asList("唱歌", "跳舞");
        model.addAttribute("list", list);

//        Map<String, String> map = new HashMap<>();
//        map.put("1", "唱歌");
//        map.put("2", "跳舞");
//
//        model.addAttribute("list", map);
    }

    /**
     * 在jsp中導入spring-form标簽庫
     * 在form标簽上一定要加上modelAttribute
     * 加上對應的form标簽,必須都要以<form:開頭
     * @param user
     * @param result
     * @return
     */
    @PostMapping("/form/user")
    public String springformAdd(@Valid User user, BindingResult result) {

        if (result.hasErrors()) {
            return "user/add";
        }

        System.out.println(user);

        return "show";
    }

    /**
     * 添加一個顯示jsp的處理方法,一定要傳入一個空的User到model中
     * @param user
     * @return
     */
    @GetMapping("/user/add")
    public String addView(User user) {

        return "user/add";
    }           

JSON處理

java轉換為json的過程:序列化

json轉換為java的過程:反序列化

json要用雙引号,不能用單引号

JAVA JSON
String "string"
Integer 111
JavaBean/Map {"id":1, "name":"zhangsan"}

數組/集合

String[]/List<String>

["a","b","c"]

List<User>

List<Map>

[

{"id":1, "name":"zhangsan"},

{"id":2, "name":"wangwu"},

...

]

User:

屬性:id,name, Role role

{"id":1, "name":"zhangsan","role":{"id":2, "name":"wangwu"}}

User:

屬性:id,name,List<Role>

{"id":1, "name":"zhangsan","role":[{"id":2, "name":"wangwu"},

{"id":3, "name":"lisi"}

,...]

}

  • RestController
控制器類中所有的處理方法都會以json的資料進行響應,相當于控制器類中所有的處理方法都加上了@ResponseBody
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.14.2</version>
</dependency>

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

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.14.2</version>
</dependency>           
package com.demo.controllers.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String usernae;
    @JsonIgnore  // 當傳回javaBean的json時,會忽略該屬性
    private String password;
    @JsonFormat(pattern = "yyyy-MM-dd")  // 使用者轉換json時格式化資料
    private Date birthday;
}           
package com.demo.controllers;

import com.demo.controllers.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@Controller
@RestController
public class JsonController {

    @RequestMapping("/json/response")
//    @ResponseBody // 将傳回值作為文本進行傳回,并不是傳回視圖
    public User responseJson() {
        User user = new User(1, "zhangsan", "123456", new Date());

        return user;
    }
}           
  • Spring MVC擷取JSON資料
// 對象轉化為json字元串
var jsonValue = JSON.stringify(user);           
  • @RequestBody: 請求中JSON資料轉化成JAVA
  • @ResponseBody: 響應中把JAVA轉化成JSON資料

Spring MVC上傳|下載下傳

  • Servlet 下載下傳
package com.demo.controllers;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

/**
 * Servlet下實作的檔案下載下傳
 */
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String realPath = this.getServletContext().getRealPath("/file/1.png"); // 擷取要下載下傳的檔案的絕對路徑
        // 将檔案路徑封裝成File對象
        File tmpFile = new File(realPath);
        // 擷取檔案名
        String fileName = tmpFile.getName();
        // 設定響應頭 content-disposition: 設定檔案下載下傳的打開方式,預設在網頁上打開
        // attachment;filename是以下載下傳方式來打開檔案
        resp.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        InputStream is = new FileInputStream(realPath); // 擷取檔案輸入流

        int len = 0;
        byte[] buffer = new byte[1024];
        OutputStream out = resp.getOutputStream();
        while((len = is.read(buffer)) != -1) {
           out.write(buffer, 0, len); // 将緩沖區的資料輸出到用戶端浏覽器
        }

        is.close();
    }
}           
  • Spring MVC 下載下傳
package com.demo.controllers;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

@Controller
public class DownloadController {

    @RequestMapping("/download1")
    public String download1(HttpServletRequest request, HttpServletResponse response) throws Exception{
        String realPath = request.getServletContext().getRealPath("F:\\code\\springmv01\\web\\images\\img.png");
        File tmpFile = new File(realPath);
        String fileName = tmpFile.getName();

        response.setHeader("content-disposition", "attachment; filename = " + URLEncoder.encode(fileName, "UTF-8"));
        InputStream is = new FileInputStream(realPath);

        int len = 0;
        byte[] buffer = new byte[1024];
        OutputStream out = response.getOutputStream();
        while((len = is.read(buffer)) != -1) {
            out.write(buffer, 0, len); // 将緩沖區的資料輸出到用戶端浏覽器
        }

        is.close();

        return null;
    }

    /**
     * 基于Spring ResponseEntity的檔案下載下傳 不支援緩沖區
     *      ResponseEntity 可以定制檔案的響應内容,響應頭,響應狀态碼
     * @return
     */
    @RequestMapping("/getDownload")
    public ResponseEntity<String> getDownload() {
        String body = "hello world";

        HttpHeaders headers = new HttpHeaders();
        headers.set("Set-Cookie","name=hello");

        return new ResponseEntity<>(body, headers, HttpStatus.OK);
    }

    @RequestMapping("/download2")
    public ResponseEntity<Byte[]> download2(HttpServletRequest request, HttpServletResponse response) throws Exception{
        String realPath = request.getServletContext().getRealPath("F:\\code\\springmv01\\web\\images\\img.png");
        File tmpFile = new File(realPath);
        String fileName = tmpFile.getName();

        HttpHeaders headers = new HttpHeaders();
        headers.set("content-disposition", "attachment; filename = " + URLEncoder.encode(fileName, "UTF-8"));
        InputStream is = new FileInputStream(realPath);

        return new ResponseEntity<Byte[]>(new Byte[is.available()], headers, HttpStatus.OK);
    }
}           
  • Spring MVC 上傳
<!--檔案上傳解析器-->
<bean id= "multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8" />
    <!--檔案上傳最大的位元組數-->
    <property name="maxUploadSize" value="#{1024*1024*10}" />
</bean>           
<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/upload" method="post">
  檔案描述:<input type="text" name="desc" /> <br>
  檔案:<input type="file" name="myfile" multiple accept="image/*"/> <br> <%--multiple: 多檔案上傳--%>
  <input type="submit" value="送出" />
</form>           
/**
 * 多檔案檔案上傳
 */
@PostMapping("/upload")
public String upload(String desc, MultipartFile[] multipartFiles) throws IOException {

    for (MultipartFile myfile : multipartFiles) {
        String path = "D:/" + myfile.getOriginalFilename();
        File file = new File(path);

        myfile.transferTo(file);
    }

    return "success";
}           
/**
 * 多線程檔案上傳
 */
@PostMapping("/upload")
public String upload(String desc, MultipartFile[] multipartFiles) throws InterruptedException {

    for (MultipartFile myfile : multipartFiles) {
        Thread thread = new Thread(() -> {
            String path = "D:/" + myfile.getOriginalFilename();
            File file = new File(path);
            try {
                myfile.transferTo(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        thread.start();
        thread.join(); // 讓子線程執行完在執行主線程
    }

    return "success";
}            
  • Spring MVC虛拟目錄上傳
Spring MVC資料處理
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%--磁盤路徑--%>
    <img src="${pageContext.request.contextPath}/img/${filename}">
</body>
</html>           
/**
 * 檔案上傳到三個路徑
 *     1. 項目路徑(适合項目小,上傳使用率低
 *     2. 磁盤路徑,這種方式通過虛拟目錄的映射
 *     3. 靜态資源伺服器(CDN)
 */
@PostMapping("/upload")
public String upload(String desc, MultipartFile myfile, Model model) throws IOException {


    String path = "D:/" + myfile.getOriginalFilename();
    File file = new File(path);

    myfile.transferTo(file);

    model.addAttribute("filename", myfile.getOriginalFilename());

    return "success";
}           

Spring MVC攔截器

攔截器:采用AOP的設計思想,類似Servlet過濾器,用來攔截處理方法

如:權限驗證、日志、異常記錄、記錄方法執行時間

<mvc:interceptors>
    <bean class="com.demo.interceptors.MyInterceptor" />
</mvc:interceptors>           
package com.demo.interceptors;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;

public class MyInterceptor implements HandlerInterceptor {

    /**
     * 在處理方法之前執行
     * @param request   在方法請求進來之前更改request中的的屬性值
     * @param response
     * @param handler   封裝了目前處理方法的資訊
     * @return true: 後續調用鍊是否執行  false:中斷後續執行
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handler1 = (HandlerMethod) handler;
        }

        System.out.println("-------類:["+ handler1.getBean().getClass() +"] 方法:["+ handler1.getMethod().getName() +"] 參數:["+ Arrays.toString(handler1.getMethod().getParameters()) +"] 前執行---------preHandle--------------------------------");
        return true;
    }

    /**
     * 在請求執行後執行,在視圖渲染之前執行
     * 當處理方法出現了異常則不會執行方法
     * @param request
     * @param response 在方法執行後更改response中的資訊
     * @param handler  封裝了目前處理方法的資訊
     * @param modelAndView  封裝了model和view,請求結束後可以修改model中的資料或新增model資料,也可修改view的跳轉
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("------方法後執行,在視圖渲染之前----------postHandle--------------------------------");
    }

    /**
     * 在視圖渲染之後執行,出現異常也會執行該方法
     * @param request
     * @param response
     * @param handler
     * @param ex 可以記錄異常日志的功能,或者清除資源
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("-----------在視圖渲染之後--------afterHandle----------");
    }
}           
  • 攔截器|過濾器的差別
Spring MVC資料處理
Spring MVC資料處理
Spring MVC資料處理
<!--必須保證對應的請求沒有映射處理-->
<mvc:view-controller path="/admin" view-name="admin"></mvc:view-controller>

<mvc:interceptors>

    <!--直接配置bean會攔截Spring MVC所有請求-->
    <bean class="com.demo.interceptors.MyInterceptor" />

    <!--不是所有的請求都要攔截-->
    <mvc:interceptor>
        <!--需要攔截請求-->
        <mvc:mapping path="/**"/>
        <!--不需要攔截的請求-->
        <mvc:exclude-mapping path="/login"/>
        <!--攔截器-->
        <bean class="com.demo.interceptors.CheckLoginInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>           
package com.demo.interceptors;

import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

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

/**
 * 驗證使用者是否登入的攔截器
 */
public class CheckLoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        HttpSession session = request.getSession();

        // 如果沒有登入
        if (StringUtils.isEmpty(session.getAttribute("username"))) {
            response.sendRedirect(request.getContextPath() + "/login");
            return false;
        } else {
            return true;
        }

    }
}           

Spring MVC 國際化

  • 通過浏覽器語言設定國際化
<!--設定國際化支援 配置國際化屬性資源檔案-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename">
        <array>
            <value>i18n/login</value>
            <value>i18n/index</value>
        </array>
    </property>
</bean>           
package com.demo.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 基于浏覽器設定的語言切換國際化
 *      1. 建立jsp對應的國際化屬性資源文
 *          login_en_US.properties
 *          login_zh_CN.properties
 *      2. 配置sping_mvc.xml 将國際化支援和資源檔案都注入到springmvc中
 *      3. 在jsp頁面調用對應的屬性資源内容:<spring:message code="綁定屬性資源檔案中對應的key"
 */
@Controller
public class I8NController {

    @RequestMapping("/i18n")
    public String i18n() {
        return "login";
    }
}           
  • 通過超連結來切換國際化
Spring MVC資料處理

》》》方式一

<!--使用SessionLocaleResolver保持Locale的狀态,會在session中擷取Locale對象-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>           
package com.demo.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

/**
 * 基于浏覽器設定的語言切換國際化
 *      1. 建立jsp對應的國際化屬性資源文
 *          login_en_US.properties
 *          login_zh_CN.properties
 *      2. 配置sping_mvc.xml 将國際化支援和資源檔案都注入到springmvc中
 *      3. 在jsp頁面調用對應的屬性資源内容:<spring:message code="綁定屬性資源檔案中對應的key"
 */
@Controller
public class I8NController {

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

    @RequestMapping("/i18n/{language}_{country}")
    public String changeLocale(@PathVariable("language") String language,
                               @PathVariable("country") String country,
                               HttpServletRequest request, HttpServletResponse response,
                               @Autowired SessionLocaleResolver localeResolver) {
        Locale local = new Locale(language, country);
        localeResolver.setLocale(request, response, local);

        return "login";
    }
}           

》》》方式二

<!--使用SessionLocaleResolver保持Locale的狀态,會在session中擷取Locale對象-->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>

<!--使用springmvc提供的攔截器,接收local參數,設定到session中去-->
<mvc:interceptors>
    <!--所有的請求都會被攔截-->
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>           
  • Spring MVC基于注解的國際化

Spring MVC 異常處理

  • @ExceptionHandler
通過@ExceptionHandler可以在方法中記錄日志
  • @ControllerAdvice是對Controller的增強

全局異常處理

全局資料綁定

全局資料預處理

》》》優先級:處理器異常 > 全局異常中具體異常 > 全局異常

Spring MVC資料處理

繼續閱讀