三次框架
在B/S架構中,系統標準的三層架構包括:表現層、業務層、持久層。
表現層
也就是常說的web層。
它負責接收客戶端請求,向客戶端響應結果,通常客戶端使用http協議請求web層,web需要接收http請求,完成http響應。
表現層包括展示層和控制層:控制層負責接收請求,展示層負責結果的展示。
表現層依賴業務層,接收到客戶端請求一般會調用業務層進行業務處理,並將處理結果響應給客戶端。
表現層的設計一般都使用MVC模型。(MVC是表現層的設計模型,和其他層沒有關係)
業務層
也就是常說的service層。
它負責業務邏輯處理,和我們開發項目的需求息息相關。
web層依賴業務層,但是業務層不依賴web層。
業務層在業務處理時可能會依賴持久層,如果要對數據持久化需要保證事務一致性。(也就是我們說的,事務應該放到業務層來控制)
持久層
也就是常說的dao層。
負責數據持久化,包括數據層即數據庫和數據訪問層,數據庫是對數據進行持久化的載體,數據訪問層是業務層和持久層交互的接口,業務層需要通過數據訪問層將數據持久化到數據庫中。
通俗的講,持久層就是和數據庫交互,對數據庫表進行曾刪改查的。
MVC模型
MVC全名是Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫。
Model(模型): 通常指的就是我們的數據模型。作用一般情況下用於封裝數據。
View(視圖): 通常指的就是我們的jsp或者html。作用一般就是展示數據的。 通常視圖是依據模型數據創建的。
Controller(控制器): 是應用程序中處理用戶交互的部分。作用一般就是處理程序邏輯的。
SpringMVC是什麼
SpringMVC是一種基於Java的實現MVC設計模型的請求驅動類型的輕量級Web框架,屬於 Spring FrameWork 的後續產品,已經融合在Spring Web Flow裏面。
SpringMVC和Struts2的優勢分析
共同點:
它們都是表現層框架,都是基於MVC模型編寫的。
它們的底層都離不開原始ServletAPI。
它們處理請求的機制都是一個核心控制器。
區別:
Spring MVC 的入口是 Servlet, 而 Struts2 是 Filter
Spring MVC 是基於方法設計的,而Struts2是基於類,Struts2每次執行都會創建一個動作類。所以Spring MVC 會稍微比 Struts2 快些。 Spring MVC 使用更加簡潔,同時還支持 JSR303, 處理 ajax 的請求更方便。
Struts2 的OGNL 表達式使頁面的開發效率相比Spring MVC 更高些,但執行效率並沒有比JSTL提升,尤其是struts2的表單標籤,遠沒有html執行效率高。
SpringMVC中的部分組件
DispatcherServlet:前端控制器
用戶請求到達前端控制器,它就相當於mvc模式中的c,dispatcherServlet是整個流程控制的中心,由它調用其它組件處理用戶的請求,dispatcherServlet的存在降低了組件之間的耦合性。
HandlerMapping:處理器映射器
HandlerMapping負責根據用戶請求找到Handler即處理器,SpringMVC提供了不同的映射器實現不同的映射方式,例如:配置文件方式,實現接口方式,註解方式等。
Handler:處理器
它就是我們開發中要編寫的具體業務控制器。由DispatcherServlet把用戶請求轉發到Handler。由Handler對具體的用戶請求進行處理。
HandlAdapter:處理器適配器
通過HandlerAdapter對處理器進行執行,這是適配器模式的應用,通過擴展適配器可以對更多類型的處理器進行執行。
View Resolver:視圖解析器
View Resolver負責將處理結果生成View視圖,View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最後對View進行渲染將處理結果通過頁面展示給用戶。
View:視圖
SpringMVC框架提供了很多的View視圖類型的支持,包括:jstlView、freemarkerView、pdfView等。我們最常用的視圖就是jsp。
入門案例
配置核心控制器web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置spring mvc的核心控制器 -->
<servlet>
<!--固定的寫控制器組件DispatcherServlet,起控制作用,相當於servlet的指揮中心-->
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化參數,用於讀取SpringMVC的配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--配置文件的位置-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 配置servlet的對象的創建時間點:應用加載時創建。 取值只能是非0正整數,表示啓動順序 -->
<!--配置爲1代表當Tomcat啓動的時候DispatcherServlet就會被創建-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--用servlet攔截-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<!--填寫"/"代表攔截全部頁面-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
創建SpringMVC的配置文件springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--開啓註解掃描-->
<context:component-scan base-package="com.lwl"></context:component-scan>
<!--配置視圖解析器,視圖解析器對象,id和class都是固定不變的,它可以幫助我們跳轉到指定頁面-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--prefix指定跳轉到文件的位置-->
<property name="prefix" value="/WEB-INF/pages/"/>
<!--suffix指定跳轉到的文件的後綴名-->
<property name="suffix" value=".jsp"></property>
</bean>
<!--開啓SpringMVC框架註解的支持-->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
編寫控制器並使用註解配置
@Controller
public class HelloController {
//@RequestMapping請求映射標籤,當該類被調用的時候會執行這個方法
//path屬性指定方法的請求路徑
@RequestMapping(path="/hello")
public String sayHello(){
System.out.println("Hello StringMVC");
//默認表示爲跳轉到名稱爲success.jsp的頁面
return "success";
}
}
SpringMVC的請求響應流程
@RequestMapping註解的作用
源碼:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
作用:用於建立請求URL和處理請求方法之間的對應關係。
出現的位置:該註解可以寫在類上也可以寫在方法上。
出現在類上:
請求URL的第一級訪問目錄。此處不寫的話,就相當於應用的根目錄。寫的話需要以/開頭。 它出現的目的是爲了使我們的URL可以按照模塊化管理:
例如:
賬戶模塊:
/account/add
/account/update
/account/delete ...
訂單模塊:
/order/add
/order/update
/order/delete
上面例子中 /account 和 /order 可以使用 @RequestMapping 填寫在類上, 而 /add 等這些作爲二級目錄寫在方法上
方法上: 請求URL的第二級訪問目錄。
註解的屬性:
- value:用於指定請求的URL。它和path屬性的作用是一樣的。
- method:用於指定請求的方式(選擇GET,POST,PUT,PATCH等方式)。
- params:用於指定限制請求參數的條件。它支持簡單的表達式。要求請求參數的key和value必須和配置的一模一樣。
例如: params = {“accountName”},表示請求參數必須有accountName params = {“moeny!100”},表示請求參數中money不能是100。 - headers:用於指定限制請求消息頭的條件。
請求參數的綁定
- 請求參數的綁定說明
- 綁定機制
- 表單提交的數據都是key=value格式的 username=haha&password=123
- SpringMVC的參數綁定過程是把表單提交的請求參數,作爲控制器中方法的參數進行綁定的
- 要求:提交表單的name和參數的名稱是相同的
- 綁定機制
- 支持的數據類型
- 基本數據類型和字符串類型
- 實體類型(JavaBean)
- 集合數據類型(List、map集合等)
零散接收參數-案例
編寫一個簡單的jsp頁面,超鏈接中傳遞兩個參數:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>請求參數綁定</title>
</head>
<body>
<a href="param/testParam?username=hehe&password=12345">請求參數綁定</a>
</body>
</html>
參數將會進入@RequestMapping指定的控制類的方法中:
@Controller
@RequestMapping("/param")
public class ParamController {
//請求參數綁定入門
@RequestMapping("/testParam")
public String testParam(String username, String password) {
System.out.println("testParam執行了。。");
System.out.println("賬戶:" + username + ",密碼:" + password);
return "success";
}
}
當訪問該jsp頁面並點擊該鏈接後就會調用testParam方法,在控制檯輸出:
賬戶:hehe,密碼:1234
用類封裝參數-案例
當時如果傳入的參數比較多,就應該把參數封裝到一個類中:
新建一個實體類Account並添加get,set和toString方法:
public class Account implements Serializable {
private String username;
private String password;
private Double money;
//省去get,set和toString方法
}
創建一個表單,並且地址指向一個方法:
<!--創建一個表單-->
<form action="param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br>
密碼:<input type="text" name="password"/><br>
金額:<input type="text" name="money"/><br>
<input type="submit" value="提交"><br>
</form>
在測試類中創建這個方法,直接在方法的形參中輸入這個類,就可以直接封裝進去:
@Controller
@RequestMapping("/param")
public class ParamController {
//請求參數封裝到類中
@RequestMapping("/saveAccount")
public String saveAccount(Account account) {
System.out.println("testParam執行了。。");
System.out.println(account);
return "success";
}
}
輸入:
控制檯輸出:
如果Account 類中包含另一個類的實例。應該怎麼封裝呢?
User類:
public class User implements Serializable {
private String uname;
private Integer age;
}
Account :
public class Account implements Serializable {
private String username;
private String password;
private Double money;
//新增
private User user;
那麼在創建表單的時候可以這樣實現:
<!--創建一個表單-->
<form action="param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br>
密碼:<input type="text" name="password"/><br>
金額:<input type="text" name="money"/><br>
用戶姓名:<input type="text" name="user.uname"/><br>
用戶年齡:<input type="text" name="user.age"/><br>
<input type="submit" value="提交"><br>
</form>
解決中文亂碼問題:在過濾器中實現:
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>
如果這個Account類中還包含了List與Map集合應該怎麼封裝?
public class Account implements Serializable {
private String username;
private String password;
private Double money;
//新增
private User user;
//新增
List<User> users;
}
jsp中的表單:
<!--創建一個表單,類中存在List、Map集合-->
<form action="param/saveAccount" method="post">
姓名:<input type="text" name="username"/><br>
密碼:<input type="text" name="password"/><br>
金額:<input type="text" name="money"/><br>
用戶姓名:<input type="text" name="user.uname"/><br>
用戶年齡:<input type="text" name="user.age"/><br>
<%--封裝到list中的一個User對象裏--%>
路人姓名0:<input type="text" name="users[0].uname"/><br>
路人年齡0:<input type="text" name="users[0].age"/><br>
<%--封裝到list中的一個User對象裏--%>
路人姓名1:<input type="text" name="users[1].uname"/><br>
路人年齡1:<input type="text" name="users[1].age"/><br>
<%--封裝到map中的一個User對象裏,鍵設置爲‘key2’--%>
路人姓名2:<input type="text" name="map['key2'].uname"/><br>
路人年齡2:<input type="text" name="map['key2'].age"/><br>
<input type="submit" value="提交"><br>
</form>
如此就可以實現集合的封裝。
輸入:
控制檯輸出;
testParam執行了。。
Account{username=‘張張’, password=‘123456’, money=200.0, user=User{uname=‘打哈’, age=22}, users=[User{uname=‘甲’, age=21}, User{uname=‘乙’, age=25}], map={key2=User{uname=‘丙’, age=30}}}
自定義類型轉換器
在User中添加一個生日:
public class User implements Serializable {
private String uname;
private Integer age;
private Date date;
}
創建表單:
<!--創建一個表單-->
<form action="param/saveUser" method="post">
姓名:<input type="text" name="uname"/><br>
年齡:<input type="text" name="age"/><br>
生日:<input type="text" name="date"/><br>
<input type="submit" value="提交"><br>
</form>
處理提交的數據:
//自定義類型轉換器
@RequestMapping("/saveUser")
public String saveUser(User user) {
System.out.println("saveUser執行了。。");
System.out.println(user);
return "success";
}
輸入:
此時控制檯會打印:
saveUser執行了。。
User{uname=‘張張’, age=123456, date=Sun Feb 02 00:00:00 CST 2020}
但是如果想把Date類型的輸入進行修改:
訪問就會報錯:
此時我們需要自定義類型轉換器:
/**
* 字符串轉換成日期
* @author liwenlong
* @data 2020/5/21
*/
public class StringToDate implements Converter<String, Date> {
@Override
public Date convert(String s) {
if (s == null) {
throw new RuntimeException("請傳入數據");
}
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
try {
//把字符串轉日期
return df.parse(s);
} catch (ParseException e) {
throw new RuntimeException("錯誤");
}
}
}
在springmvc.xml文件中註冊類型轉換器:
<!--配置自定義類型轉換器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!--註冊類型轉化器-->
<property name="converters">
<set>
<bean class="com.lwl.util.StringToDate"></bean>
</set>
</property>
</bean>
<!--開啓SpringMVC框架註解的支持-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
此時輸入2020-02-02就不會再報錯,但是原來的2020/02/02就不能用了。