MVC 設計概述
在早期Java Web的開發中,統一把顯示層,控制層,資料層全部交給JSP或者JavaBean來進行處理,就像下圖所示:
【弊端】:
- JSP和Java Bean之間嚴重耦合,java代碼和HTML代碼也耦合在了一起
- 要求開發者不僅要掌握java,還要有高水準的前端技術
- 前段和後端互相依賴,前段需要等待後端完成,後端也要依賴前端完成,才能進行有效的測試
- 代碼難以複用
正是因為上面的弊端,是以很快就被Servlet+JSP+Java Bean所取代,早期的MVC模型如下:
首先使用者的請求會到達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給出了方案
傳統的業務層被拆分為業務層(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>-->
<!--<!– /hello 路徑的請求交給 id 為 helloController 的控制器處理–>-->
<!--<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>-->
<!--<!– /hello 路徑的請求交給 id 為 helloController 的控制器處理–>-->
<!--<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,選擇檔案點選上傳
轉載:我沒有三顆心髒
歡迎大家一起來交流學習哦,如果有錯誤的地方歡迎指出!謝謝