springMVC:校驗框架:多規則校驗,巢狀校驗,分組校驗;ssm整合技術
知識點梳理
課堂講義
學習目標
-
能夠闡述表單驗證的分類和區別
-
能夠運用表單驗證的常用註解
-
能夠編寫表單驗證的示例
-
能夠編寫SSM整合的應用案例
-
能夠總結SSM整合的步驟
1 校驗框架
1.1 入門-視訊01
1.1.1 表單校驗的重要性
表單校驗保障了資料有效性、安全性
資料可以隨意輸入,導致錯誤的結果。
1.1.2 表單校驗分類
-
按校驗位置分類:
-
客戶端校驗:瀏覽器,手機APP
-
服務端校驗:後端伺服器程式碼校驗
-
-
按校驗內容分類:
-
格式校驗:比如年齡是否為負數,郵箱是否符合規則
-
邏輯校驗:比如使用者名稱在當前專案中是否已註冊(需要查詢資料庫或者業務校驗)
-
1.1.3 格式校驗規則
以下幾種全部屬於格式校驗:
-
長度:例如使用者名稱長度,評論字元數量
-
非法字元:例如使用者名稱組成
-
資料格式:例如Email格式、 IP地址格式
-
邊界值:例如轉賬金額上限,年齡上下限
1.1.4 表單校驗框架
-
JSR(Java Specification Requests):Java 規範提案
303:提供bean屬性相關校驗規則
-
JCP(Java Community Process):提出JSR規範議案
-
一個開放性的國際技術標準組織,職責是發展和更新Java技術規範。
-
成員:Oracle、ARM、IBM、Intel、SAP、Twitter、阿里巴巴、亞馬遜等
-
-
JSR規範列表
-
企業應用技術 Contexts and Dependency Injection for Java (Web Beans 1.0) (JSR 299) Dependency Injection for Java 1.0 (JSR 330)@postConstruct, @PreDestroy Bean Validation 1.0 (JSR 303) Enterprise JavaBeans 3.1 (includes Interceptors 1.1) (JSR 318) Java EE Connector Architecture 1.6 (JSR 322) Java Persistence 2.0 (JSR 317) Common Annotations for the Java Platform 1.1 (JSR 250) Java Message Service API 1.1 (JSR 914) Java Transaction API (JTA) 1.1 (JSR 907) JavaMail 1.4 (JSR 919)
-
Web應用技術 Java Servlet 3.0 (JSR 315) JavaServer Faces 2.0 (JSR 314) JavaServer Pages 2.2/Expression Language 2.2 (JSR 245) Standard Tag Library for JavaServer Pages (JSTL) 1.2 (JSR 52) Debugging Support for Other Languages 1.0 (JSR 45) 模組化 (JSR 294) Swing應用框架 (JSR 296) JavaBeans Activation Framework (JAF) 1.1 (JSR 925) Streaming API for XML (StAX) 1.0 (JSR 173)
-
管理與安全技術 Java Authentication Service Provider Interface for Containers (JSR 196) Java Authorization Contract for Containers 1.3 (JSR 115) Java EE Application Deployment 1.2 (JSR 88) J2EE Management 1.1 (JSR 77) Java SE中與Java EE有關的規範 JCache API (JSR 107) Java Memory Model (JSR 133) Concurrency Utilitie (JSR 166) Java API for XML Processing (JAXP) 1.3 (JSR 206) Java Database Connectivity 4.0 (JSR 221) Java Management Extensions (JMX) 2.0 (JSR 255) Java Portlet API (JSR 286)
-
-
Web Service技術 Java Date與Time API (JSR 310) Java API for RESTful Web Services (JAX-RS) 1.1 (JSR 311) Implementing Enterprise Web Services 1.3 (JSR 109) Java API for XML-Based Web Services (JAX-WS) 2.2 (JSR 224) Java Architecture for XML Binding (JAXB) 2.2 (JSR 222) Web Services Metadata for the Java Platform (JSR 181) Java API for XML-Based RPC (JAX-RPC) 1.1 (JSR 101) Java APIs for XML Messaging 1.3 (JSR 67) Java API for XML Registries (JAXR) 1.0 (JSR 93)
-
JSR303實現:校驗框架hibernate-validator
1.2 快速使用
匯入hibernate-validator校驗框架的座標
<!--匯入校驗的jsr303規範:介面--> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <!--匯入校驗框架實現技術--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>6.1.0.Final</version> </dependency>
注意: tomcat7 :搭配hibernate-validator版本5...Final tomcat8.5↑ :搭配hibernate-validator版本6...Final ,必須使用外接tomcat
1. 開啟校驗
名稱:@Valid 、 @Validated 型別:形參註解 位置:處理器類中的實體類型別的方法形參前方 作用:設定對當前實體類型別引數進行校驗 範例:
@RequestMapping(value = "/addemployee") public String addEmployee(@Valid Employee employee) { System.out.println(employee); return "success"; }
2.設定校驗規則
名稱:@NotNull, @NotBlank, @NotEmpty 型別:屬性註解 位置:屬性上方 作用:設定當前屬性校驗規則 範例:每個校驗規則所攜帶的引數不同,根據校驗規則進行相應的調整
public class Employee{ @NotBlank(message = "姓名不能為空") private String name;//員工姓名 //新增getter,setter方法 }
易錯點:必須給name,age等屬性編寫getter,setter方法
3.獲取錯誤資訊
通過形參Errors獲取校驗結果資料,通過Model介面將資料封裝後傳遞到頁面顯示
@RequestMapping(value = "/addemployee") public String addEmployee(@Valid Employee employee, Errors errors, Model model){ System.out.println(employee); if(errors.hasErrors()){ for(FieldError error : errors.getFieldErrors()){ model.addAttribute(error.getField(),error.getDefaultMessage()); } return "addemployee.jsp"; } return "success.jsp"; }
在JSP頁面中獲取後臺封裝的校驗結果資訊
<form action="/addemployee" method="post"> 員工姓名:<input type="text" name="name"><span style="color:red">${name}</span><br/> 員工年齡:<input type="text" name="age"><span style="color:red">${age}</span><br/> <input type="submit" value="提交"> </form>
使用校驗步驟小結
-
方法引數上開啟校驗:@Valid 、 @Validated
-
Bean的屬性上設定校驗規則:@NotBlank(message = "姓名不能為空")
-
通過引數Errors獲取錯誤資訊
-
獲取所有錯誤:errors.getFieldErrors()
-
獲取錯誤欄位:error.getField()
-
獲取錯誤提示:error.getDefaultMessage()
-
將錯誤資訊放置到model中顯示給使用者 -> JSP頁面中${name}
-
1.3 多規則校驗-視訊02
同一個欄位有多個約束條件,比如對於年齡的校驗:
if (age !=null && age >18 && age < 60)
@NotNull(message = "請輸入您的年齡") @Max(value = 60,message = "年齡最大值不允許超過60歲") @Min(value = 18,message = "年齡最小值不允許低於18歲") private Integer age;//員工年齡
1.4 巢狀校驗
引用型別欄位如何校驗,比如Employee中有address屬性記錄地址
名稱:@Valid 型別:屬性註解 位置:實體類中的引用型別屬性上方 作用:設定當前應用型別屬性中的屬性開啟校驗
1.在address屬性上新增@Valid註解,開啟巢狀屬性校驗
public class Employee { //實體類中的引用型別通過標註@Valid註解,設定開啟當前引用型別欄位中的屬性參與校驗 @Valid private Address address; //新增address屬性的get,set方法 }
2.開啟巢狀校驗後,被校驗物件內部需要新增對應的校驗規則,並新增巢狀屬性的get,set方法
//巢狀校驗的實體中,對每個屬性正常新增校驗規則即可 public class Address { @NotBlank(message = "請輸入省份名稱") private String provinceName;//省份名稱 @NotBlank(message = "請輸入城市名稱") private String cityName;//城市名稱 @NotBlank(message = "請輸入詳細地址") private String detail;//詳細住址 @NotBlank(message = "請輸入郵政編碼") @Size(max = 6, min = 6, message = "郵政編碼由6位組成") private String zipCode;//郵政編碼 //新增get,set方法 } @PostMapping(value = "/addemployee") public String addEmployee(@Valid Employee employee, Errors errors, Model m) { //Errors物件用於封裝校驗結果,如果不滿足校驗規則,對應的校驗結果封裝到該物件中,包含校驗的屬性名和校驗不通過返回的訊息 //判定Errors物件中是否存在未通過校驗的欄位 if (errors.hasErrors()) { //獲取所有未通過校驗規則的資訊 List<FieldError> fieldErrors = errors.getFieldErrors(); for (FieldError error : fieldErrors) { //將校驗結果資訊新增到Model物件中,用於頁面顯示,後期實際開發中無需這樣設定,返回json資料即可 //name = 姓名不能為空 -> ${name} m.addAttribute(error.getField(), error.getDefaultMessage()); //address.cityName = 請輸入城市名稱 -> ${requestScope['address.provinceName']} //m.addAttribute("address.provinceName", "請輸入省份名稱"); } //el獲取物件屬性值 Address address = new Address(); address.setProvinceName("test省份不能為空"); //${address.provinceName} m.addAttribute("address", address); //當出現未通過校驗的欄位時,跳轉頁面到原始頁面,進行資料回顯 return "addemployee.jsp"; } return "success.jsp"; }
3.在jsp頁面中獲取巢狀屬性中的錯誤資訊
${requestScope['address.cityName']} <%--注意,引用型別的校驗未通過資訊不是通過物件進行封裝的,直接使用物件名.屬性名的格式作為整體屬性字串進行儲存的,和使用者的屬性傳遞方式有關,不具有通用性,僅適用於本案例--%> 省:<input type="text" name="address.provinceName"><span style="color:red">${requestScope['address.provinceName']}</span><br/> 市:<input type="text" name="address.cityName"><span style="color:red">${requestScope['address.cityName']}</span><br/>
3種判定空校驗器的區別
String 選擇@NotBlank,底層會呼叫"字串".trim()清除前後空格
int, double 選擇@NotNull,不能使用@NotBlank
陣列選擇@NotEmpty
1.5 分組校驗
根據業務不同需要調整是否參與校驗,比如同一個模組,新增和修改時校驗規則是不一致的
-
新增使用者:新增時使用者名稱不能為空
-
修改使用者:修改時不能修改使用者名稱,所以不用強制使用者名稱不能為空
解決方案:對不同種類的屬性進行分組,在校驗時可以指定參與校驗的欄位所屬的組類別
-
根據業務型別使用介面定義分組為Save, Update
public interface Save { } public interface Update { }
-
在實體類Employee中為屬性設定所屬組,可以設定多個
姓名不能為空只在新增時校驗
@NotEmpty(message = "姓名不能為空",groups = {Save.class}) private String name;//員工姓名
年齡校驗新增和修改都需要校驗
@NotEmpty(message = "年齡不能為空",groups = {Save.class, Update.class}) private Integer age;//員工年齡
-
在Controller的方法引數上開啟分組校驗,並指定使用哪一種分組:@Validated({Save.class})
//新增Employee @RequestMapping("/addemployee") public String addEmployee(@Validated({Save.class}) Employee employee, Errors errors, Model m) { } //更新Employee @PostMapping(value = "/updateEmployee") public String updateEmployee(@Validated({Update.class}) EmployeeGroup employee, Errors errors, Model m) { }
2 SSM整合
2.1 整合流程簡介-視訊03
整合步驟分析:SSM(Spring+SpringMVC+MyBatis)
表結構
2.2 專案結構搭建-視訊04
Part0:專案基礎結構搭建
-
建立專案,組織專案結構,建立包
-
建立表與實體類
-
建立三層架構對應的模組、介面與實體類,建立關聯關係
-
資料層介面(代理自動建立實現類)
-
業務層介面+業務層實現類
-
表現層類
-
public interface UserDao { /** * 新增使用者 * @param user * @return */ public boolean save(User user); /** * 修改使用者 * @param user * @return */ public boolean update(User user); /** * 刪除使用者 * @param uuid * @return */ public boolean delete(Integer uuid); /** * 查詢單個使用者資訊 * @param uuid * @return */ public User get(Integer uuid); /** * 查詢全部使用者資訊 * @return */ public List<User> getAll(); /** * 根據使用者名稱密碼查詢個人資訊 * @param userName 使用者名稱 * @param password 密碼資訊 * @return */ //注意:資料層操作不要和業務層操作的名稱混淆,通常資料層僅反映與資料庫間的資訊交換,不體現業務邏輯 public User getByUserNameAndPassword(String userName,String password); } public interface UserService { /** * 新增使用者 * @param user * @return */ public boolean save(User user); /** * 修改使用者 * @param user * @return */ public boolean update(User user); /** * 刪除使用者 * @param uuid * @return */ public boolean delete(Integer uuid); /** * 查詢單個使用者資訊 * @param uuid * @return */ public User get(Integer uuid); /** * 查詢全部使用者資訊 * @return */ public List<User> getAll(); /** * 根據使用者名稱密碼進行登入 * @param userName * @param password * @return */ public User login(String userName,String password); }
2.3 Spring整合Mybatis(複習)
在pom.xml引入相關座標
<dependencies> <!--spring+springmvc環境環境--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <!--mybatis環境--> <!--mybatis環境--> <!--mybatis環境--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency> <!--mysql環境--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--spring整合jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <!--spring整合mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.3</version> </dependency> <!--druid連線池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <!--分頁外掛座標--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.2</version> </dependency> <!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.0</version> </dependency> <!--其他元件--> <!--其他元件--> <!--其他元件--> <!--junit單元測試--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--spring整合junit--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.9.RELEASE</version> </dependency> </dependencies>
2.3.1 Spring整合Mybatis-視訊05
-
建立Spring配置檔案:元件掃描
-
建立MyBatis對映檔案
-
整合MyBatis到Spring環境中
-
SqlSesionFactoryBean
-
資料來源(druid+jdbc.properties)
-
對映掃描
-
-
Spring環境配置:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--開啟bean註解掃描--> <context:component-scan base-package="com.itheima"/> </beans>
-
MyBatis對映檔案:UserDao.xml 注意目錄:resources\com\itheima\dao\UserDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itheima.dao.UserDao"> <!--新增--> <insert id="save" parameterType="user"> insert into user(userName,password,realName,gender,birthday)values(#{userName},#{password},#{realName},#{gender},#{birthday}) </insert> <!--刪除--> <delete id="delete" parameterType="int"> delete from user where uuid = #{uuid} </delete> <!--修改--> <update id="update" parameterType="user"> update user set userName=#{userName},password=#{password},realName=#{realName},gender=#{gender},birthday=#{birthday} where uuid=#{uuid} </update> <!--查詢單個--> <select id="get" resultType="user" parameterType="int"> select * from user where uuid = #{uuid} </select> <!--分頁查詢--> <select id="getAll" resultType="user"> select * from user </select> <!--登入--> <select id="getByUserNameAndPassword" resultType="user" > select * from user where userName=#{userName} and password=#{password} </select> </mapper>
在UserDao中完成登入方法引數繫結:
/** * 根據使用者名稱密碼查詢個人資訊 * * @param userName 使用者名稱 * @param password 密碼資訊 * @return */ //注意:資料層操作不要和業務層操作的名稱混淆,通常資料層僅反映與資料庫間的資訊交換,不體現業務邏輯 public User getByUserNameAndPassword( @Param("userName") String userName, @Param("password") String password);
-
整合MyBatis到Spring環境中:applicationContext.xml
-
資料來源(druid+jdbc.properties)
-
SqlSesionFactoryBean
-
對映掃描MapperScannerConfigurer
<!--載入properties檔案--> <context:property-placeholder location="classpath*:jdbc.properties"/> <!--資料來源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!--整合mybatis到spring中--> <bean class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.itheima.domain"/> </bean> <!--對映掃描--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.itheima.dao"/> </bean>
-
2.3.2 配置分頁外掛與事務-視訊06
-
新增分頁外掛pagehelper座標
<!--分頁外掛座標--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.2</version> </dependency>
-
配置分頁外掛pagehelper:applicationContext.xml
-
<property name="plugins"> -> setPlugins()
-
<property name="properties"> -> setProperties()
<!--整合mybatis到spring中--> <bean class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="typeAliasesPackage" value="com.itheima.domain"/> <!--分頁外掛--> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageInterceptor"> <property name="properties"> <props> <prop key="helperDialect">mysql</prop> <!--pageNum<=0 時會查詢第一頁,pageNum>pageSize(超過總數時),會查詢最後一頁--> <prop key="reasonable">true</prop> </props> </property> </bean> </array> </property> </bean>
-
-
在UserServiceImpl中完善分頁查詢
@Override public PageInfo<User> getAll(int page, int size) { PageHelper.startPage(page, size); List<User> all = userDao.getAll(); return new PageInfo<User>(all); }
-
新增事務管理:applicationContext.xml
-
確認已新增tx名稱空間
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
配置事務管理器
-
<!--開啟註解式事務--> <tx:annotation-driven transaction-manager="txManager"/> <!--事務管理器--> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
-
在Service類上或方法上新增@Transactional註解
//類或介面上 @Transactional(readOnly = true) //增刪改方法上 @Transactional(readOnly = false)
-
2.4 整合junit(複習)-視訊07
測試環境使用單獨的資料庫配置:在test\resources放置一套applicationContext.xml和jdbc.properties
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class UserServiceTest { @Autowired private UserService userService; @Test public void testSave(){ User user = new User(); user.setUserName("Jock"); user.setPassword("root"); user.setRealName("Jockme"); user.setGender(1); user.setBirthday(new Date(333333000000L)); userService.save(user); } @Test public void testDelete(){ User user = new User(); userService.delete(3); } @Test public void testUpdate(){ User user = new User(); user.setUuid(1); user.setUserName("Jockme"); user.setPassword("root"); user.setRealName("JockIsMe"); user.setGender(1); user.setBirthday(new Date(333333000000L)); userService.update(user); } @Test public void testGet(){ User user = userService.get(1); System.out.println(user); } @Test public void testGetAll(){ PageInfo<User> all = userService.getAll(2, 2); System.out.println(all); System.out.println(all.getList().get(0)); System.out.println(all.getList().get(1)); } @Test public void testLogin(){ User user = userService.login("Jockme", "root"); System.out.println(user); } }
Spring整合MyBatis小結
2.5 Spring整合SpringMVC
2.5.1 Rest風格開發SpringMVC(複習)-視訊08
Part3:配置SpringMVC,完成如下4個步驟:
-
在web.xml中配置DispatcherServlet,並載入spring-mvc.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/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
在spring-mvc.xml中配置只掃描com.itheima.controller包
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" 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"> <mvc:annotation-driven/> <context:component-scan base-package="com.itheima.controller"/> </beans>
-
Spring的配置檔案不載入controller:修改applicationContext.xml的包掃描,排除@Controller標記的類
<!--開啟bean註解掃描--> <context:component-scan base-package="com.itheima"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
-
編寫UserController
@RestController @RequestMapping("/user") public class UserController { @PostMapping public boolean save(User user){ System.out.println("save ..." + user); return true; } @PutMapping public boolean update(User user){ System.out.println("update ..." + user); return true; } @DeleteMapping("/{uuid}") public boolean delete(@PathVariable Integer uuid){ System.out.println("delete ..." + uuid); return true; } @GetMapping("/{uuid}") public User get(@PathVariable Integer uuid){ System.out.println("get ..." + uuid); return null; } @GetMapping("/{page}/{size}") public List getAll(@PathVariable Integer page,@PathVariable Integer size){ System.out.println("getAll ..." + page+","+size); return null; } @PostMapping("/login") public User login(String userName,String password){ System.out.println("login ..." + userName + " ," +password); return null; } }
2.5.2 Spring整合SpringMVC-視訊09
使用Spring時載入applicationContext.xml方式如下所示:
Part4:改為Web專案後,必須讓tomcat啟動時通過監聽器載入Spring的配置檔案:applicationContext.xml
-
web.xml載入Spring配置檔案
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:applicationContext.xml</param-value> </context-param> <!--啟動伺服器時,通過監聽器載入spring執行環境--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
-
Controller呼叫Service
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @PostMapping public boolean save(User user){ return userService.save(user); } @PutMapping public boolean update(User user){ return userService.update(user); } @DeleteMapping("/{uuid}") public boolean delete(@PathVariable Integer uuid){ return userService.delete(uuid); } @GetMapping("/{uuid}") public User get(@PathVariable Integer uuid){ return userService.get(uuid); } @GetMapping("/{page}/{size}") public PageInfo<User> getAll(@PathVariable Integer page, @PathVariable Integer size){ return userService.getAll(page,size); } @PostMapping("/login") public User login(String userName,String password){ return userService.login(userName,password); } }
2.6 表現層資料封裝(重點)
Part5-1:表現層資料封裝
操作型別 | 返回資料 | 格式 |
---|---|---|
操作是否成功 | true/false | 格式A |
單個數據 | 1, "zhangshan" | 格式B |
物件資料 | json物件:{"name":"Jock","age":39} | 格式C |
集合資料 | json陣列:[{"name":"Jock","age":39},{"name":"Jockme","age":40}] | 格式D |
下面進行表現層資料封裝,分為如下4個步驟:
-
返回資料格式設計
-
編寫返回資料封裝類:Result,新增get,set方法
public class Result { // 操作結果編碼 private Integer code; // 操作資料結果 private Object data; // 訊息 private String message; //新增get,set方法 public Result(Integer code) { this.code = code; } public Result(Integer code, Object data) { this.code = code; this.data = data; } public Result(Integer code, String message) { this.code = code; this.message = message; } }
-
狀態碼常量可以根據自己的業務需求設定
public class Code { // 操作結果編碼 public static final Integer SAVE_OK = 20011; public static final Integer UPDATE_OK = 20021; public static final Integer DELETE_OK = 20031; public static final Integer GET_OK = 20041; public static final Integer SAVE_ERROR = 20010; public static final Integer UPDATE_ERROR = 20020; public static final Integer DELETE_ERROR = 20030; public static final Integer GET_ERROR = 20040; // 系統錯誤編碼 // 操作許可權編碼 // 校驗結果編碼 }
-
Controller 呼叫
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @PostMapping public Result save(User user){ boolean flag = userService.save(user); return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERROR); } @PutMapping public Result update(User user){ boolean flag = userService.update(user); return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERROR); } @DeleteMapping("/{uuid}") public Result delete(@PathVariable Integer uuid){ boolean flag = userService.delete(uuid); return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERROR); } @GetMapping("/{uuid}") public Result get(@PathVariable Integer uuid){ User user = userService.get(uuid); return new Result(null != user ?Code.GET_OK: Code.GET_ERROR,user); } @GetMapping("/{page}/{size}") public Result getAll(@PathVariable Integer page, @PathVariable Integer size){ PageInfo<User> all = userService.getAll(page, size); return new Result(null != all ?Code.GET_OK: Code.GET_ERROR,all); } @PostMapping("/login") public Result login(String userName,String password){ User user = userService.login(userName,password); return new Result(null != user ?Code.GET_OK: Code.GET_ERROR,user); } }
2.7 自定義異常-視訊11
Part5-2:自定義異常處理
-
設定自定義業務異常,封裝程式執行過程中出現的問題,便於表現層進行統一的異常攔截並進行處理
在自定義異常類中新增錯誤編碼屬性code以及get方法
public class BusinessException extends RuntimeException { //自定義異常中封裝對應的錯誤編碼,用於異常處理時獲取對應的操作編碼 private Integer code; public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public BusinessException(Integer code) { this.code = code; } public BusinessException(String message, Integer code) { super(message); this.code = code; } public BusinessException(String message, Throwable cause,Integer code) { super(message, cause); this.code = code; } public BusinessException(Throwable cause,Integer code) { super(cause); this.code = code; } public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace,Integer code) { super(message, cause, enableSuppression, writableStackTrace); this.code = code; } }
-
自定義異常訊息返回時需要與業務正常執行的訊息按照統一的格式進行處理
@GetMapping("/{uuid}") public Result get(@PathVariable Integer uuid){ User user = userService.get(uuid); //模擬出現異常,使用條件控制,便於測試結果 if (uuid == 10 ) throw new BusinessException("查詢出錯啦,請重試!",Code.GET_ERROR); return new Result(null != user ? Code.GET_OK: Code.GET_ERROR,user); }
2.8 返回訊息相容異常資訊(重點)
在異常處理類中,將異常資訊封裝成Result物件:通過e.getCode()獲取異常編碼
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler(BusinessException.class) //對出現異常的情況進行攔截,並將其處理成統一的頁面資料結果格式 public Result doBusinessException(BusinessException e) { return new Result(e.getCode(), e.getMessage()); } @ExceptionHandler(Exception.class) public Result doOtherException(Exception e) { //redis傳送真正的異常資訊給運維人員e.getMessage() return new Result(500, "請稍候重試"); } }
3 純註解開發SSM
需要使用註解改造的配置檔案如下所示:
-
UserDao.xml
-
applicationContext.xml
-
spring-mvc.xml
-
web.xml
3.1 用註解代替UserDao.xml-視訊12
使用MyBatis提供的註解在UserDao介面中改造
public interface UserDao { /** * 新增使用者 * @param user * @return */ @Insert("insert into user(userName,password,realName,gender,birthday)values(#{userName},#{password},#{realName},#{gender},#{birthday})") public boolean save(User user); /** * 修改使用者 * @param user * @return */ @Update("update user set userName=#{userName},password=#{password},realName=#{realName},gender=#{gender},birthday=#{birthday} where uuid=#{uuid}") public boolean update(User user); /** * 刪除使用者 * @param uuid * @return */ @Delete("delete from user where uuid = #{uuid}") public boolean delete(Integer uuid); /** * 查詢單個使用者資訊 * @param uuid * @return */ @Select("select * from user where uuid = #{uuid}") public User get(Integer uuid); /** * 查詢全部使用者資訊 * @return */ @Select("select * from user") public List<User> getAll(); /** * 根據使用者名稱密碼查詢個人資訊 * @param userName 使用者名稱 * @param password 密碼資訊 * @return */ @Select("select * from user where userName=#{userName} and password=#{password}") //注意:資料層操作不要和業務層操作的名稱混淆,通常資料層僅反映與資料庫間的資訊交換,不體現業務邏輯 public User getByUserNameAndPassword(@Param("userName") String userName,@Param("password") String password); }
易錯點:刪除UserDao.xml
3.2 用註解替代applicationContext.xml
共分為如下3個步驟:
-
新建Druid資料來源配置類:JdbcConfig
public class JdbcConfig { //使用注入的形式,讀取properties檔案中的屬性值,等同於<property name="*******" value="${jdbc.driver}"/> @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String userName; @Value("${jdbc.password}") private String password; //定義dataSource的bean,等同於<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> @Bean("dataSource") public DataSource getDataSource(){ //建立物件 DruidDataSource ds = new DruidDataSource(); //手工呼叫set方法,等同於set屬性注入<property name="driverClassName" value="******"/> ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } }
-
新建MyBatis配置類:MyBatisConfig,新增分頁外掛pageHelper配置
public class MyBatisConfig { //定義MyBatis的核心連線工廠bean,等同於<bean class="org.mybatis.spring.SqlSessionFactoryBean"> @Bean //引數使用自動裝配的形式載入dataSource,為set注入提供資料,dataSource來源於JdbcConfig中的配置 public SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource,@Autowired Interceptor interceptor){ SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); //等同於<property name="typeAliasesPackage" value="com.itheima.domain"/> ssfb.setTypeAliasesPackage("com.itheima.domain"); //等同於<property name="dataSource" ref="dataSource"/> ssfb.setDataSource(dataSource); ssfb.setPlugins(interceptor); return ssfb; } //定義MyBatis的對映掃描,等同於<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> @Bean public MapperScannerConfigurer getMapperScannerConfigurer(){ MapperScannerConfigurer msc = new MapperScannerConfigurer(); //等同於<property name="basePackage" value="com.itheima.dao"/> msc.setBasePackage("com.itheima.dao"); return msc; } //分頁外掛pageHelper配置 @Bean("pageInterceptor") public Interceptor getPageInterceptor(){ Interceptor interceptor = new PageInterceptor(); Properties properties = new Properties(); properties.setProperty("helperDialect","mysql"); properties.setProperty("reasonable","true"); //等同於<property name="properties"> interceptor.setProperties(properties); return interceptor; } }
-
新建Spring配置類:SpringConfig,完成事務管理配置
@Configuration //等同於<context:component-scan base-package="com.itheima"> @ComponentScan(value = "com.itheima",excludeFilters = //等同於<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> @ComponentScan.Filter(type= FilterType.ANNOTATION,classes = {Controller.class})) //等同於<context:property-placeholder location="classpath*:jdbc.properties"/> @PropertySource("classpath:jdbc.properties") //等同於<tx:annotation-driven />,bean的名稱預設取transactionManager @EnableTransactionManagement @Import({MyBatisConfig.class,JdbcConfig.class}) public class SpringConfig { //等同於<bean id="txManager"/> @Bean("transactionManager") //等同於<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> public DataSourceTransactionManager getTxManager(@Autowired DataSource dataSource){ DataSourceTransactionManager tm = new DataSourceTransactionManager(); //等同於<property name="dataSource" ref="dataSource"/> tm.setDataSource(dataSource); return tm; } }
3.3 用註解替代spring-mvc.xml
-
新增@EnableWebMvc註解
@Configuration //等同於<context:component-scan base-package="com.itheima.controller"/> @ComponentScan("com.itheima.controller") //等同於<mvc:annotation-driven/>,還不完全相同 @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { //解決@ResponseBody返回中文亂碼問題,指定編碼格式為UTF-8 converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); //將Java物件轉成JSON格式字串格式 converters.add(new MappingJackson2HttpMessageConverter()); } }
替換XML中@ResponseBody返回中文亂碼的方式:
-
EnableWebMvc作用(瞭解)
-
支援ConversionService的配置,可以方便配置自定義型別轉換器
-
支援@NumberFormat註解格式化數字型別
-
支援@DateTimeFormat註解格式化日期資料,日期包括Date,Calendar,JodaTime(JodaTime要導包)
-
支援@Valid的引數校驗(需要匯入JSR-303規範)
-
配合第三方jar包和SpringMVC提供的註解讀寫XML和JSON格式資料
-
3.4 用註解替代web.xml
-
新建Servlet容器初始化配置類:ServletContainersInitConfig
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //建立Servlet容器時,使用註解的方式載入SPRINGMVC配置類中的資訊,並載入成WEB專用的ApplicationContext物件 //該物件放入了ServletContext範圍,後期在整個WEB容器中可以隨時獲取呼叫 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } //註解配置對映地址方式,服務於SpringMVC的核心控制器DispatcherServlet @Override protected String[] getServletMappings() { return new String[]{"/"}; } //亂碼處理作為過濾器,在servlet容器啟動時進行配置,相關內容參看Servlet零配置相關課程 @Override public void onStartup(ServletContext servletContext) throws ServletException { //觸發父類的onStartup super.onStartup(servletContext); //1.建立字符集過濾器物件 CharacterEncodingFilter cef = new CharacterEncodingFilter(); //2.設定使用的字符集 cef.setEncoding("UTF-8"); //3.新增到容器(它不是ioc容器,而是ServletContainer) FilterRegistration.Dynamic registration = servletContext.addFilter("characterEncodingFilter", cef); //4.新增對映 registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), false, "/*"); //使用隱藏域設定請求方式 //指定form表單請求方式為DELETE: <input type="text" name="_method" value="DELETE"/> //HiddenHttpMethodFilter httpMethodFilter = new HiddenHttpMethodFilter(); //FilterRegistration.Dynamic httpMethodFilterRegistration = // servletContext.addFilter( // "hiddenHttpMethodFilter", cef); //httpMethodFilterRegistration.addMappingForServletNames( // EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE), // false, // getServletName()); } }
-
新增載入spring核心配置檔案,生成Spring核心容器(主容器/父容器/根容器)
@Override //基本等同於<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx; }
-
父容器:Spring環境載入後形成的容器,包含spring環境下所有的bean,配置檔案和Service層的bean
-
子容器:當前springmvc環境載入後形成的容器,不包含spring環境下的bean,只包含Controller層的bean
-
子容器可以訪問父容器中的資源,父容器不可以訪問子容器中的資源
-
易錯點:ServletContainersInitConfig類和web.xml只能保留一個