一罪佳、關(guān)于三層架構(gòu)和 MVC
三層架構(gòu)
我們的開發(fā)架構(gòu)一般都是基于兩種形式泼掠,一種是 C/S 架構(gòu),也就是客戶端/服務(wù)器绰沥,另一種是 B/S 架構(gòu)昔期,也就是瀏覽器服務(wù)器。在 JavaEE 開發(fā)中,幾乎全都是基于 B/S 架構(gòu)的開發(fā)。那么在 B/S 架構(gòu)中,系統(tǒng)標(biāo)準(zhǔn)的三層架構(gòu)包括:表現(xiàn)層扔罪、業(yè)務(wù)層、持久層桶雀。
三層架構(gòu)中矿酵,每一層各司其職,接下來我們就說說每層都負(fù)責(zé)哪些方面:
表現(xiàn)層:
也就是我們常說的web層矗积。它負(fù)責(zé)接收客戶端請求全肮,向客戶端響應(yīng)結(jié)果,通臣罚客戶端使用http協(xié)議請求web 層辜腺,web 需要接收 http 請求,完成 http 響應(yīng)。
表現(xiàn)層包括展示層和控制層:控制層負(fù)責(zé)接收請求评疗,展示層負(fù)責(zé)結(jié)果的展示测砂。
表現(xiàn)層依賴業(yè)務(wù)層,接收到客戶端請求一般會調(diào)用業(yè)務(wù)層進(jìn)行業(yè)務(wù)處理百匆,并將處理結(jié)果響應(yīng)給客戶端砌些。
表現(xiàn)層的設(shè)計一般都使用 MVC 模型。(MVC 是表現(xiàn)層的設(shè)計模型加匈,和其他層沒有關(guān)系)
業(yè)務(wù)層:
也就是我們常說的 service 層存璃。它負(fù)責(zé)業(yè)務(wù)邏輯處理,和我們開發(fā)項目的需求息息相關(guān)雕拼。web 層依賴業(yè)務(wù)層纵东,但是業(yè)務(wù)層不依賴 web 層。業(yè)務(wù)層在業(yè)務(wù)處理時可能會依賴持久層啥寇,如果要對數(shù)據(jù)持久化需要保證事務(wù)一致性篮迎。(也就是我們說的,事務(wù)應(yīng)該放到業(yè)務(wù)層來控制)
持久層:
也就是我們是常說的 dao 層示姿。負(fù)責(zé)數(shù)據(jù)持久化,包括數(shù)據(jù)層即數(shù)據(jù)庫和數(shù)據(jù)訪問層逊笆,數(shù)據(jù)庫是對數(shù)據(jù)進(jìn)行持久化的載體栈戳,數(shù)據(jù)訪問層是業(yè)務(wù)層和持久層交互的接口,業(yè)務(wù)層需要通過數(shù)據(jù)訪問層將數(shù)據(jù)持久化到數(shù)據(jù)庫中难裆。通俗的講子檀,持久層就是和數(shù)據(jù)庫交互,對數(shù)據(jù)庫表進(jìn)行曾刪改查的乃戈。
MVC 模型
MVC 全名是 Model View Controller褂痰,是模型(model)-視圖(view)-控制器(controller)的縮寫,是一種用于設(shè)計創(chuàng)建 Web 應(yīng)用程序表現(xiàn)層的模式症虑。MVC 中每個部分各司其職:
Model(模型):
通常指的就是我們的數(shù)據(jù)模型缩歪。作用一般情況下用于封裝數(shù)據(jù)。
View(視圖):
通常指的就是我們的 jsp 或者 html谍憔。作用一般就是展示數(shù)據(jù)的匪蝙。
通常視圖是依據(jù)模型數(shù)據(jù)創(chuàng)建的。
Controller(控制器):
是應(yīng)用程序中處理用戶交互的部分习贫。作用一般就是處理程序邏輯的逛球。
它相對于前兩個不是很好理解,這里舉個例子:
例如:
我們要保存一個用戶的信息苫昌,該用戶信息中包含了姓名颤绕,性別,年齡等等。
這時候表單輸入要求年齡必須是 1~100 之間的整數(shù)奥务。姓名和性別不能為空物独。并且把數(shù)據(jù)填充
到模型之中。
此時除了 js 的校驗之外汗洒,服務(wù)器端也應(yīng)該有數(shù)據(jù)準(zhǔn)確性的校驗议纯,那么校驗就是控制器的該做的。
當(dāng)校驗失敗后溢谤,由控制器負(fù)責(zé)把錯誤頁面展示給使用者瞻凤。
如果校驗成功,也是控制器負(fù)責(zé)把數(shù)據(jù)填充到模型世杀,并且調(diào)用業(yè)務(wù)層實現(xiàn)完整的業(yè)務(wù)需求阀参。
二、SpringMVC 概述
SpringMVC 是什么
SpringMVC 是一種基于 Java 的實現(xiàn) MVC 設(shè)計模型的請求驅(qū)動類型的輕量級 Web 框架瞻坝,屬于 SpringFrameWork 的后續(xù)產(chǎn)品蛛壳,已經(jīng)融合在 Spring Web Flow 里面。Spring 框架提供了構(gòu)建 Web 應(yīng)用程序的全功能 MVC 模塊所刀。使用 Spring 可插入的 MVC 架構(gòu)衙荐,從而在使用 Spring 進(jìn)行 WEB 開發(fā)時,可以選擇使用 Spring的 Spring MVC 框架或集成其他 MVC 開發(fā)框架浮创,如 Struts1(現(xiàn)在一般不用)忧吟,Struts2 等。
SpringMVC 已經(jīng)成為目前最主流的 MVC 框架之一斩披,并且隨著 Spring3.0 的發(fā)布溜族,全面超越 Struts2,成為最優(yōu)秀的 MVC 框架垦沉。它通過一套注解煌抒,讓一個簡單的 Java 類成為處理請求的控制器,而無須實現(xiàn)任何接口厕倍。同時它還支持RESTful 編程風(fēng)格的請求寡壮。
SpringMVC 在三層架構(gòu)的位置
SpringMVC 的優(yōu)勢
1、清晰的角色劃分:
前端控制器(DispatcherServlet)
請求到處理器映射(HandlerMapping)
處理器適配器(HandlerAdapter)
視圖解析器(ViewResolver)
處理器或頁面控制器(Controller)
驗證器( Validator)
命令對象(Command 請求參數(shù)綁定到的對象就叫命令對象)
表單對象(Form Object 提供給表單展示和提交到的對象就叫表單對象)绑青。
2诬像、分工明確,而且擴(kuò)展點相當(dāng)靈活闸婴,可以很容易擴(kuò)展坏挠,雖然幾乎不需要。 3邪乍、由于命令對象就是一個 POJO降狠,無需繼承框架特定 API对竣,可以使用命令對象直接作為業(yè)務(wù)對象。 4榜配、和 Spring 其他框架無縫集成否纬,是其它 Web 框架所不具備的。 5蛋褥、可適配临燃,通過 HandlerAdapter 可以支持任意的類作為處理器。 6烙心、可定制性膜廊,HandlerMapping、ViewResolver 等能夠非常簡單的定制淫茵。 7爪瓜、功能強(qiáng)大的數(shù)據(jù)驗證、格式化匙瘪、綁定機(jī)制铆铆。 8、利用 Spring 提供的 Mock 對象能夠非常簡單的進(jìn)行 Web 層單元測試丹喻。 9薄货、本地化、主題的解析的支持碍论,使我們更容易進(jìn)行國際化和主題的切換菲驴。
10、強(qiáng)大的 JSP 標(biāo)簽庫骑冗,使 JSP 編寫更容易。
………………還有比如RESTful風(fēng)格的支持先煎、簡單的文件上傳贼涩、約定大于配置的契約式編程支持、基于注解的零配
置支持等等薯蝎。
三遥倦、SpringMVC入門
第一步:導(dǎo)入所需Maven標(biāo)注
<!--spring家族核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version>
</dependency>
<!--Spring webmvc:包含SpringMVC框架相關(guān)的所有類。包含國際化占锯、標(biāo)簽袒哥、Theme、視圖展現(xiàn)的FreeMarker消略、JasperReports堡称、 Tiles、Velocity艺演、XSLT相關(guān)類却紧。-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--servlet支持包-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
第二步:配置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">
<!--開啟包掃描-->
<context:component-scan base-package="com.gzy.web"></context:component-scan>
<!--
定制化了一下 視圖解析器 不然話 每次寫很長的路徑 配置前綴后后綴
-->
<bean id="InternalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
第三步:配置web.xml文件
注意:web.xml版本不要過低桐臊!
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 配置 spring mvc 的核心控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化參數(shù),用于讀取 SpringMVC 的配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 配置 servlet 的對象的創(chuàng)建時間點:應(yīng)用加載時創(chuàng)建晓殊。
取值只能是非 0 正整數(shù)断凶,表示啟動順序 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--應(yīng)用范圍-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--利用攔截器設(shè)置編碼-->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
第四步:編寫控制器并使用注解配置
package com.gzy.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.text.SimpleDateFormat;
import java.util.Date;
//測試:發(fā)送日期時間到頁面
@Controller
public class UserController {
@RequestMapping("/test1")
public ModelAndView test1(ModelAndView modelAndView){
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String format = simpleDateFormat.format(date);
modelAndView.addObject("cc",format);
//springmvc.xml不配置前綴后后綴
//modelAndView.setViewName("/WEB-INF/pages/user/hello.jsp");
//springmvc.xml配置前綴后后綴后改為
modelAndView.setViewName("user/hello");
return modelAndView;
}
}
hello.jsp頁面代碼
<%--
Created by IntelliJ IDEA.
User: gzy
Date: 2019/7/5
Time: 21:05
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>測試</title>
</head>
<body>
<div style="text-align: center;color: red;margin: 200px auto;">
當(dāng)前時間是:<br/>
${cc}
</div>
</body>
</html>
效果:
Snipaste_2019-07-05_21-52-28.png
涉及的組件
DispatcherServlet:
前端控制器用戶請求到達(dá)前端控制器,它就相當(dāng)于 mvc 模式中的 c巫俺,dispatcherServlet 是整個流程控制的中心认烁,由它調(diào)用其它組件處理用戶的請求,dispatcherServlet 的存在降低了組件之間的耦合性介汹。
HandlerMapping:
處理器映射器HandlerMapping 負(fù)責(zé)根據(jù)用戶請求找到 Handler 即處理器却嗡,SpringMVC 提供了不同的映射器實現(xiàn)不同的映射方式,例如:配置文件方式痴昧,實現(xiàn)接口方式稽穆,注解方式等。
Handler:
處理器它就是我們開發(fā)中要編寫的具體業(yè)務(wù)控制器赶撰。由DispatcherServlet 把用戶請求轉(zhuǎn)發(fā)到 Handler舌镶。由Handler 對具體的用戶請求進(jìn)行處理。
HandlAdapter:處理器適配器
View Resolver:視圖解析器
View Resolver 負(fù)責(zé)將處理結(jié)果生成 View 視圖豪娜,View Resolver 首先根據(jù)邏輯視圖名解析成物理視圖名即具體的頁面地址餐胀,再生成 View 視圖對象,最后對 View 進(jìn)行渲染將處理結(jié)果通過頁面展示給用戶瘤载。
View:視圖
SpringMVC 框架提供了很多的 View 視圖類型的支持否灾,包括:jstlView、freemarkerView鸣奔、pdfView等墨技。我們最常用的視圖就是 jsp。一般情況下需要通過頁面標(biāo)簽或頁面模版技術(shù)將模型數(shù)據(jù)通過頁面展示給用戶挎狸,需要由程序員根據(jù)業(yè)務(wù)需求開發(fā)具體的頁面扣汪。
<mvc:annotation-driven>說明
在 SpringMVC 的各個組件中,處理器映射器锨匆、處理器適配器崭别、視圖解析器稱為 SpringMVC 的三大組件。使 用 <mvc:annotation-driven> 自動加載 RequestMappingHandlerMapping (處理映射器) 和
RequestMappingHandlerAdapter ( 處 理 適 配 器 ) 恐锣, 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用<mvc:annotation-driven>替代注解處理器和適配器的配置茅主。
它就相當(dāng)于在 xml 中配置了:
<!-- 上面的標(biāo)簽相當(dāng)于 如下配置-->
<!-- Begin -->
<!-- HandlerMapping -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerM
apping">
</bean>
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
<!-- HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
</bean>
<bean
class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter">
</bean>
<bean
class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter">
</bean>
<!-- HadnlerExceptionResolvers -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExcept
ionResolver">
</bean><beanclass="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
</bean>
<bean
class="org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver"></bean>
<!-- End -->
注意:
一般開發(fā)中,我們都需要寫上此標(biāo)簽.
明確:
我們只需要編寫處理具體業(yè)務(wù)的控制器以及視圖土榴。
演示參數(shù)綁定
演示使用的實體類
user
package com.gzy.domain;
import java.util.Date;
public class User {
private int id;
private String username;
private String desc;
private boolean vip;
private Date birthday;
......................
}
QueryVo
package com.gzy.domain;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class QueryVo {
private User user;
private int age;
private String[] hobbies;
private List<User> users;
private Map faxinren;
................
}
jsp頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<fieldset>
<legend>演示1 演示參數(shù)綁定 簡單類型</legend>
<form action="/mvc/user/test2" method="post">
id:<input type="text" name="xid"><br>
用戶名:<input type="text" name="username"><br>
描述:<input type="text" name="desc"><br>
是否是vip:<input type="text" name="vip"><br>
<input type="submit" value="點我提交">
</form>
</fieldset>
<fieldset>
<legend>演示2 演示參數(shù)綁定 復(fù)合對象類型</legend>
<form action="/mvc/user/test3" method="post">
id:<input type="text" name="user.id"><br>
用戶名:<input type="text" name="user.username"><br>
描述:<input type="text" name="user.desc"><br>
是否是vip:<input type="text" name="user.vip"><br>
生日:<input type="date" name="user.birthday"><br>
年齡:<input type="text" name="age"><br>
愛好:
<input type="checkbox" name="hobbies" value="抽煙">抽煙
<input type="checkbox" name="hobbies" value="喝酒">喝酒
<input type="checkbox" name="hobbies" value="燙頭">燙頭
<br>
收信人列表:<br>
收信人1:<input type="text" name="users[0].id">
<input type="text" name="users[0].username">
<br>
收信人2:<input type="text" name="users[1].id">
<input type="text" name="users[1].username">
<br>
收信人3:<input type="text" name="users[2].id">
<input type="text" name="users[2].username">
<br>
發(fā)信人:
<input type="text" name="faxinren['name']">
<input type="text" name="faxinren['tel']">
<input type="text" name="faxinren['address']">
<input type="submit" value="點我提交">
</form>
</fieldset>
</body>
</html>
Handler代碼(只做簡單演示诀姚,代碼簡陋,勿噴)
package com.gzy.web.controller;
import com.gzy.domain.QueryVo;
import com.gzy.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/test1")
public ModelAndView test1(ModelAndView modelAndView){
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String format = simpleDateFormat.format(date);
modelAndView.addObject("cc",format);
//springmvc.xml不配置前綴后后綴
//modelAndView.setViewName("/WEB-INF/pages/user/hello.jsp");
//springmvc.xml配置前綴后后綴后改為
modelAndView.setViewName("user/hello");
return modelAndView;
}
/**
* RequestParam 強(qiáng)制告訴springmvc 該位置參數(shù)名字 是啥
* 可以給默認(rèn)值
* 主要的作用 協(xié)商的作用
* 跟前端開發(fā)協(xié)商 參數(shù)名字叫啥
* @RequestMapping(value = "/test2",method = RequestMethod.POST)
* method代表請求方式玷禽,value請求路徑
*/
@RequestMapping(value = "/test2",method = RequestMethod.POST)
public ModelAndView test2(ModelAndView modelAndView, @RequestParam(name ="xid",defaultValue = "123") int id, String username, String desc, boolean vip){
System.out.println(id);
System.out.println(username);
System.out.println(desc);
System.out.println(vip);
return modelAndView;
}
//演示參數(shù)綁定 復(fù)合對象類型,簡單對象包含特殊類型(這里是Date)
@RequestMapping(value = "/test3",method = RequestMethod.POST)
public ModelAndView test3(ModelAndView modelAndView, QueryVo queryVo){
System.out.println(queryVo);
return modelAndView;
}
//獲取請求頭的值
@RequestMapping(value="/test4",method = RequestMethod.GET)
public ModelAndView test10(ModelAndView modelAndView,@RequestHeader(value = "user-agent") String useragent){
System.out.println(useragent);
return modelAndView;
}
//獲取HttpServletRequest学搜,cookie參數(shù)
@RequestMapping(value="/test5",method = RequestMethod.GET)
public ModelAndView test11(ModelAndView modelAndView, HttpServletRequest request, @CookieValue(value = "JSESSIONID") String useragent){
String host = request.getHeader("Host");
System.out.println(host);
System.out.println(useragent);
return modelAndView;
}
}
springmvc.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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--開啟包掃描-->
<context:component-scan base-package="com.gzy.web"></context:component-scan>
<!--
主動強(qiáng)制性告訴springmvc 處理映射器采用 RequestMappingHandlerMapping來實現(xiàn)
歷史原因?qū)е碌?
1.很久以前 3版本 4 版本的時候 已經(jīng)人們寫了RequestMappingHandlerMapping 但是考慮向下兼容性
2.不僅僅是用來指明默認(rèn) 處理映射器 還有其他的作用
-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!--
寫了一個注冊轉(zhuǎn)換服務(wù)器類 這個類 負(fù)責(zé)將很多的轉(zhuǎn)換器 注冊給我springmvc框架
-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.gzy.web.conterver.MyDateConverter"></bean>
</set>
</property>
</bean>
<!--
定制化了一下 視圖解析器 不然話 每次寫很長的路徑 配置前綴后后綴
-->
<bean id="InternalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
轉(zhuǎn)換服務(wù)器類(這里將Date特殊類型轉(zhuǎn)換為String)
package com.gzy.web.conterver;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
//這是一個類型轉(zhuǎn)換類
public class MyDateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
Date date=null;
//現(xiàn)在一個 2019-07-02
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
date = simpleDateFormat.parse(s);
} catch (ParseException e) {
date=new Date();
}
return date;
}
}