天天看點

Spring初步學習——Spring MVCMVC 設計概述

MVC 設計概述

在早期Java Web的開發中,統一把顯示層,控制層,資料層全部交給JSP或者JavaBean來進行處理,就像下圖所示:

Spring初步學習——Spring MVCMVC 設計概述

【弊端】:

  • JSP和Java Bean之間嚴重耦合,java代碼和HTML代碼也耦合在了一起
  • 要求開發者不僅要掌握java,還要有高水準的前端技術
  • 前段和後端互相依賴,前段需要等待後端完成,後端也要依賴前端完成,才能進行有效的測試
  • 代碼難以複用

正是因為上面的弊端,是以很快就被Servlet+JSP+Java Bean所取代,早期的MVC模型如下:

Spring初步學習——Spring MVCMVC 設計概述

首先使用者的請求會到達Servlet,然後根據請求調用相應的Java Bean,并把顯示結果交給JSP去完成,這樣的模式我們稱為MVC模式:

M 代表 模型(Model)

模型是什麼呢? 模型就是資料,就是 dao,bean

V 代表 視圖(View)

視圖是什麼呢? 就是網頁, JSP,用來展示模型中的資料

C 代表 控制器(controller)

控制器是什麼? 控制器的作用就是把不同的資料(Model),顯示在不同的視圖(View)上,Servlet 扮演的就是這樣的角色

Spring MVC架構

使用XML配置Spring MVC

為解決持久層一直未處理好的資料庫事務的程式設計,又為了迎合NoSQL的強勢崛起,Spring MVC給出了方案

Spring初步學習——Spring MVCMVC 設計概述

傳統的業務層被拆分為業務層(Service)和資料通路層(Dao),在Service下可以通過Spring的聲明式事務操作資料通路層,而在業務上還允許我們通路NoSQL,這樣就能夠滿足異軍突起的NoSQL的使用,它可以大大提高網際網路系統的性能

特點:

  • 結構松散,幾乎可以在Spring MVC中使用各類視圖
  • 松耦合,各子產品分離
  • 與Spring無縫內建

接下來我們再來實戰一下:

1、在IDEA中建立Spring MVC項目,取名為【HelloSpringMVC】,IDEA會自動為我們下載下傳必要的jar包,并且為我們建立好一些預設的目錄和檔案

2、修改web.xml檔案,如下所示:

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
<!--表示攔截所有的請求,交給Spring MVC的背景控制器來處理-->
</servlet-mapping>
           

3、編輯dispatcher-servlet.xml,這是Spring MVC的映射配置檔案,我們編輯如下:

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

    <bean id="simpleUrlHandlerMapping"
          class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <!-- /hello 路徑的請求交給 id 為 helloController 的控制器處理-->
                <prop key="/hello">helloController</prop>
            </props>
        </property>
    </bean>
    <bean id="helloController" class="controller.HelloController"></bean>
</beans>
           

4、編寫HelloController,在Package【controller】下建立【HelloController】類,并實作

org.springframework.web.servlet.mvc.Controller接口:

package controller;

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

public class HelloController implements Controller{
    @Override
    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        return null;
    }
}
           

Note:這個時候會出現報錯問題,提示javax.servlet包找不到,将Tomcat目錄下【lib】lib檔案夾下的servlet-api.jar包拷貝到工程【lib】檔案夾下,添加依賴Spring MVC通過ModelAndView對象把模型和視圖結合在一起

package controller;

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

public class HelloController implements Controller {

    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mav = new ModelAndView("index.jsp");
        mav.addObject("message", "Hello Spring MVC");
        return mav;
    }
}
           

5、準備index.jsp,将index.jsp的内容修改為

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isELIgnored="false"%>
 
<h1>${message}</h1><!--用EL表達式顯示message的内容-->
           

跟蹤Spring MVC的請求:

每當使用者在Web浏覽器中點選連結或者送出表單的時候,請求就開始工作了,想郵差一樣,從離開浏覽器開始到響應傳回,它會經曆很多站點,在每一個站點都會留下一些資訊同時也會帶上一些其他的資訊,下圖為Spring MVC的請求流程:

1》第一站:DispatcherServlet

從請求離開浏覽器以後,第一站到達的就是DispatcherServlet,看名字就知道是一個Servlet,通過J2EE的學習,我們知道Servlet可以攔截并處理HTTP請求,DispatcherServlet會攔截所有請求,并且将這些請求發送給Spring MVC控制器

DispatcherServlet的任務就是攔截請求發送給Spring MVC控制器

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <!-- 攔截所有的請求 -->
    <url-pattern>/</url-pattern>
</servlet-mapping>
           

2》第二站:處理器映射(HandlerMapping)

問題:典型的應用程式中可能會有多個控制器,這些請求到底應該發給哪一個控制器呢?

是以DispatcherServlet會查詢一個或多個處理器映射來确定請求的下一站在哪裡,處理器映射會根據請求攜帶的URL資訊來進行決策,例如上面的例子中我們通過simpleUrlHandlerMapping來将/hello位址交給helloController處理

3》控制器

一旦選擇合适的控制器,DispatcherServlet會将請求發送給選中的控制器,到了控制器,請求會卸下負載(使用者送出的請求)等待控制器處理這些資訊:

4》傳回DispatcherServlet

當控制器在完成邏輯處理後,通常會産生一些資訊,這些資訊就是需要傳回給使用者并在浏覽器上顯示的資訊,他們被稱為模型(Model)。僅僅傳回原始的資訊是不夠的——這些資訊需要以使用者友好的方式進行格式化,一般會是HTML,是以,資訊需要發送給一個視圖(view),通常是JSP

控制器所做的最後一件事就是将模型打包,并且表示出用于渲染輸出的視圖名(邏輯視圖名)。他接下來會将請求連同模型和視圖名發送回DispatcherServlet

5》視圖解析器

這樣的話,控制器就不會和特定的視圖相耦合,傳遞給DispatcherServlet的視圖名并不直接表示某個特定的JSP,他僅僅傳遞是一個邏輯名稱,這個名稱将會用來查找産生結果的真正視圖

DispatcherServlet将會使用視圖解析器(view resolver)來将邏輯視圖名比對為一個特定的視圖實作,它可能也不是JSP

6》視圖

既然DispatcherServlet已經知道由哪個視圖渲染結果了,那請求的任務基本上也就完成了

它的最後一站是視圖的實作,在這裡他傳遞模型資料,請求的任務也就完成了,視圖使用模型資料渲染出結果,這個結果會通過響應對象傳遞給用戶端

使用注解配置Spring MVC

上面的例子我們采用了XML配置的方式,接下來我們看看基于注解應該怎麼完成上述程式的配置

1、為HelloController添加注解

package controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController{

    @RequestMapping("/hello")
    public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
        ModelAndView mav = new ModelAndView("index.jsp");
        mav.addObject("message", "Hello Spring MVC");
        return mav;
    }
}
           

@Controller注解:是用來聲明控制器的

@RequestMapping:表示路徑/hello會映射到該方法上

2、取消之前的XML注解

在dispatcher-servlet.xml檔案中,注釋掉之前的配置,然後增加一句元件掃描

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

    <!--<bean id="simpleUrlHandlerMapping"-->
                                        <!--class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">-->
    <!--<property name="mappings">-->
            <!--<props>-->
                <!--&lt;!&ndash; /hello 路徑的請求交給 id 為 helloController 的控制器處理&ndash;&gt;-->
                <!--<prop key="/hello">helloController</prop>-->
            <!--</props>-->
        <!--</property>-->
    <!--</bean>-->
    <!--<bean id="helloController" class="controller.HelloController"></bean>-->

    <!-- 掃描controller下的元件 -->
    <context:component-scan base-package="controller"/>
</beans>
           

3、重新開機伺服器,運作

@RequestMapping注解

作用在類上:相當于給該類所有配置的映射位址前加了一個位址

@Controller
@RequestMapping("/wmyskxz")
public class HelloController {
    @RequestMapping("/hello")
    public ModelAndView handleRequest(....) throws Exception {
        ....
    }
}
           

則,通路位址變為:localhost/wmyskxz/hello

配置視圖解析器:

視圖解析器負責定位視圖,他接受一個由DispatcherServlet傳遞的邏輯視圖名來比對一個特定的視圖

需求:有些視圖我們不希望使用者直接通路到,例如有重要資料的頁面,有模型資料支撐的頁面

解決方案:我們将JSP檔案配置在【WEB-INF】檔案夾中的【page】檔案夾下,【WEB-INF】是java Web中預設的安全目錄,是不允許使用者直接通路的

但是我們需要将這告訴視圖解析器,我們在dispatcher-servlet.xml檔案中做如下配置:

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

這裡配置了一個Spring MVC内置的一個視圖解析器,該解析器是遵循着一種約定:會在視圖名上添加字首和字尾,進而确定一個Web應用中視圖資源的實體路徑,我們再來重新配置一下:

1、将HelloController的index.jsp修改為index

2、配置視圖解析器

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

    <!--<bean id="simpleUrlHandlerMapping"-->
                                        <!--class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">-->
    <!--<property name="mappings">-->
            <!--<props>-->
                <!--&lt;!&ndash; /hello 路徑的請求交給 id 為 helloController 的控制器處理&ndash;&gt;-->
                <!--<prop key="/hello">helloController</prop>-->
            <!--</props>-->
        <!--</property>-->
    <!--</bean>-->
    <!--<bean id="helloController" class="controller.HelloController"></bean>-->

    <!-- 掃描controller下的元件 -->
    <context:component-scan base-package="controller"/>
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>
           

3、在【WEB-INF】檔案夾下建立一個【page】檔案夾,并将【index.jsp】檔案剪貼到裡面

4、更新資源重新開機伺服器

通路localhost/hello路徑,仍可以看到上面的界面

原理:

Note:此時的配置僅是在dispatcher-servlet.xml下的

控制器接收請求資料

使用控制器接收參數往往是Spring MVC開發業務邏輯的第一步,為探索Spring MVC的傳參方式,為此我們先來建立一個簡單的表單用于送出資料:

<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" import="java.util.*" isELIgnored="false"%>
<html>
<head>
    <meta charset="utf-8">
    <title>Spring MVC 傳參方式</title>
</head>
<body>
<form action="/param" role="form">
    使用者名:<input type="text" name="userName"><br/>
    密碼:<input type="text" name="password"><br/>
    <input type="submit" value="提  交">
</form>
</body>
</html>
           

1》使用servlet原生API實作:

從上面的代碼我們可以看出,表單是會送出到/param這個目錄,我們先來使用Servlet原生API看看能能擷取到資料

@RequestMapping("/param")
public ModelAndView getParam(HttpServletRequest request,
                         HttpServletResponse response) {
    String userName = request.getParameter("userName");
    String password = request.getParameter("password");

    System.out.println(userName);
    System.out.println(password);
    return null;
}
           

2》使用同名比對規則:

@RequestMapping("/param")
public ModelAndView getParam(String userName,String password) {
    System.out.println(userName);
    System.out.println(password);
    return null;
}


//使用@RequestParam("前台參數名")來注入,進而實作解耦
@RequestMapping("/param")
public ModelAndView getParam(@RequestParam("username") String userName,@RequestParam("password") String password) {
    System.out.println(userName);
    System.out.println(password);
    return null;
}
           

3》使用模型傳參

要求:前台參數名字必須和模型中的字段名一樣

我們先來為我們的表單建立一個User模型:

package pojo;

public class User {
    
    String userName;
    String password;

    /* getter and setter */
}
@RequestMapping("/param")
public ModelAndView getParam(User user) {
    System.out.println(userName);
    System.out.println(password);
    return null;
}
           

中文亂碼問題,我們可以通過配置Spring MVC字元編碼過濾器來完成,在web.xml中添加如下代碼:

<filter>
    <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
           

控制器回顯資料

通過上面的學習,我們知道了怎麼接受請求資料,并且能夠解決POST亂碼問題,那麼我們怎麼回顯資料呢?接下來先建立一個【test2.jsp】

<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" import="java.util.*" isELIgnored="false" %>
<html>
<head>
    <title>Spring MVC 資料回顯</title>
</head>
<body>
<h1>回顯資料:${message}</h1>
</body>
</html>
           

1》使用Servlet原生API來實作

@RequestMapping("/value")
public ModelAndView handleRequest(HttpServletRequest request,
                                  HttpServletResponse response) {
    request.setAttribute("message","成功!");
    return new ModelAndView("test1");
}
           

在浏覽器中輸入:localhost/value 測試,能夠顯示成功

2》使用Spring MVC所提供的ModelAndView對象

@RequestMapping("/value")
public ModelAndView handleRequest(HttpServletRequest req,HttpServletResponse resp){
    ModelAndView mav = new ModelAndView("test1");
    mav.addObject("message","成功");
    return mav;
}
           

3》使用Model對象

//在Spring MVC中可以使用這樣的方式來綁定資料
@RequestMapping("/value")
public String handleRequest(Model  model){
    model.addAttribute("message","成功!");
    return "test1";
}
           

4》使用@ModelAttribute注解

@ModelAttribute
public void model(Model model) {
    model.addAttribute("message", "注解成功");
}

@RequestMapping("/value")
public String handleRequest() {
    return "test1";
}
//這樣寫的話就會在通路控制器方法handlerRequest()時,先調用model()方法,
//将message添加至頁面參數中,在視圖中直接調用,但是這樣寫會導緻控制器的所有
//方法都會調用model()方法
           

用戶端跳轉:

前面的不管是位址/hello跳轉到index.jsp環視/test跳轉到etst.jsp,這些都是伺服器的跳轉,也就是

request.getRequestDispatcher(“位址”).forward(request,response);

我們繼續HelloController中編寫,實作用戶端跳轉:

@RequestMapping("/hello")
public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
    ModelAndView mav = new ModelAndView("index");
    mav.addObject("message", "Hello Spring MVC");
    return mav;
}

@RequestMapping("/jump")
public ModelAndView jump() {
    ModelAndView mav = new ModelAndView("redirect:/hello");
    return mav;
}
//也可以這麼寫
@RequestMapping("/jump")
public String jump() {
    return "redirect: ./hello";
}
           

Note:我們使用 redirect:/hello 表示我們要跳轉到/hello這個路徑,我們重新開機伺服器,在位址欄輸入localhost/jump,會自動跳轉到 /hello 路徑下

Spring MVC中的檔案上傳和下載下傳

首先導入jar包,commons-io-1.3.2.jar和commons-fileupload-1.2.1.jar兩個包,接下來舉例說明:

1、配置上傳解析器

在dispatcher-servlet.xml中新增如下這句,開啟對上傳功能的支援

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
           

2、編寫JSP

檔案名為upload.jsp,仍建立在【page】下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>測試檔案上傳</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="picture">
    <input type="submit" value="上 傳">
</form>
</body>
</html>
           

3、編寫控制器

在Package【controller】下建立【UploadController】類:

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class UploadController {

    @RequestMapping("/upload")
    public void upload(@RequestParam("picture") MultipartFile picture) throws Exception {
        System.out.println(picture.getOriginalFilename());
    }

    @RequestMapping("/test2")
    public ModelAndView upload() {
        return new ModelAndView("upload");
    }

}
           

4、測試,在浏覽器位址欄輸入:localhost/test2,選擇檔案點選上傳

轉載:我沒有三顆心髒

歡迎大家一起來交流學習哦,如果有錯誤的地方歡迎指出!謝謝

繼續閱讀