【SpringMVC】

響應數據和結果視圖

返回值的分類

處理器返回String對象

轉發到字符串指定的URL
處理器方法返回字符串可以指定邏輯視圖名,通過視圖解析器解析爲物理視圖地址.

在本例中,因爲我們在Spring容器配置文件bean.xml中配置的視圖解析器中注入prefix和suffix屬性,所以視圖解析器會把處理器返回的"字符串值"解析爲"/WEB-INF/pages/字符串值.jsp",再請求對應視圖.這是一個請求轉發過程,瀏覽器地址欄不會發生變化.

bean.xml中配置的視圖解析器如下:

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>

處理器方法如下:

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

@RequestMapping("/testString")
public String testString(Model model) {
	// 執行方法體...向隱式對象添加屬性attribute_user,可以在jsp中通過 ${attribute_user} 獲取到
	model.addAttribute("attribute_user", new User("張三", "123"));
    
    // 經過視圖解析器的處理,SpringMVC會將請求轉發到/WEB-INF/pages/succeess.jsp,但瀏覽器地址欄顯示的一直是 項目域名/user/testString
    return "success";
}

}

處理器返回void: 轉發到當前URL

若處理器返回void,表示執行完處理器方法體內代碼後,不進行請求轉發,而直接轉發到當前URL.若沒有在web.xml中配置當前對應的url-pattern,則會返回404錯誤.

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

    @RequestMapping("/testVoid")
    public void testVoid(Model model) {
		// 執行方法體...向隱式對象添加屬性attribute_user,可以在jsp中通過 ${attribute_user} 獲取到
		model.addAttribute("attribute_user", new User("張三", "123"));
        
        // 處理器沒有返回值,則會將請求轉發到當前 項目域名/user/testVoid 路徑
        // 若在web.xml中沒有配置 項目域名/user/testVoid 對應的url-pattern,則會返回404錯誤
        return;
    }
}

可以在返回語句之前執行請求轉發,重定向或getWriter方法指定視圖,示例如下:

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

	@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 執行方法體...向隱式對象添加屬性attribute_user,可以在jsp中通過 ${attribute_user} 獲取到
		model.addAttribute("attribute_user", new User("張三", "123"));
        
        // 通過下面三個方法之一,可以指定訪問的視圖
        // 指定視圖的方式1: 請求轉發
        request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);

        // 指定視圖的方式2: 重定向
        response.sendRedirect(request.getContextPath() + "/index.jsp");

        // 指定視圖的方式3: 通過Writer對象寫入內容
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().print("你好");

        return;
    }
}

處理器返回ModelAndView對象: 更靈活地添加屬性和指定返回視圖

ModelAndView爲我們提供了一種更靈活地爲頁面添加屬性和指定返回視圖的方法,其主要方法如下:

public ModelMap getModelMap(): 返回當前頁面的ModelMap對象.
public ModelAndView addObject(Object attributeValue): 向當前頁面的ModelMap對象中添加屬性
public void setViewName(@Nullable String viewName): 指定返回視圖,viewName會先被視圖解析器處理解析成對應視圖.

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

	@RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView() {
        
        // 創建ModelAndView對象
        ModelAndView mv = new ModelAndView();
        
        // 向model中存入屬性attribute_user
		mv.addObject("attribute_user", new User("張三", "123"));

        // 指定返回視圖,視圖解析器將"success"解析爲視圖URL /WEB-INF/pages/succeess.jsp
        mv.setViewName("success");

        return mv;
    }
}

SpringMVC框架提供的請求轉發和重定向

使用SpringMVC框架提供的請求轉發

要使用SpringMVC框架提供的請求轉發,只需要在處理器方法返回的viewName字符串首加上forward:即可,要注意的是,此時forward:後的地址不能直接被視圖解析器解析,因此要寫完整的相對路徑.示例如下:

@Controller
@RequestMapping("/user")
public class UserController {
	@RequestMapping("/testForward")
    public String testForward() {
        // 在forward:要寫完整的相對路徑
        // return "forward:success"	// 錯誤,會將請求轉發到 /項目名/user/success
        return "forward:/WEB-INF/pages/success.jsp";
    }
}

使用SpringMVC框架提供的重定向

要使用SpringMVC框架提供的請求重定向,只需要在處理器方法返回的viewName字符串首加上redirect:即可,要注意的是,此時redirect:後的地址要寫相對於ContextPath的地址.示例如下:

@Controller
@RequestMapping("/user")
public class UserController {
	@RequestMapping("/testRedirct")
    public String testRedirct() {
        // 在forward:要寫完整的相對路徑
        // return "redirect:" + request.getContextPath() + "/index.jsp";	// 錯誤,會將請求轉發到 /項目名/項目名/index.jsp
		return "redirect:/index.jsp";
    }
}

SpringMVC響應json數據

前期準備

  1. jsp在頁面上引入jQuery以發送json數據,因此需要向服務器發起一個對jQuery的請求.像這種對靜態資源的請求,不應當經過具體的某個處理器處理,而應當直接返回對應的靜態資源.
    因此我們需要在Spring容器配置bean.xml中使用mvc:resources標籤聲明該資源爲靜態資源,否則請求該資源會報404錯誤.該標籤的屬性如下:
    1.location屬性: 表示該資源在服務器上所在的位置,必須是一個有效的目錄
    2.mapping屬性: 指定匹配的URL

我們在bean.xml中配置各靜態文件的位置如下:

<!-- 配置靜態文件的路徑於對應的URL -->
<!-- location屬性必須是一個有效的目錄,因此必須以 / 結尾 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/js/" mapping="/js/**"/>

2.要將json字符串與JavaBean對象相互轉換,我們需要引用jackson的jar包,在pom.xml中添加依賴座標如下:

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

在jsp中編寫代碼發送json數據

在jsp頁面中編寫代碼發送json請求如下:

<script>
// 頁面加載,綁定單擊事件
$(function () {
    $("#btn").click(function () {
        // 發送ajax請求
        $.ajax({
            // 配置請求參數
            url: "user/testAjax",
            contentType: "application/json;charset=UTF-8",
            dataType: "json",
            type: "post",
            // 請求的json數據
            data: '{"username":"myname","password":"mypassowrd","age":30}',
            // 回調函數,處理服務器返回的數據returnData
            success: function (returnData) {
                // 我們假定服務器返回的是一個user對象,將其輸出在控制檯上
                console.log(returnData);            }
        });
    });
});
</script>

在控制器中編寫代碼響應json數據

使用@RequestBody註解將請求體綁定到控制器方法參數上,使用@ResponseBody註解表示將該方法的返回值直接寫回到HTTP響應中,而不是存入Model或解析爲視圖名.

我們引入的jackson包自動完成從Java實體類到json數據之間的相互轉換.

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/testAjax")
    @ResponseBody
    public User testAjax(@RequestBody User user) {

        System.out.println(user);
        
        // 將user對象返回給前端頁面
        return user;
    }
}

參考鏈接:參考博客鏈接

文件上傳

前提條件

  1. 表單的enctype屬性取值必須是multipart/form-data(默認值是application/x-www-form-urlencoded),表示表單內容是分塊的。這時request對象的getParameter()方法將失效。
  2. 表單的method屬性取值必須是post,因爲get請求長度有限制。
  3. 提供一個標籤,用來選擇上傳文件.
<form action="/fileUpload/uploadHandler" method="post" enctype="multipart/form-data">
    param1<input type="text" name="param1"/><br/>
    param2<input type="text" name="param2"/><br/>
    選擇文件<input type="file" name="fileParam"/><br/>
    <input type="submit" value="上傳文件"/>
</form>

  1. 引用文件上傳的相關jar包
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>

文件表單內容

因爲我們設置了enctype屬性取值爲multipart/form-data,因此在請求參數頭中會有一項Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryOMtUEa1sSZ3ayCfC,表示當前表單內容數據是分塊的,每兩塊之間以----WebKitFormBoundaryOMtUEa1sSZ3ayCfC分界.
服務器通過遍歷每一塊數據,找到文件所在的數據塊並執行保存.

文件上傳的三種實現

使用JavaEE進行文件上傳

@Controller
@RequestMapping("/user")
public class UserController {
    //文件上傳,傳統方式
    @RequestMapping("/fileupload1")
    public String fileupload1(HttpServletRequest request) throws Exception {
        System.out.println("文件上傳");
        //使用fileupload組件完成文件上傳
        //上傳的位置
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        //判斷路徑是否存在
        File file = new File(path);
        if (!file.exists()) {
            //創建該文件夾
            file.mkdir();
        }
        //解析request對象,獲取上傳文件項
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
        //解析request
        List<FileItem> items = upload.parseRequest(request);
        // 遍歷解析的結果,尋找上傳文件項
        for (FileItem item : items) {
            // 進行判斷,當前item對象是否是上傳文件項
            if (!item.isFormField()) {//不是普通表單項
                // 服務器中保存的文件名
                String filename = UUID.randomUUID().toString().replace("-", "") + "_" + item.getName();
                // 上傳文件
                item.write(new File(path, filename));
                System.out.println(path);
                // 刪除臨時文件
                item.delete();
            }
        }
        return "success";
    }
}

使用SpringMVC進行單服務器文件上傳

可以使用SpringMVC提供的文件解析器實現文件上傳,在Spring容器中注入文件解析器CommonsMultipartResolver對象如下:

<!-- 配置文件解析器,其id是固定的,必須爲multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 設置文件的最大尺寸 -->
    <property name="maxUploadSize" value="10485760"/>
</bean>

只要在處理器方法的參數列表中定義一個與表單文件項同名的MultipartFile參數,就可以將上傳的文件綁定到該MultipartFile對象上,調用其transferTo(File file)方法即可保存文件.

@Controller
@RequestMapping("/fileUpload")
public class FileUploadController {

	@RequestMapping("/springMVC")
    public String fileupload2(HttpServletRequest request, @RequestParam("fileParam") MultipartFile upload) throws Exception {
        
        // 創建目錄保存上傳的文件
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }

        // 服務器中保存的文件名
        String filename = UUID.randomUUID().toString().replace("-", "") + "_" + upload.getOriginalFilename();
        // 上傳文件
        upload.transferTo(new File(path,filename));

        return "success";
    }
}

使用SpringMVC進行跨服務器文件上傳

我們可以引入jersey庫進行服務器間通信,實現將文件上傳到一個專用的文件服務器,需要在pom.xml中引入jersey庫的座標如下:

<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-core</artifactId>
    <version>1.18.1</version>
</dependency>
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-client</artifactId>
    <version>1.18.1</version>
</dependency>

在處理器方法中創建Client對象實現服務器間通信,將文件上傳到文件服務器上,代碼如下:

@Controller
@RequestMapping("/fileUpload")
public class FileUploadController {

	@RequestMapping("/betweenServer")
    public String fileupload3(@RequestParam("fileParam") MultipartFile upload) throws Exception {
        System.out.println("跨服務器文件上傳...");

        // 文件服務器URL
        String fileServerPath = "http://localhost:9090/uploads/";	
        
        // 獲取服務器中保存的文件名
        String filename = UUID.randomUUID().toString().replace("-", "") + "_" + upload.getOriginalFilename();

        // 創建客戶端對象並在文件服務器上創建資源
        Client client = Client.create();
        WebResource webResource = client.resource(fileServerPath + filename);
        webResource.put(upload.getBytes());

        return "success";
    }
}

編寫處理文件的工具類

我們將上述程序中對文件的處理封裝成抽象類FileUtil:

public class FileUtil {

    // 上傳文件
    public static void uploadFile(byte[] file, String filePath, String fileName) throws Exception {
        File targetFile = new File(filePath);
        if (!targetFile.exists()) {
            targetFile.mkdirs();
        }
        FileOutputStream out = new FileOutputStream(filePath + fileName);
        out.write(file);
        out.flush();
        out.close();
    }

    // 刪除文件,返回值表示是否刪除成功
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        // 如果文件路徑所對應的文件存在,並且是一個文件,則直接刪除
        if (file.exists() && file.isFile()) {
            if (file.delete()) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    // 重命名文件
    public static String renameToUUID(String fileName) {
        return UUID.randomUUID() + "." + fileName.substring(fileName.lastIndexOf(".") + 1);
    }
}

保存文件的操作可簡化爲如下三句:

String fileName = FileUtil.renameToUUID(uploadFile.getOriginalFilename());
String filePath = request.getSession().getServletContext().getRealPath("/uploads/");
FileUtil.uploadFile(uploadFile.getBytes(), filePath, fileName);

SpringMVC中的攔截器

Spring MVC 的處理器攔截器類似於Servlet開發中的過濾器Filter,用於對處理器進行預處理和後處理。
攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,攔截器鏈中的攔截器就會按其之前定義的順序被調用。
它和過濾器是有幾分相似,但是也有區別:

  • 過濾器是servlet規範中的一部分,任何java web工程都可以使用。 攔截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用。
  • 過濾器在url-pattern中配置了/*之後,可以對所有要訪問的資源攔截。 攔截器它是隻會攔截訪問的控制器方法,如果訪問的是jsp,html,css,image或者js是不會進行攔截的。
  • 它也是AOP思想的具體應用。
  • 要想自定義攔截器, 要求必須實現:HandlerInterceptor接口。
  • 過濾器 > 攔截器

第一步:編寫一個普通類實現HandlerInterceptor接口

重寫接口中的preHandle方法,該方法返回一個布爾類型:
返回true表示放行,執行下一個攔截器,如果沒有,就執行controller中的方法。返回false表示攔截,不載進行執行,而是進行轉發或者重定向。

public class MyInterceptor1 implements HandlerInterceptor {
    //預處理,controller方法執行前
    //返回true表示放行,執行下一個攔截器,如果沒有,就執行controller中的方法。
    // 返回false表示攔截
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor1執行了。。");
        return true;
    }
}

第二步:配置攔截器

    <!--配置攔截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--要攔截的具體方法-->
            <mvc:mapping path="/user/*"/>
            <!--不要攔截的具體方法,和不要攔截的方法二選一-->
            <!--<mvc:exclude-mapping path=""></mvc:exclude-mapping>-->
            <!--配置攔截器對象-->
            <bean class="com.lwl.MyInterceptor1"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

jsp頁面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>攔截器</title>
</head>

<body>
    <h3>攔截器</h3>
    <a href="user/testInterceptor">攔截</a>
</body>
</html>

測試運行結果:

在點擊index中的超鏈接訪問user/testInterceptor的方法時,會首先被攔截器攔截,打印MyInterceptor1執行了。。,因爲返回的是true,因此放行執行testInterceptor方法,最後訪問success頁面。
在這裏插入圖片描述

攔截器中方法的說明

  • preHandle:
    • 只要配置了都會調用(方法執行前調用)
    • 如果程序員決定該攔截器對請求進行攔截處理後還要調用其他的攔截器,或者是業務處理器去進行處理,則返回true。
    • 如果程序員決定不需要再調用其他的組件去處理請求,則返回false。
  • postHandle
    • 在攔截器鏈內所有攔截器返成功調用
    • 在業務處理器處理完請求後,但是DispatcherServlet向客戶端返回響應前被調用。
    • 在該方法中對用戶請求request進行處理。
  • afterCompletion
    • 只有preHandle返回true才調用
    • 在 DispatcherServlet 完全處理完請求後被調用,可以在該方法中進行一些資源清理的操作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章