寫在前面:本文簡單講解SpringMVC的一些註解
公衆號:小白編碼
本文目錄
第一章:三層架構和MVC
- 咱們開發服務器端程序,一般都基於兩種形式,一種C/S架構程序,一種B/S架構程序
- 使用Java語言基本上都是開發B/S架構的程序,B/S架構又分成了三層架構
三層架構:
- 表現層: WEB層,用來和客戶端進行數據交互的。表現層一般會採用MVC的設計模型
- 業務層: 處理公司具體的業務邏輯的
- 持久層: 用來操作數據庫的
MVC模型
MVC全名是Model View Controller 模型視圖控制器,每個部分各司其職。
- Model:數據模型,JavaBean的類,用來進行數據封裝。
- View:指JSP、HTML用來展示數據給用戶
- Controller:用來接收用戶的請求,整個流程的控制器。用來進行數據等。
第二章:SpringMVC優勢
SpringMVC優勢
1.清晰的角色劃分:
- 前端控制器(DispatcherServlet)
- 請求到處理器映射(HandlerMapping)
- 處理器適配器(HandlerAdapter)
- 視圖解析器(ViewResolver)
- 處理器或頁面控制器(Controller)
- 驗證器( Validator)
- 命令對象(Command 請求參數綁定到的對象就叫命令對象)
- 表單對象(Form Object 提供給表單展示和提交到的對象就叫表單對象)。
2.分工明確,而且擴展點相當靈活,可以很容易擴展,雖然幾乎不需要。
3.由於命令對象就是一個POJO,無需繼承框架特定API,可以使用命令對象直接作爲業務對象。
4.和Spring 其他框架無縫集成,是其它Web框架所不具備的。
5.可適配,通過HandlerAdapter可以支持任意的類作爲處理器。
6.可定製性,HandlerMapping、ViewResolver等能夠非常簡單的定製。
7.功能強大的數據驗證、格式化、綁定機制。
8.利用Spring提供的Mock對象能夠非常簡單的進行Web層單元測試。
9.本地化、主題的解析的支持,使我們更容易進行國際化和主題的切換。
10.強大的JSP標籤庫,使JSP編寫更容易。
其他:還有比如RESTful風格的支持、簡單的文件上傳、約定大於配置的契約式編程支持、基於註解的零配置支持等等。
第三章: SpringMVC入門案例
新建一個maven骨架web工程
Maven依賴準備:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 編譯版本-->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<!-- Spring版本鎖定-->
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<!--SpringIOC-->
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
web.xml:
<servlet>
<!--配置spring mvc的核心控制器 -->
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置初始化參數,用於讀取SpringMVC的配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!--spring配置文件的地址-->
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--配置servlet的對象的創建時間點:應用加載時創建。 取值只能是非0正整數,表示啓動順序 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!--SpirngMVC-->
<servlet-name>dispatcherServlet</servlet-name>
<!--攔截所有請求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
配置jsp:
<a href="hello">hello,world</a>
配置控制器類:
//控制器
@Controller
public class HelloController {
/**
*
* @return
*/
@RequestMapping(path = "/hello")
public String sayHello() {
System.out.println("Hello StringMVC");
return "success";
}
}
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:context="http://www.springframework.org/schema/context"
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
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="cn.codewhite"/>
<!--視圖解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 默認到/WEB-INF/pages/下-->
<property name="prefix" value="/WEB-INF/pages/"></property>
<!-- 匹配後綴.jsp-->
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置Spring開啓MVC的支持,並且開啓自定義的類型轉換器-->
<mvc:annotation-driven ></mvc:annotation-driven>
</beans>
success.jsp地址:
success.jsp:
啓動測試!:
點擊!跳轉!
第四章:SpringMVC中的組件:
DispatcherServlet:前端控制器
用戶請求到達前端控制器,它就相當於mvc 模式中的c,dispatcherServlet 是整個流程控制的中心,由它調用其它組件處理用戶的請求,dispatcherServlet 的存在降低了組件之間的耦合性。
DispatcherServlet:前端控制器
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。
一般情況下需要通過頁面標籤或頁面模版技術將模型數據通過頁面展示給用戶,需要由程序員根據業務需求開發具體的頁面。
<mvc:annotation-driven>
說明
在SpringMVC的各個組件中,處理器映射器、處理器適配器、視圖解析器稱爲SpringMVC的三大組件。 使用
<mvc:annotation-driven>
自動加載RequestMappingHandlerMapping(處理映射器)和RequestMappingHandlerAdapter(處理適配器),可用在SpringMVC.xml配置文件中使用<mvc:annotation-driven>
替代註解處理器和適配器的配置。
第五章:SpringMVC常用註解:
@RequestMapping註解
源碼:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mappinga
public @interface RequestMapping {
}
作用:用於建立請求URL和處理請求方法之間的對應關係。
出現位置: 類上:
請求URL的第一級訪問目錄。此處不寫的話,就相當於應用的根目錄。寫的話需要以/開頭。 它出現的目的是爲了使我們的URL可以按照模塊化管理。
例如:賬戶模塊:
/user/login
/user/logout
/user/register
加粗的部分就是把RequsetMappding寫在類上,使我們的URL更加精細。
方法上: 請求URL的第二級訪問目錄。
此時請求地址需要寫成 /user/login
屬性:
value
:用於指定請求的URL。它和path屬性的作用是一樣的。
method
:用於指定請求的方式。(get,post,put,delete)
params
:用於指定限制請求參數的條件。它支持簡單的表達式。要求請求參數的key和value必須和配置的一模一樣。
此時鏈接地址必須有username
這個參數:
params
:其他用法:請求參數必須爲username=123
,否則其他404
@RequestParam註解
- 作用:把請求中的指定名稱的參數傳遞給控制器中的形參賦值
屬性
- value:請求參數中的名稱
- required:請求參數中是否必須提供此參數,默認值是true,必須提供
jsp:
<a href="anno/testRequestParam?name=xiaobai">requestParam</a>
此時控制器:由於形參不對應,所以username
賦值爲null 因爲:(name!=username)
第一次只進行結果:賦值失敗。
添加註解@RequestParam
:指定參數名稱:封裝成功。
點擊超鏈接結果: username封裝爲:xiaobai
@RequestBody註解
- 作用:用於獲取請求體內容。直接使用得到是key=value&key=value…結構的數據。注意:get方法不可以)
屬性
required
:是否必須有請求體,默認值是true
post請求jsp代碼: <!-- request body註解 -->
<form action="testRequestBody" method="post">
用戶名稱:<input type="text" name="username"><br/>
用戶密碼:<input type="password" name="password"><br/>
用戶年齡:<input type="text" name="age"><br/>
<input type="submit" value="保存">
</form>
控制器:
/**
* 測試請求體:@RequestBody
* @param body
* @return
*/
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String body) {
System.out.println("執行了SpringMVC");
System.out.println(body);
return "success";
}
post請求執行結果:
get請求結果: null
@PathVariable註解:
- 作用:擁有綁定
url
中的佔位符的。例如:url中有/delete/{id}
,{id}就是佔位符,涉及到restful設計風格 - 屬性
- value:指定url中的佔位符名稱
<a href="testPathVariable/10">PathVariable</a>
控制器:
/**
* 測試 @PathVariable
* @param id
* @return
*/
@RequestMapping("/testPathVariable/{id}")
public String testPathVariable(@PathVariable("id") String id) {
System.out.println("執行了SpringMVC");
System.out.println(id);
return "success";
}
運行結果:
@RequestHeader註解
作用: 用於獲取請求消息頭。
屬性: value:提供消息頭名稱 required:是否必須有此消息頭
注: 在實際開發中一般不怎麼用。
演示:jsp
<a href="testRequestHeader">testRequestHeader</a>
控制器:
/**
* 測試 @RequestHeader
*
* @param requestHeader
* @return
*/
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader("Accept-Language") String requestHeader) {
System.out.println("執行了SpringMVC");
System.out.println(requestHeader);
return "success";
}
運行結果:
@CookieValue註解
作用: 用於把指定cookie
名稱的值傳入控制器方法參數。
屬性: value
:指定cookie
的名稱。
required
:是否必須有此cookie。
jsp:
<a href="testCookieValue">testCookieValue</a>
控制器:
/**
* 測試 @CookieValue
*
* @param jessionid
* @return
*/
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String jessionid) {
System.out.println("執行了SpringMVC");
System.out.println(jessionid);
return "success";
}
結果:
@ModelAttribute註解
作用:
該註解是SpringMVC4.3
版本以後新加入的。它可以用於修飾方法和參數。
@ModelAttribute
註解作用在方法上或者方法的參數上,表示將被註解的方法的返回值或者是被註解的參數作爲Model的屬性加入到Model中,然後Spring框架自會將這個Model傳遞給ViewResolver。Model的生命週期只有一個http請求的處理過程,請求處理完後,Model就銷燬了
-
出現在方法上,表示當前方法會在控制器的方法執行之前,先執行。它可以修飾沒有返回值的方法,也可以修飾有具體返回值的方法。
-
出現在參數上,獲取指定的數據給參數賦值。
-
屬性: value:用於獲取數據的key。key可以是POJO的屬性名稱,也可以是map結構的key。
應用場景:當表單提交數據不是完整的實體類數據時,保證沒有提交數據的字段使用數據庫對象原來的數據。
例如:我們有一個表單:(他的請求封裝沒有用戶生日的提交表單信息)
<form action="testModelAttribute" method="post">
用戶姓名:<input type="text" name="uname" /><br/>
用戶年齡:<input type="text" name="age" /><br/>
<input type="submit" value="提交" />
</form>
User:
@Data
public class User implements Serializable {
private String uname;
private Integer age;
private Date birthday;
}
控制器:(由於表單沒有用戶生日,所以user裏的birthday屬性賦值不上)
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("執行了SpringMVC");
System.out.println(user);
return "success";
}
點擊提交:(由於表單沒有用戶生日,所以user裏的birthday屬性賦值不上)
此時在控制器裏,就需要使用@ModelAttribute
註解寫一個方法,對原有控制器的testModelAttribute方法
裏的user賦值
:(有返回值情況,自動補充user)
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
System.out.println("執行了SpringMVC");
System.out.println(user);
return "success";
}
/**
* 該方法會先執行,然後將返回的user,封裝到testModelAttribute()裏面的User
*
* @ModelAttribute
*/
@ModelAttribute
public User showUser(String uname) {
System.out.println("showUser執行了...");
// 通過用戶查詢數據庫(模擬)
User user = new User();
user.setUname(uname);
user.setAge(20);
user.setBirthday(new Date());
return user;
}
此時重新提交表單運行結果:(User封裝成功)
@ModelAttribute
註解沒返回值情況:通過Map
方式
/**
* @ModelAttribute("1")取的是map裏面的key
* @param user
* @return
*/
@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("1") User user) {
System.out.println("執行了SpringMVC");
System.out.println(user);
return "success";
}
/**
* Map裏存放了一個User
* @param uname
* @param map
*/
@ModelAttribute
public void showUser(String uname, Map<String,User> map){
System.out.println("showUser執行了...");
// 通過用戶查詢數據庫(模擬)
User user = new User();
user.setUname(uname);
user.setAge(20);
user.setBirthday(new Date());
map.put("1",user);
}
運行結果:
@SessionAttribute註解
作用: 用於多次執行控制器方法間的參數共享。(域對象)
屬性:
value
:用於指定存入的屬性名稱
type
:用於指定存入的數據類型。
jsp:
<!-- SessionAttribute註解的使用 -->
<a href="anno/testPut">存入SessionAttribute</a>
<hr/>
<a href="anno/testGet">取出SessionAttribute</a>
<hr/>
<a href="anno/testClean">清除SessionAttribute</a>
控制器代碼:
@Controller
@RequestMapping("/anno")
@SessionAttributes(value = {"username", "password", "age"}, types = {String.class, Integer.class})
// 把username,password,age都存入到session域對象中
public class AnnoController {
/**
* 把數據存入SessionAttribute
*
* Model是spring提供的一個接口,該接口有一個實現類ExtendedModelMap
*
* 該類繼承了ModelMap,而ModelMap就是LinkedHashMap子類
*/
@RequestMapping("/testPut")
public String testPut(Model model) {
//底層會存取到request域對象中,可以從jsp裏獲取request的域數據
model.addAttribute("username", "小白");
model.addAttribute("password", "123");
model.addAttribute("age", 18);
System.out.println("存入數據!");
//跳轉之前將數據保存到username、password和age中,
// 因爲註解@SessionAttribute中有這幾個參數
return "success";
}
/**
* 從session中獲取值
*
* @return
*/
@RequestMapping(path = "/testGet")
public String testGet(ModelMap modelMap) {
//獲取session域中的值
String username = (String) modelMap.get("username");
String password = (String) modelMap.get("password");
Integer age = (Integer) modelMap.get("age");
System.out.println("取出數據:" + username + " : " + password + " : " + age);
return "success";
}
/**
* 清除值
*
* @return
*/
@RequestMapping(path = "/testClean")
public String delete(SessionStatus status) {
System.out.println("清除了數據!");
//清除域中的值
status.setComplete();
return "success";
}
}
測試結果:
第六章:參數的綁定與解決參數亂碼
我們都知道,表單中請求參數都是基於key=value的。
SpringMVC綁定請求參數的過程是通過把表單提交請求參數,作爲控制器中方法參數進行綁定的。
封裝基本類型:
比如:參數是username=小白
jsp:
<a href="params/testParam?username=小白">測試參數綁定</a>
控制器:testparam是二級地址
@RequestMapping("/testParam")
public String testParam(String username){
System.out.println("用戶名:" + username);
return "success";
}
測試結果: 參數封裝成功!
封裝POJO類型參數
Account:
@Data
public class Account implements Serializable {
private String username;
private String password;
private Double money;
private User user;
}
User:
@Data
public class User implements Serializable {
private String uname;
private Integer age;
}
封裝規則:(表單)
控制器:
運行結果:
測試結果:
支持的數據類型:
基本類型參數: 包括基本類型和String類型
包括實體類,以及關聯的實體類
數組和集合類型參數封裝:包括List結構和Map結構的集合(包括數組)
實現效果:將User封裝到List和Map中
數組和集合類型參數封裝
實現效果:將User封裝到List和Map中
Account:
@Data
public class Account implements Serializable {
private String username;
private String password;
private Double money;
private User user;
private List<User> list;
private Map<String,User> map;
}
User:
@Data
public class User implements Serializable {
private String uname;
private Integer age;
}
表單:
<%--把數據封裝Account類中,類中存在list和map的集合--%>
<form action="params/saveAccount" method="post">
姓名:<input type="text" name="username" /><br/>
密碼:<input type="text" name="password" /><br/>
金額:<input type="text" name="money" /><br/>
<%-- 將User放入list集合list[index].value--%>
用戶姓名:<input type="text" name="list[0].uname" /><br/>
用戶年齡:<input type="text" name="list[0].age" /><br/>
<%--將User放入Map集合,map['key'].value--%>
用戶姓名:<input type="text" name="map['one'].uname" /><br/>
用戶年齡:<input type="text" name="map['one'].age" /><br/>
<input type="submit" value="提交" />
</form>
Controller:
@RequestMapping("/saveAccount")
public String saveAccount(Account account){
System.out.println(account);
return "success";
}
運行:
結果:
請求參數亂碼解決:
web.xml:
<!-- 解決Post請求亂碼,配置SpringMVC編碼過濾器-->
<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> <!-- 啓動過濾器 -->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<!-- 過濾所有請求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
在springmvc的配置文件中可以配置,靜態資源不過濾: <!-- location表示路徑,mapping表示文件,**表示該目錄下的文件以及子目錄的文件 -->
<mvc:resources location="/css/" mapping="/css/**"/>
<mvc:resources location="/images/" mapping="/images/**"/>
<mvc:resources location="/scripts/" mapping="/javascript/**"/>
get請求方式: tomacat對GET和POST請求處理方式是不同的,GET請求的編碼問題,要改tomcat的server.xml配置文件,如下:
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
改爲:
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"
useBodyEncodingForURI="true"/>
如果遇到ajax請求仍然亂碼,請把: useBodyEncodingForURI="true"改爲URIEncoding="UTF-8" 即可。
第七章:自定義類型轉換器
問題出現:封裝Date類型使用2020-10-10的格式無法封裝,只能使用2020/10/10的格式,那麼如何自定義格式來封裝呢?如下:
表單:
<%--自定義類型轉換器 --%>
<form action="params/saveUser" method="post">
用戶姓名:<input type="text" name="uname" /><br/>
用戶年齡:<input type="text" name="age" /><br/>
用戶生日:<input type="text" name="birthday" /><br/>
<input type="submit" value="提交" />
</form>
控制器:
@RequestMapping("/saveUser")
public String saveUser(User user) {
System.out.println(user);
return "success";
}
User:
@Data
public class User implements Serializable {
private String uname;
private Integer age;
private Date birthday;
}
測試正確格式封裝:
提交結果:(封裝成功)
若日期寫成:2020-12-12
則封裝失敗:
此時需要自定義類型轉換器:
1.表單提交的任何數據類型全部都是字符串類型,但是後臺定義Integer類型,數據也可以封裝上,說明
Spring框架內部會默認進行數據類型轉換。
- 如果想自定義數據類型轉換,可以實現Converter的接口
/**
* 把字符串轉換成日期的轉換器
* @create 2020-05-29 12:05
*/
public class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (source == null) {
throw new RuntimeException("參數不能爲空");
}
try {
//日期解析器
DateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = dataFormat.parse(source);
return date;
} catch (ParseException e) {
throw new RuntimeException("類型轉換錯誤");
}
}
}
- 註冊自定義類型轉換器,在springmvc.xml配置文件中編寫配置:
<!-- 註冊自定義類型轉換器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters" >
<set>
<bean class="cn.codewhite.contorller.StringToDateConverter"></bean>
</set>
</property>
</bean>
<!-- 配置Spring開啓MVC的支持,並且開啓自定義的類型轉換器-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
此時重新測試:
測試結果:封裝成功。
第八章: 在控制器中使用原生的ServletAPI對象
SpringMVC 還支持使用原始 ServletAPI 對象作爲控制器方法的參數。
支持原始 ServletAPI 對象有:
HttpServletRequest
HttpServletResponse
HttpSession
java.security.Principal
Locale
InputStream
OutputStream
Reader
Writer
- 只需要在控制器的方法參數定HttpServletRequestHttpServletResponse對象。
/**
* 原生的API
* @return
*/
@RequestMapping("/testServlet")
public String testServlet(HttpServletRequest request, HttpServletResponse response){
System.out.println("執行了...");
System.out.println(request);
HttpSession session = request.getSession();
System.out.println(session);
ServletContext servletContext = session.getServletContext();
System.out.println(servletContext);
System.out.println(response);
return "success";
}