ssm: MyBatis + Spring + SpringMVCjavascript
Model(模型):數據類型,提供要展現的數據,所以包含數據和行爲,能夠認爲是領域模型或 JavaBean 組件(包含數據和行爲)。不過如今通常都分離開來: Value Object(數據 Dao)和服務層(行爲 Service)。也就是模型提供了模型數據查詢和模型數據的狀態更新等功能,包括數據和業務。css
View(視圖):負責進行模型的展現,通常就是咱們見到的用戶界面,客戶想看到的東西。html
Controller(控制器):接收用戶請求,委託給模型進行處理(狀態改變)。處理完畢後把返回的模型數據返回給視圖,由視圖負責展現。也就是說控制器作了個調度員的工做。前端
最典型的MVC 就是 JSP + Servlet + JavaBean 的模式vue
在web早期的開發中,一般採用的是 Model1。java
Model1中,主要分爲兩層,視圖層和模型層mysql
Model1 的優勢:架構簡單,比較適合小型項目的開發。react
Model1 的缺點:JSP 職責不單一,職責太重,不便於維護。jquery
Model2 把一個項目分紅三部分,包括視圖、控制、模型。程序員
職責分析:
Controller:控制器
獲取表單數據
調用業務邏輯
轉向指定的頁面
Model:模型
View:視圖
Model1 和 Model2 的對比:
Model2 這樣不只提升了代碼的複用率與項目的拓展性,並且大大下降了項目的維護成本。
Model1模式的實現比較簡單,適用於快速開發小規模項目,Model1 中的 JSP 頁面身兼 View 和 Controller 兩種角色,將控制邏輯和表現邏輯混雜在一塊兒,從而致使代碼的重用性很是低,增長了應用的拓展性和維護的難度。Model2 消除了 Model1 的缺點。
新建一個 Maven 工程看成父工程,並在 pom.xml 中導入須要的依賴
<dependencies> <!-- junit:測試 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- SpringMvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!-- 開啓Servlet支持 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!-- 開啓jsp支持 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> </dependencies>
創建一個 Module:SpringMVC-01-Servlet ,添加 Web app的支持
編寫一個Servlet類:HelloServlet
public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 獲取前端參數 String method = req.getParameter("method"); if ("add".equals(method)){ req.getSession().setAttribute("msg","執行了add方法"); } if ("delete".equals(method)){ req.getSession().setAttribute("msg","執行了delete方法"); } // 調用業務層 // 視圖轉發或重定向 req.getRequestDispatcher("WEB-INF/jsp/test.jsp").forward(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
在 web.xml 中配置 servlet
<servlet> <servlet-name>hello</servlet-name> <servlet-class>servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
配置 Tomcat 服務器,並將工程項目打包發佈
直接用 url 請求 Servlet
http://localhost:8888/hello?method=add http://localhost:8888/hello?method=delete
MVC 框架要作哪些事情
說明:
常見的服務端 MVC 框架: Struts、Spring MVC、ASP.NET MVC、Zend Frameword、JSF;
常見前端 MVC 框架:vue、angularjs、react、backbone;
由 MVC 演化出了另一些模式入:MVP、MVVM 等等
Spring MVC 是 Spring Framework 的一部分,是基於 Java 實現 MVC 的輕量級 Web 框架。
官方文檔地址:https://docs.spring.io/spring/docs/5.3.0-SNAPSHOT/spring-framework-reference/web.html#spring-web
咱們爲何要學習 Spring MVC呢?
Spring MVC 的特色:
正是由於 SpringMVC 好,簡單,邊界,易學,天生和 Spring 無縫集成(使用 SpringIoC 和 Aop),使用約定優於配置,可以進行簡單的 junit 測試,支持 Restful 風格,異常處理,本地化,國際化,數據驗證,類型轉換,攔截器等等,因此咱們要學習。
最重要的一點仍是用的人多,使用的公司多。
Spring 的 web 框架圍繞 DispatcherServlet【調度Servlet】設計
DispatcherServlet 的做用是將請求分發到不一樣的處理器。從 Spring 2.5 開始,使用 Java 5 或者以上版本的用戶能夠採用基於註解形式進行開發,十分簡潔,用戶能夠採用基於註解的 controller 聲明方式。
Spring MVC 框架像許多其它 MVC 框架同樣,以請求爲驅動,圍繞一箇中心 Servlet 分派請求及提供其它功能,DispatcherServlet 是一個實際的 Servlet (它繼承自 HttpServlet 基類)。
(圖片引自博客 https://blog.csdn.net/licwzy/article/details/81875635)
SpringMVC 的原理以下圖所示:
當發起請求時被前置的控制器攔截到請求,根據請求參數生成代理請求,找到請求對應的實際控制器,控制器處理請求,建立數據模型,訪問數據庫,將模型響應給中心控制器,控制器使用模型與視圖渲染視圖結果,將結果返回給中心控制器。再將結果返回給請求者。
(圖引自狂神說公衆號)
圖爲 SpringMVC 的一個較完整的流程圖,實線表示 SpringMVC框架提供的技術,不須要開發者實現,虛線表示須要開發者實現。
須要分析執行流程
DispatcherServlet 表示前置控制器,是整個 SpringMVC 的控制中心。用戶發出請求,DispatcherServlet 接收請求並攔截請求。
咱們假設請求的url爲:http://localhost:8080/SpringMVC/hello
如上url拆分紅三部分:
http://localhost:8080:服務器域名
SpringMVC: 部署在服務器上的 web 站點
hello: 表示控制器
經過分析,如上url表示爲:請求位於http://localhost:8080 上的 SpringMVC 站點的 hello 控制器
HandlerMapping 爲處理器映射。DispatcherServlet 調用HandelerMapping,HandlerMapping 根據請求 url 查找 Handler。
HandlerExecution表示具體的 Handler,其主要做用是根據 url 查找控制器,如上 url 被查找控制器爲 :hello。
HandlerExecution 將解析後的信息傳遞給 DispatcherServlet,如解析控制器映射等。
HandlerAdapter 表示處理器適配器,其按照特定的規則去執行 Handler。
Handler 讓具體的 Controller 執行。
Controller 將具體的執行信息返回給 HandlerAdapter ,如 ModelAndView。
HandlerAdapter 將視圖邏輯名或模型傳遞給 DispatcherServlet。
DispatcherServlet 調用視圖解析器(ViewResolver)來解析 HandlerAdapter 傳遞的邏輯視圖名。
視圖解析器將解析的邏輯視圖名傳給 DispatcherServlet。
視圖解析器將解析的邏輯視圖結果,調用具體的視圖。
最總視圖呈現給用戶。
新建一個Moudle:SpringMVC-02-HelloMVC,添加 web 支持
導入SpringMVC依賴
配置 web.xml,註冊 DispatcherServlet
<?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_4_0.xsd" version="4.0"> <!-- 註冊DispatcherServlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 關聯一個SpringMVC配置文件:【servlet-name】 springmvc-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/springmvc-servlet.xml</param-value> </init-param> <!-- 啓動級別:1 --> <load-on-startup>1</load-on-startup> </servlet> <!-- / :匹配全部的請求(不包括.jsp) --> <!-- /* :匹配全部的請求(包括.jsp) --> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
編寫咱們要操做的業務 Controller,要麼實現 Controller 接口,要麼增長註解;須要返回一個 ModelAndView ,裝數據,封視圖
public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { // 建立 ModelAndView 對象 ModelAndView mv = new ModelAndView(); // 封裝對象,放在 ModelAndView 中,Model mv.addObject("msg","HelloSpringMVC!"); // 封裝要跳轉的視圖,放在 ModelAndView 中,至關於 /WEB-INF/jsp/hello.jsp mv.setViewName("hello"); return mv; } }
編寫 SpringMVC 配置文件:springmvc-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" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 處理映射器 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- 處理器適配器 --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" /> <!--視圖解析器:DispatcherServlet給他的ModelAndView--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <!-- 前綴 --> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 後綴 --> <property name="suffix" value=".jsp"/> </bean> <!-- 跳轉的視圖 --> <bean id="/hello" class="controller.HelloController"/> </beans>
建立須要跳轉的 jsp 頁面,並顯示 ModelAndView 中存放的數據
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>
配置 Tomcat 並啓動測試
在 url 地址欄中輸入 http://localhost:8080/hello
可能遇到的問題,訪問出現404,排查步驟:
上面配置版的 SpringMVC項目,能更好地理解 SpringMVC 的原理。可是,在咱們實際開發中並不會這麼寫。而是使用註解進行開發!
新建一個Moudle:SpringMVC-03-Hello-Annotation,添加 web 支持
導入依賴,並解決 Maven 資源過濾問題
<!-- 解決 Maven 資源過濾問題 --> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
建立 SpringMVC 配置文件
在 resource 目錄下建立個 config 文件夾,並添加springmvc-servlet.xml 配置文件,配置的形式與 Spring 容器配置基本相似,爲了支持基於註解的 IoC,配置了自動掃描包的功能,
配置步驟:
具體配置信息以下:
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 自動掃描包,讓指定包下的註解生效,由 IoC 容器同一管理 --> <context:component-scan base-package="com.xp.controller"/> <!-- 讓 SpringMVC 處理靜態資源 --> <mvc:default-servlet-handler/> <!-- 支持mvc註解驅動 在 Spring 中通常採用 @RequestMapping 註解來完成映射關係 要想使 @RequestMapping 註解生效 必須向上下文中註冊 DefaultAnnotationHandlerMapping 和一個 AnnotationMethodHandlerAdapter實例 這兩個實例分別在類級別和方法級別處理 而 annotation-driver 配置幫助咱們自動完成上述兩個實例的注入 --> <mvc:annotation-driven/> <!-- 視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <!-- 前綴 --> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 後綴 --> <property name="suffix" value=".jsp"/> </bean> </beans>
在視圖解析器中,咱們把全部的視圖都存放在 /WEB-INF/
目錄下,這樣能夠保證視圖安全,由於這個目錄下的文件,客戶端不能直接訪問
配置 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_4_0.xsd" version="4.0"> <!-- 註冊 DispatcherServlet註解 --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
/ 和 / 的區別:*
< url-pattern > / </ url-pattern > 不會匹配到.jsp
,只針對咱們編寫的請求;即.jsp
不會進入 Spring 的 DispatcherServlet 類。
< url-pattern > /* </ url-pattern > 會匹配*.jsp
,會出現返回 JSP 視圖時再次進入 Spring 的 DispatcherServlet 類,致使找不到對應的 controller 因此報404錯。
建立視圖層 hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${msg} </body> </html>
建立 Controller
編寫一個 Java 控制類:HelloController
@Controller @RequestMapping("hello") public class HelloController { // 真實的訪問地址:項目名/hello/h1 @RequestMapping("h1") public String hello(Model model){ // 向模型中添加屬性msg與值,能夠在JSP頁面中取出並渲染 model.addAttribute("msg","HelloSpringMVCAnnotation!"); // WEB-INF/jsp/hello.jsp return "hello"; } }
@Controller 是爲了讓 Spring IoC容器初始化時自動掃描到並做爲 Spring 中的一個組件
@RequestMapping 時爲了映射請求路徑,這裏時由於類與方法上都有映射,因此訪問的是項目名/hello/h1
方法中聲明 Model 類型的參數是爲了把 Action 中的數據帶到視圖中
方法返回的結果是視圖的名稱 hello,加上配置文件中的先後綴變成 WEB-INF/jsp/hello.jsp
啓動 Tomcat 測試
小結
實現步驟其實很是簡單:
使用 SpringMVC 必須配置的三大件:
處理器映射器、處理器適配器、視圖解析器
一般,咱們只須要手動配置視圖解析器,而處理器映射器和處理器適配器只須要開啓註解驅動便可,這樣省去了大段的 xml 配置
控制器 Controller
Controller 有兩種實現方式
兩種方式的對比:
將類看成組件交由 Spring 託管的註解
@Component // 普通類註解 @Controller // controller層註解 @Repository // dao層註解 @Service // service層註解
@Controller @RequestMapping("hello") public class HelloController { @RequestMapping("h1") public String hello(Model model){ model.addAttribute("msg","HelloSpringMVCAnnotation!"); return "hello"; } }
上面代碼hello方法的路徑是 項目名/hello/h1
概念
RESTful 就是一個資源定位及資源操做的風格。不是標準也不是協議,只是一種風格。基於這個風格設計的軟件能夠更簡潔,更有層次,更易於實現緩存機制。
功能
資源:互聯網全部的事物均可以被抽象爲資源。
資源操做:使用 POST、DELETE、PUT、GET,使用不一樣方法對資源進行操做。
分別對應 添加、刪除、修改、查詢。
傳統方式操做資源:
經過不一樣的參數來實現不一樣的效果,方法單一: POST 和 GET。具體以下:
http://127.0.0.1/item/queryItem.action?id=1 查詢,GET
http://127.0.0.1/item/saveItem.action 新增,POST
http://127.0.0.1/item/updateItem.action 更新,POST
http://127.0.0.1/item/deleteItem.action?id=1 刪除,GET或POST
使用RESTful操做資源:
能夠經過不一樣的請求方式來實現不一樣的效果。具體以下:
http://127.0.0.1/item/1 查詢,GET
http://127.0.0.1/item 新增,POST
http://127.0.0.1/item 更新,PUT
http://127.0.0.1/item/1 刪除,DELETE
在原來的配置基礎上,增長一個 Controller 類
使用 @PathVariable 註解,讓方法參數的值對應綁定到一個 URI 模板變量上
@Controller public class HelloController { @RequestMapping("/add/{a}/{b}") public String hello(@PathVariable int a,@PathVariable int b, Model model){ int res = a+b; model.addAttribute("msg","結果爲:"+res); return "hello"; } }
瀏覽器 url 訪問及結果
全部地址欄請求默認都是 HTTP GET 類型的。
方法級別的註釋變體有以下幾個
@GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping
ModelAndView
設置 ModelAndView 對象,根據 view 的名稱,和視圖解析器跳到指定的頁面。
頁面:{視圖解析器前綴} + viewName +{視圖解析器後綴}
<!-- 視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前綴 --> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 後綴 --> <property name="suffix" value=".jsp"/> </bean>
對應的 Controller 爲:
public class HelloController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { // 建立 ModelAndView 對象 ModelAndView mv = new ModelAndView(); // 封裝對象,放在 ModelAndView 中,Model mv.addObject("msg","HelloSpringMVC!"); // 封裝要跳轉的視圖,放在 ModelAndView 中,至關於 /WEB-INF/jsp/hello.jsp mv.setViewName("hello"); return mv; } }
經過設置 ServletAPI,不須要視圖解析器
@Controller public class ResultGo { @RequestMapping("/result/t1") public void test1(HttpServletRequest req, HttpServletResponse rsp) throwsIOException { rsp.getWriter().println("Hello,Spring BY servlet API"); } @RequestMapping("/result/t2") public void test2(HttpServletRequest req, HttpServletResponse rsp) throwsIOException { rsp.sendRedirect("/index.jsp"); } @RequestMapping("/result/t3") public void test3(HttpServletRequest req, HttpServletResponse rsp) throwsException { //轉發 req.setAttribute("msg","/result/t3"); req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp); } }
測試前,須要將視圖解析器註釋掉
默認地址是轉發地址,加了前綴 foward:/
也是轉發,加了 redirect:/
前綴表明則是重定向。
@Controller public class TestController { @RequestMapping("test1") public String test1(Model model){ model.addAttribute("msg","test1"); // 轉發 return "/WEB-INF/jsp/test.jsp"; } @RequestMapping("test2") public String test2(Model model){ model.addAttribute("msg","test2"); // 轉發 return "forward:/WEB-INF/jsp/test.jsp"; } @RequestMapping("test3") public String test3(){ // 重定向 return "redirect:/index.jsp"; } }
重定向時須要注意一個點。重定向是客戶端的,轉發是服務端內部的。重定向是讓客戶端去訪問重定向的地址。客戶端是無權訪問 WEB-INF
目錄下資源。
若須要重定向到 WEB-INF
下,能夠先定向到一個地址,而後由服務器內部去跳轉
@RequestMapping("test3") public String test3(){ // 重定向到轉發的url return "redirect:/toWebInf"; } @RequestMapping("toWebInf") public String toWebInf(){ // 經過轉發,能夠訪問WEB-INF目錄下的資源 return "/WEB-INF/jsp/test.jsp"; }
@Controller @RequestMapping("/t2") public class TestController2 { @RequestMapping("/test1") public String test1(Model model){ model.addAttribute("msg","test1"); // 轉發 return "test"; } @RequestMapping("/test2") public String test2(Model model){ model.addAttribute("msg","test2"); // 轉發,使用 forward 必須使用全限定名 return "forward:/WEB-INF/jsp/test.jsp"; } @RequestMapping("/test3") public String test3(){ // 重定向 return "redirect:/t2/toWebInf"; } @RequestMapping("/toWebInf") public String toWebInf(){ // 經過轉發訪問 WEB-INF 目錄下的資源 return "test"; } }
使用 forward:/
進行轉發和使用 redirect:/
重定向 時,後面必須是全限定名
第一種:經過ModelAndView
和一開始的講原理時的代碼同樣
public class ControllerTest1 implements Controller { public ModelAndView handleRequest(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception { //返回一個模型視圖對象 ModelAndView mv = new ModelAndView(); mv.addObject("msg","test"); mv.setViewName("test"); return mv; } }
第二種:經過ModelMap
ModelMap
@Controller @RequestMapping("user") public class UserController { // 經過 http://localhost:8080/user/test1?name=xp 傳遞給後端 @RequestMapping("test1") public String test1(String name, ModelMap model){ // 接收前端對象 System.out.println("name->"+name); // 返回數據給前端 model.addAttribute("msg",name); // 跳轉視圖 return "test"; } }
第三種:經過Model
Model
@Controller @RequestMapping("user") public class UserController { // 經過 http://localhost:8080/user/test2?name=xp 傳遞給後端 @RequestMapping("test2") public String test1(String name, Model model){ // 接收前端對象 System.out.println("name->"+name); // 返回數據給前端 model.addAttribute("msg",name); // 跳轉視圖 return "test"; } }
對比
就對於新手而言,簡單來講使用區別就是:
固然,之後開發考慮的更多的是性能和優化,就不能單單僅限於此的瞭解
測試環境
建立一個 encoding.jsp,用來提交表單
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>解決亂碼問題</title> </head> <body> <form action="${pageContext.request.contextPath}/encoding/test1" method="post"> <input type="text" name="name"/> <input type="submit" value="提交"/> </form> </body> </html>
編寫控制器 EncodingController 來接收前端提交的表單
@Controller @RequestMapping("encoding") public class EncodingController { @PostMapping("test1") public String test1(String name, Model model){ // 查看獲取的前端提交的表單數據是否爲亂碼,方便定位問題 System.out.println("name->"+name); // 獲取前端提交的表單數據 model.addAttribute("msg",name); return "test"; } }
提交表單,查看控制檯和瀏覽器頁面輸出
能夠看出,在前端頁面提交表單到後臺接收前端表單數據的過程當中,亂碼就已經產生了!
解決辦法
在 web.xml 中配置 SpringMVC 自帶的編碼過濾器,並重啓 Tomcat 服務器
<!-- 編碼過濾器:解決中文亂碼問題 --> <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>
可是咱們發現,有些極端狀況下,這個過濾器對 GET 的支持很差
處理方法:
修改 Tomcat 配置文件,設置編碼
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
自定義過濾器
package com.xp.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Map; /** * 解決get和post請求 所有亂碼的過濾器 */ public class GenericEncodingFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //處理response的字符編碼 HttpServletResponse myResponse = (HttpServletResponse) response; myResponse.setContentType("text/html;charset=UTF-8"); // 轉型爲與協議相關對象 HttpServletRequest httpServletRequest = (HttpServletRequest) request; // 對request包裝加強 HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } } //自定義request對象,HttpServletRequest的包裝類 class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; //是否編碼的標記 private boolean hasEncode; //定義一個能夠傳入HttpServletRequest對象的構造函數,以便對其進行裝飾 public MyRequest(HttpServletRequest request) { super(request);// super必須寫 this.request = request; } // 對須要加強方法 進行覆蓋 @Override public Map getParameterMap() { // 先得到請求方式 String method = request.getMethod(); if (method.equalsIgnoreCase("post")) { // post請求 try { // 處理post亂碼 request.setCharacterEncoding("utf-8"); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get")) { // get請求 Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { // 確保get手動編碼邏輯只運行一次 for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null) { for (int i = 0; i < values.length; i++) { try { // 處理get亂碼 values[i] = new String(values[i] .getBytes("ISO-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } hasEncode = true; } return parameterMap; } return super.getParameterMap(); } //取一個值 @Override public String getParameter(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); if (values == null) { return null; } return values[0]; // 取回參數的第一個值 } //取全部值 @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); return values; } }
引入 Jackson 依賴
<!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency>
什麼是JSON?
在 JavaScript 語言中,一切都是對象。所以,任何 JavaScript 支持的類型均可以經過 JSON 來表示,例如字符串、數字、對象、數組等。
JSON 的格式:
JSON 鍵值對 是用來保存 JavaScript 對象的一種方式,和 JavaScript 對象的寫法也大同小異,鍵值對組合中的鍵名卸載前面,並用雙引號 "" 包裹,使用冒號 : 分隔,而後緊接着值:
{"name": "zhangsan"} {"age": "18"} {"sex": "男"}
不少人搞不清楚 JSON 和 JavaScript 對象的關係,甚至誰是誰都不清楚。其實,能夠這麼理解:
JSON 是 JavaScript 對象的字符串表示法,它使用文本表示一個 JS 對象的信息,本質是一個字符串。
var obj = {name:"zhangsan",age:18,sex:"男"}; var json = ‘{"name","zhangsan","age":18,"sex":"男"}’;
要實現從 JSON 字符串轉換爲 JavaScript 對象,使用 JSON.parse()
方法:
var obj = JSON.parse(‘{"name","zhangsan","age":18,"sex":"男"}’);
要實現從 JavaScript 對象轉換爲 JSON 字符串,使用 JSON.stringify()
方法:
var json = JSON.stringify({name:"zhangsan",age:18,sex:"男"});
測試環境搭建
新建一個 module: SpringMVC-05-JSON,並添加web支持
在 web 目錄下新建一個 json1.html,編寫測試內容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JSON</title> </head> <body> <script type="text/javascript"> //編寫一個js的對象 var user = { name:"張三", age:18, sex:"男" }; //將js對象轉換成json字符串 var str = JSON.stringify(user); console.log(str); //將json字符串轉換爲js對象 var user2 = JSON.parse(str); console.log(user2.age,user2.name,user2.sex); </script> </body> </html>
在IDEA中使用瀏覽器打開,查看控制檯輸出
Jackson: 目前比較好的 JSON 解析工具
固然工具不止這一種,好比還有阿里的 fastjson ,谷歌的 gson 等等
咱們這裏使用 Jackson,具體操做以下:
導入 Jackson 的依賴
<!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency>
配置 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_4_0.xsd" version="4.0"> <!-- 註冊DispatcherServlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 編碼過濾 --> <filter> <filter-name>encoding</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>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
建立 springmvc-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" 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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 開啓註解支持 --> <context:component-scan base-package="com.xp.controller"/> <!-- 靜態資源過濾 --> <mvc:default-servlet-handler/> <!-- 開啓mvc註解驅動支持 --> <mvc:annotation-driven/> <!-- 視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前綴 --> <property name="prefix" value="/WEB-INF/jsp"/> <!-- 後最 --> <property name="suffix" value=".jsp"/> </bean> </beans>
編寫實體類 User
@Data @NoArgsConstructor @AllArgsConstructor public class User { public String name; public String age; public String sex; }
編寫控制器 JsonController
@Controller public class JsonController { @RequestMapping(path = "/json1", produces = "application/json;charset=utf-8") @ResponseBody // 添加ResponseBody註釋,它就不會走視圖解析器,會直接返回一個字符串 public String json1() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); User user = new User("zhangsan", "18", "男"); return objectMapper.writeValueAsString(user); } }
@ResponseBody :該註解能夠添加在方法或類上,添加該註解後,該類或該方法就不會走視圖解析器,會直接返回一個字符串。
在先後端分離開發中,通常都使用 @ResponseBody 註解,十分方便。
若是在 @RequestMapping 註解中不配置 produces 屬性的話,就會產生中文亂碼。
固然,還有一種解決亂碼一勞永逸的方法
在 springmvc-servlet.xml 配置文件中增長以下配置
<!-- 開啓mvc註解驅動支持 --> <mvc:annotation-driven> <!-- 解決jackson亂碼問題 --> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="failOnEmptyBeans" value="false"/> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
使用這種方法解決亂碼問題後,就不須要在 @RequestMapping 註解中配置 produces 屬性
輸出集合
@RequestMapping("/json3") public String json3() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); User user1 = new User("張三1", "18", "男"); User user2 = new User("張三2", "18", "男"); User user3 = new User("張三3", "18", "男"); User user4 = new User("張三4", "18", "男"); List<User> users = new ArrayList<>(); users.add(user1); users.add(user2); users.add(user3); users.add(user4); String str = objectMapper.writeValueAsString(users); return str; }
輸出時間對象
@RequestMapping("/json4") public String json4() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = sdf.format(date); return objectMapper.writeValueAsString(format); }
fastjson.jar 是阿里開發的一款專門用於 Java 開發的包。能夠方便的實現 json 對象與 JavaBean 對象的轉換,實現 JavaBean 對象與 json 字符串的轉換,實現 json 對象與 json 字符串的轉換。
導入 fastjson 依賴
<dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.68</version> </dependency> </dependencies>
fastjson 三個主要的類:
JSONObject 表明 json 對象
JSONArray 表明 json 對象數組
JSON 表明 JSONObject 和 JSONArray 的轉化
代碼測試
public class FastJsonDemo { public static void main(String[] args) { //建立一個對象 User user1 = new User("張三1號", 3, "男"); User user2 = new User("張三2號", 3, "男"); User user3 = new User("張三3號", 3, "男"); User user4 = new User("張三4號", 3, "男"); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3); list.add(user4); System.out.println("*******Java對象 轉 JSON字符串*******"); String str1 = JSON.toJSONString(list); System.out.println("JSON.toJSONString(list)==>"+str1); String str2 = JSON.toJSONString(user1); System.out.println("JSON.toJSONString(user1)==>"+str2); System.out.println("\n****** JSON字符串 轉 Java對象*******"); User jp_user1=JSON.parseObject(str2,User.class); System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1); System.out.println("\n****** Java對象 轉 JSON對象 ******"); JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2); System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name")); System.out.println("\n****** JSON對象 轉 Java對象 ******"); User to_java_user = JSON.toJavaObject(jsonObject1, User.class); System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user); } }
使用AJAX能夠作:
iframe,能夠算是僞造的 AJAX,也能夠實現頁面局部刷新。雖然,iframe 標籤的使用比 AJAX 要簡單得多,但其安全性以及用戶體驗性並不如人意。
下面,咱們來使用 iframe 標籤實現頁面的局部刷新
建立一個新的工程項目:SpringMVC-06-AJAX,添加 web 框架
在 index.jsp 中編寫 iframe 標籤
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>iframeTest</title> <script> // iframe 加載url function loadUrl(url) { var iframe = document.getElementById("iframe"); iframe.src = url; } </script> </head> <body> <%-- 點擊按鈕後,iframe 加載頁面 --%> <div> <button id="iframeBtn" onclick="loadUrl('iframe.jsp')">點我加載 iframe.jsp</button> </div> <iframe src="" id="iframe"></iframe> </body> </html>
在 web 目錄下建立 iframe.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>這是 iframe.jsp</h1> </body> </html>
測試是否頁面局部加載成功
若是咱們讓 loadUrl()
的參數是百度網址時,也能夠跳轉到百度的頁面
<div> <button id="iframeBtn" onclick="loadUrl('http://www.baidu.com')">點我加載 iframe.jsp</button> </div>
而且咱們按 f12 打開開發者工具的時候,會發現頁面中,只有 iframe 內部的 #document 在改變,而整個頁面並無刷新。因此 iframe 標籤是實現頁面局部刷新的,能夠當作是一個僞 AJAX
XMLHttpRequest 是 AJAX 的基礎。
全部現代瀏覽器均支持 XMLHttpRequest 對象(IE5 和 IE6 使用 ActiveXObject)
XMLHttpRequest 用於在後臺與服務器交換數據。這意味着能夠不從新加載整個網頁的狀況下,對網頁的某部分進行更新
jQuery AJAX 簡介
Ajax 的核心是 XMLHttpRequest 對象(XHR),XHR 爲向服務器發送請求和解析服務器響應提供了接口,可以以異步方式從服務器獲取新數據。
jQuery 提供多個與 AJAX 有關的方法。
經過 jQuery AJAX 方法,咱們可以使用 HTTP GET 和 HTTP POST 從遠程服務器上請求文本、HTML、XML 或 JSON。同時咱們可以把這些外部數據直接載入網頁的被選元素中。
jQuery Ajax本質就是 XMLHttpRequest,對他進行了封裝,方便調用!
jQuery.ajax(...) 部分參數: url:請求地址 type:請求方式,GET、POST(1.9.0以後用method) headers:請求頭 data:要發送的數據 contentType:即將發送信息至服務器的內容編碼類型(默認: "application/x-www-form-urlencoded; charset=UTF-8") async:是否異步 timeout:設置請求超時時間(毫秒) beforeSend:發送請求前執行的函數(全局) complete:完成以後執行的回調函數(全局) success:成功以後執行的回調函數(全局) error:失敗以後執行的回調函數(全局) accepts:經過請求頭髮送給服務器,告訴服務器當前客戶端可接受的數據類型 dataType:將服務器端返回的數據轉換成指定類型 "xml": 將服務器端返回的內容轉換成xml格式 "text": 將服務器端返回的內容轉換成普通文本格式 "html": 將服務器端返回的內容轉換成普通文本格式,在插入DOM中時,若是包含JavaScript標籤,則會嘗試去執行。 "script": 嘗試將返回值看成JavaScript去執行,而後再將服務器端返回的內容轉換成普通文本格式 "json": 將服務器端返回的內容轉換成相應的JavaScript對象 "jsonp": JSONP 格式使用 JSONP 形式調用函數時,如 "myurl?callback=?" jQuery 將自動替換 ? 爲正確的函數名,以執行回調函數
SpringMVC使用AJAX
配置 springmvc-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" 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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 掃描包,開啓註解支持 --> <context:component-scan base-package="com.xp.controller"/> <!-- 靜態資源過濾 --> <mvc:default-servlet-handler/> <!-- 開啓註解驅動支持 --> <mvc:annotation-driven/> <!-- 視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前綴 --> <property name="prefix" value="/WEB=INF/html/"/> <!-- 後綴 --> <property name="suffix" value=".html"/> </bean> </beans>
配置 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_4_0.xsd" version="4.0"> <!-- 註冊 DispatcherServlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 編碼過濾器:解決中文亂碼問題 --> <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>
編寫控制器 AJAXController,假設數據庫中帳號和密碼分別是 admin 和 12345
package com.xp.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class AJAXController { // produces 解決亂碼問題 @RequestMapping(path = "/ajax/userName" ,produces="text/html;charset=UTF-8;") @ResponseBody public String ajaxUserName(String userName){ return ("admin".equals(userName))?"正確":"錯誤"; } @RequestMapping(path = "/ajax/password" ,produces="text/html;charset=UTF-8;") @ResponseBody public String ajaxPassword(String password){ return ("12345".equals(password))?"正確":"錯誤"; } }
編寫前端頁面和 AJAX 請求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX</title> <!-- 引入 jQuery --> <script src="../js/jquery-3.4.1.min.js"></script> <script type="text/javascript"> function userNameBlur() { $.post({ url: "/ajax/userName", data: "userName="+$("#userName").val(), dataType: "text", success: function (data) { var userNameAjax = $("#userNameAjax"); if ("正確" === data){ userNameAjax.css("color","green"); }else { userNameAjax.css("color","red"); } userNameAjax.html(data); } }) } function passwordBlur() { $.post({ url: "/ajax/password", data: "password="+$("#password").val(), dataType: "text", success: function (data) { var passwordAjax = $("#passwordAjax"); if ("正確" === data){ passwordAjax.css("color","green"); }else { passwordAjax.css("color","red"); } passwordAjax.html(data); } }); } </script> </head> <body> <div id="test"> <label>用戶名:<input type="text" id="userName" onblur="userNameBlur()"/></label> <span id="userNameAjax"></span> </div> <div> <label>密碼:<input type="text" id="password" onblur="passwordBlur()"/></label> <span id="passwordAjax"></span> </div> </body> </html>
啓動 Tomcat 服務器,測試
測試時,能夠發現,當咱們鼠標點擊輸入框後再點擊其它地方失去焦點時,右邊的span標籤內的內容會發生變化。這樣,咱們就學會了 SpringMVC 中使用 AJAX。
Thymeleaf 是一款用於渲染 XML/XHTML/HTML5 內容的模板引擎,相似 JSP, Velocity, FreeMaker 等。它能夠輕易與 Spring MVC 等 Web 框架進行集成做爲 Web 應用的模板引擎。是 SpringBoot 官方使用的模板引擎。
新建一個module:Springmvc-07-Thymeleaf,添加 web 支持
配置 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_4_0.xsd" version="4.0"> <!-- 註冊 DispatcherServlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 解決亂碼問題 --> <filter> <filter-name>encoding</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>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
導入 Themeleaf 依賴
<dependencies> <!-- Thymeleaf 支持Spring方言 --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.11.RELEASE</version> </dependency> <!-- Thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.11.RELEASE</version> </dependency> </dependencies>
配置springmvc-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" 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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:component-scan base-package="com.xp.controller"/> <mvc:default-servlet-handler/> <mvc:annotation-driven/> <!-- 模板解析器 --> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver" id="templateResolver"> <property name="prefix" value="/WEB-INF/html"/> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML"/> <property name="characterEncoding" value="utf-8"/> <property name="cacheable" value="false"/> </bean> <!-- 模板引擎 --> <bean class="org.thymeleaf.spring5.SpringTemplateEngine" id="templateEngine"> <property name="templateResolver" ref="templateResolver"/> <property name="enableSpringELCompiler" value="true"/> </bean> <!-- 視圖解析器 --> <bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine"/> <property name="characterEncoding" value="utf-8"/> </bean> </beans>
編寫控制器
@Controller public class ThymeleafController { @RequestMapping("/thymeleaf") public String thymeleaf(Model model){ model.addAttribute("thymeleaf","Hello,Thymeleaf!"); return "thymeleaf"; } }
編寫視圖層
注:要使用 Thymeleaf 模板引擎的頁面,必需要在 html 標籤上加上 xmlns:th="http://www.thymeleaf.org"
index.xml
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Thymeleaf</title> </head> <body> <h1><a href="/thymeleaf">Hello,Thymeleaf!</a></h1> </body> </html>
thymeleaf.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--/*@thymesVar id="thymeleaf" type="java.lang.String"*/--> <div th:text="${thymeleaf}"></div> </body> </html>
測試
咱們能夠發現 SpringMVC 整合 Thymeleaf 模板引擎,只是導入依賴後,在 springmvc-servlet.xml 配置文件中加入了模板解析器、模板引擎和視圖解析器三個 bean,並不須要修改 web.xml 中的配置。咱們就能夠在 html 中像 jsp 同樣使用 el 表達式了。下面,咱們來看下經常使用的 Thymeleaf 語法以及標籤。
Thymeleaf 的主要做用是把 model 中的數據渲染到html 中,所以其語法主要是如何解析 model 中的數據。從如下方面來學習:
Thymeleaf 經過 ${...}
來獲取 model 中的變量,語法和 el 表達式差很少,但它是 ognl 表達式。
<!--/*@thymesVar id="thymeleaf" type="java.lang.String"*/--> <div th:text="${thymeleaf}"></div>
Themeleaf 經過 th:object
自定義變量,能夠經過 *{...}
取出對應的屬性
<!--/*@thymesVar id="user" type="com.xp.entity.User"*/--> <div th:object="${user}"> <h2 th:text="*{name}"></h2> <h2 th:text="*{age}"></h2> <!--/*@thymesVar id="friend" type="com.xp.entity.Friend"*/--> <h2 th:text="*{friend.name}"></h2> </div>
ognl 表達式自己就支持方法調用,但須要注意的是必須使用註釋指明該變量是哪一個類的
<!--/*@thymesVar id="user" type="com.xp.entity.User"*/--> <!--/*@thymesVar id="name" type="java.lang.String"*/--> <!--/*@thymesVar id="age" type="java.lang.Integer"*/--> <div th:object="${user}"> <h2 th:text="*{name.hashCode()}"></h2> <h2 th:text="*{age.hashCode()}"></h2> <!--/*@thymesVar id="friend" type="com.xp.entity.Friend"*/--> <h2 th:text="*{friend.name.hashCode()}"></h2> </div>
Thymeleaf 中提供了一些內置對象,而且這些對象中提供了一些方法,方便咱們調用、獲取這些對象,須要使用 #對象名
來調用
一些環境相關的對象
對象 | 做用 |
---|---|
#ctx | 獲取 Thymeleaf 本身的 Context 對象 |
#request | 若是是 web 程序,能夠獲取 HttpServletRequest 對象 |
#respone | 若是是 web 程序,能夠獲取 HttpServletResponse 對象 |
#session | 若是是 web 程序,能夠獲取 HttpSession 對象 |
#servletContext | 若是是web 程序,能夠獲取 HttpServletContext 對象 |
Thymeleaf 提供的全局對象
對象 | 做用 |
---|---|
#datas | 處理 java.util.date 的工具對象 |
#calendars | 處理 java.util.calendar 的工具對象 |
#numbers | 用來對數字格式的方法 |
#strings | 用來處理字符串的方法 |
#bools | 用來判斷布爾值的方法 |
#arrays | 用來護理數組的方法 |
#lists | 用來處理 List 集合的方法 |
#sets | 用來處理 Set 集合的方法 |
#maps | 用來處理 Map 集合的方法 |
例如:
<div th:text="${#dates.format(data,'yyyy-MM-dd HH:mm:ss')}"></div>
<div th:Object="${#session.getAttribute('user')}"> <h1 th:text="*{name}"></h1> <h1 th:text="*{age}"></h1> <h1 th:text="*{friend.name}"></h1> </div>
字面值
字符串字面值:使用一對 '' (單引號)引用的內容就是字符串的字面值了
<div th:text="'字符串字面值'"></div>
數字字面值:不須要任何特殊語法,寫的是是什麼就是什麼,能夠進行算術運算
<div th:text="2020"></div> <div th:text="2018+2"></div>
布爾字面值:只有 true 或 false
<div th:if="true">布爾值:true</div>
字符串拼接
咱們常用得普通字符串拼接方法
<div th:text="'歡迎 '+${user.name}+‘ !’"></div>
Thymeleaf 使用一對 | 拼接
<div th:text="|歡迎 +${user.name} !|"></div>
運算
算術運算
支持的運算符: + - * / %
<div th:text="${user.age}%2"></div>
比較運算運算
支持的比較運算: >,<,>=,<=,可是 >,< 不能直接使用,由於 html 會解析爲標籤,要使用別名
注意 == 和 != 不只能夠比較數值,相似於 equals 的功能
可使用的別名:gt(>), lt(<), ge(>=) , le(<=), not(!), eq(==), neq/ne(!=)
條件運算
三元運算
<div th:text="${user.isAdmin}?'管理員':'普通會員'"></div>
默認值
有的時候,咱們取一個值可能爲空,這個時候須要作非空判斷,可使用表達式 ?: 默認值簡寫
<span th:text="${user.name} ?: '二狗'"></span>
Thymeleaf 經過 th:each
實現循環
<div th:each="list:${lists}"> <h1 th:text="${list}"></h1> </div>
遍歷的結合能夠是如下類型
Thymeleaf 使用 th:if
或者 if:unless
來進行邏輯判斷
<div th:if="${user.age} >= 18"> <h1>成年人</h1> </div>
若是表達式的值爲 true,則標籤會渲染到頁面,不然不進行渲染。
如下狀況會被認爲 true
其它狀況包括 null 都被認定爲 false
Thymeleaf 使用 th:switch
和 th:case
來進行分支控制
<div th:switch="${user.role}"> <p th:case="'admin'">用戶是管理員</p> <p th:case="'manager'">用戶是經理</p> <p th:case="*">用戶是別的玩意</p> </div>
須要注意的是,一旦有一個 th:case
成立,其它的則再也不判斷。與 java 中的 switch 是同樣的
另外 th:case="*"
表示默認,放在最後
Thymeleaf 使用 th:inline="javascript"
來聲明該 script 標籤的腳本是須要特殊處理的 js 腳本
<script th:inline="javascript"> var user = /*[[${user}]]*/ {}; var age = /*[[${user.age}]]*/ 20; console.log(user); console.log(age) </script>
var user = /*[[Thymeleaf表達式]]*/
由於 Thymeleaf 被註釋起來,所以即使是靜態環境下,js 代碼也不會報錯,而是採用表達式後面跟着的默認值。且 User 對象會直接處理爲 json 格式
SpringMVC 的處理器攔截器相似於 Servlet 開發中的過濾器 Filter,用於對處理器進行預處理和後處理。開發者能夠本身定義一些攔截器來實現待定的功能。
過濾器與攔截器的區別:攔截器時 AOP 思想的具體應用。
過濾器
攔截器
那如何實現攔截器呢?
想要自定義攔截器,必須實現 HandlerInterceptor 接口。
新建一個 Moudule:Spring-08-Interceptor ,添加 web 支持
配置 springmvc-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" 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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:component-scan base-package="com.xp.controller"/> <mvc:default-servlet-handler/> <mvc:annotation-driven/> <!-- 攔截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.xp.config.MyInterceptor"/> </mvc:interceptor> </mvc:interceptors> <!-- 模板解析器 --> <bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/html/"/> <property name="suffix" value=".html"/> <property name="cacheable" value="false"/> <property name="characterEncoding" value="utf-8"/> <property name="templateMode" value="HTML"/> </bean> <!-- 模板引擎 --> <bean class="org.thymeleaf.spring5.SpringTemplateEngine" id="templateEngine"> <property name="templateResolver" ref="templateResolver"/> <property name="enableSpringELCompiler" value="true"/> </bean> <!-- 視圖解析器 --> <bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine"/> <property name="characterEncoding" value="utf-8"/> </bean> </beans>
配置 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_4_0.xsd" version="4.0"> <!-- 註冊DispatcherServlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 解決亂碼問題 --> <filter> <filter-name>encoding</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>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
編寫實體類 User
@Data @AllArgsConstructor @NoArgsConstructor public class User { private String account; private String password; }
建立控制器 InterceptorController
@Controller @RequestMapping("/user") public class InperceptorController { @RequestMapping("/toMain") public String toMain(){ return "main"; } @RequestMapping("/toLogin") public String toLogin(){ return "login"; } @RequestMapping("/login") public String login(User user, HttpSession session){ // 模擬從數據庫中查詢數據後判斷帳號密碼是否正確,正確則設置session,不然返回登陸頁面 if ("admin".equals(user.getAccount()) && "12345".equals(user.getPassword())){ session.setAttribute("user",user); return "main"; }else { return toLogin(); } } @RequestMapping("/logout") public String logout(HttpSession session){ session.removeAttribute("user"); return toLogin(); } }
編寫視圖層
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h2><a href="/user/toMain">首頁</a></h2> <h2><a href="/user/toLogin">登陸</a></h2> </body> </html>
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陸</title> </head> <body> <form action="/user/login"> <label>用戶名:<input type="text" name="account"/></label> <label>密碼:<input type="text" name="password"/></label> <input type="submit" value="登陸"/> </form> </body> </html>
main.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>首頁</h1> <!-- 有session則顯示該div --> <div th:object="${#session.getAttribute('user')}" th:if="Object"> <span th:text="*{account}"></span> <a href="/user/logout">退出</a> </div> </body> </html>
建立攔截器 MyInterceptor ,實現 HandlerInterceptor 接口,並重寫 preHandle() 方法
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String url = request.getRequestURI(); System.out.println(url); // 導航頁放行 if ("/".equals(url)){ return true; } // 導航頁放行 if (url.contains("index")){ return true; } // 登陸請求放行 if (url.contains("login")){ return true; } // 跳轉登陸頁面請求放行 if (url.contains("toLogin")){ return true; } // 登陸後有session的放行 if (request.getSession().getAttribute("user") != null){ return true; } // 攔截後跳轉到登陸頁面 response.sendRedirect("/user/toLogin"); // 攔截 return false; } }
啓動 Tomcat 測試
測試沒登陸前能不能進入首頁、測試登陸後從導航頁是否能進入首頁、測試登陸後退出後可否再進入首頁。
測試成功則表明攔截器配置成功
文件上傳是項目開發中最多見的功能之一 ,SpringMVC 能夠很好的支持文件上傳,可是 SpringMVC 上下文中默認沒有裝配 MultipartResolver,所以默認狀況下其不能處理文件上傳工做。若是想使用Spring的文件上傳功能,則須要在上下文中配置 MultipartResolver。
前端表單要求:爲了能上傳文件,必須將表單的method設置爲POST,並將enctype設置爲multipart/form-data。只有在這樣的狀況下,瀏覽器纔會把用戶選擇的文件以二進制數據發送給服務器。
對錶單中的 enctype 屬性作個詳細的說明:
<form action="" enctype="multipart/form-data" method="post"> <input type="file" name="file"/> <input type="submit"> </form>
一旦設置了 enctype 爲 multipart/form-data,瀏覽器即會採用二進制流的方式來處理表單數據,而對於文件上傳的處理則涉及在服務器端解析原始的HTTP響應。在2003年,Apache Software Foundation 發佈了開源的 Commons FileUpload 組件,其很快成爲Servlet/JSP程序員上傳文件的最佳選擇。
導入文件上傳的jar包,commons-fileupload , Maven會自動幫咱們導入他的依賴包 commons-io包;
<!--文件上傳--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> <!--servlet-api導入高版本的--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency>
配置bean:multipartResolver
【注意!!!這個bena的id必須爲:multipartResolver , 不然上傳文件會報400的錯誤!在這裏栽過坑,教訓!】
<!--文件上傳配置--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 請求的編碼格式,必須和jSP的pageEncoding屬性一致,以便正確讀取表單的內容,默認爲ISO-8859-1 --> <property name="defaultEncoding" value="utf-8"/> <!-- 上傳文件大小上限,單位爲字節(10485760=10M) --> <property name="maxUploadSize" value="10485760"/> <property name="maxInMemorySize" value="40960"/> </bean>
CommonsMultipartFile 的 經常使用方法:
咱們去實際測試一下
編寫前端頁面
<form action="/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file"/> <input type="submit" value="upload"> </form>
編寫控制器
@Controller public class FileController { //@RequestParam("file") 將name=file控件獲得的文件封裝成CommonsMultipartFile 對象 //批量上傳CommonsMultipartFile則爲數組便可 @RequestMapping("/upload") public String fileUpload(@RequestParam("file") CommonsMultipartFile file ,HttpServletRequest request) throws IOException { //獲取文件名 : file.getOriginalFilename(); String uploadFileName = file.getOriginalFilename(); //若是文件名爲空,直接回到首頁! if ("".equals(uploadFileName)){ return "redirect:/index.jsp"; } System.out.println("上傳文件名 : "+uploadFileName); //上傳路徑保存設置 String path = request.getServletContext().getRealPath("/upload"); //若是路徑不存在,建立一個 File realPath = new File(path); if (!realPath.exists()){ realPath.mkdir(); } System.out.println("上傳文件保存地址:"+realPath); InputStream is = file.getInputStream(); //文件輸入流 OutputStream os = new FileOutputStream(new File(realPath,uploadFileName));//文件輸出流 //讀取寫出 int len=0; byte[] buffer = new byte[1024]; while ((len=is.read(buffer))!=-1){ os.write(buffer,0,len); os.flush(); } os.close(); is.close(); return "redirect:/index.jsp"; } }
測試上傳文件,OK!
採用file.Transto 來保存上傳的文件
編寫Controller
@RequestMapping("/upload2") public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request) throws IOException { //上傳路徑保存設置 String path = request.getServletContext().getRealPath("/upload"); File realPath = new File(path); if (!realPath.exists()){ realPath.mkdir(); } //上傳文件地址 System.out.println("上傳文件保存地址:"+realPath); //經過CommonsMultipartFile的方法直接寫文件(注意這個時候) file.transferTo(new File(realPath +"/"+ file.getOriginalFilename())); return "redirect:/index.jsp"; }
前端表單提交地址修改
訪問提交測試,OK!
文件下載步驟:
設置 response 響應頭
讀取文件 -- InputStream
寫出文件 -- OutputStream
執行操做
關閉流 (先開後關)
代碼實現
@RequestMapping(value="/download") public String downloads(HttpServletResponse response ,HttpServletRequest request)throws Exception{ //要下載的圖片地址 String path = request.getServletContext().getRealPath("/upload"); String fileName = "基礎語法.jpg"; //一、設置response 響應頭 response.reset(); //設置頁面不緩存,清空buffer response.setCharacterEncoding("UTF-8"); //字符編碼 response.setContentType("multipart/form-data"); //二進制傳輸數據 //設置響應頭 response.setHeader("Content-Disposition", "attachment;fileName="+URLEncoder.encode(fileName, "UTF-8")); File file = new File(path,fileName); //二、 讀取文件--輸入流 InputStream input=new FileInputStream(file); //三、 寫出文件--輸出流 OutputStream out = response.getOutputStream(); byte[] buff =new byte[1024]; int index=0; //四、執行 寫出操做 while((index= input.read(buff))!= -1){ out.write(buff, 0, index); out.flush(); } out.close(); input.close(); return null; }
前端
<a href="/download">點擊下載</a>
測試,文件下載OK
環境配置
建立數據庫並建立對應的表,並插入數據
# 建立數據庫 ssmproject CREATE DATABASE IF NOT EXISTS ssmproject; # 建立表 DROP TABLE IF EXISTS book; CREATE TABLE IF NOT EXISTS book( book_id INT(10) PRIMARY KEY auto_increment COMMENT '書id', book_name VARCHAR(100) NOT NULL COMMENT '書名', book_counts INT(11) NOT NULL COMMENT '數量', detail VARCHAR(200) COMMENT '描述' ); # 插入數據 INSERT INTO book (book_name,book_counts,detail) VALUES ('Java',1,'從入門到放棄'), ('MySQL',10,'從刪庫到排路'), ('Linux',5,'從入門到進牢');
在 pom.xml 中導入依賴並配置靜態資源過濾
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xp</groupId> <artifactId>SSMProject</artifactId> <version>1.0-SNAPSHOT</version> <!-- 依賴 --> <dependencies> <!-- junit 測試 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- SpringMVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!-- 織入包 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency> <!-- servlet支持 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!-- jackson 解析json --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency> <!-- spring-jdbc JDBC驅動支持 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.3.RELEASE</version> </dependency> <!-- mysql驅動 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <!-- c3p0 數據庫鏈接池 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</version> </dependency> <!-- mybatis-Spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.3</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> <!-- thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>LATEST</version> </dependency> <!-- Spring 整合 thymeleaf --> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>LATEST</version> </dependency> <!-- log4j 日誌 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>2.12.1</version> </dependency> </dependencies> <!-- 靜態資源過濾問題 --> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> </project>
添加 web 支持,並把包目錄結構先創建起來
編寫實體類 Book
/** * 書 實體類 * @author xp */ @Data @AllArgsConstructor @NoArgsConstructor public class Book { /** * 書id */ private Integer bookId; /** * 書名 */ private String bookName; /** * 書數量 */ private Integer bookCounts; /** * 描述 */ private String detail; }
編寫 mapper 接口
/** * BookMapper * * @author xp */ public interface BookMapper { /** * 增長一本書 * * @param book 待增長的書籍信息 * @return 影響行數 */ int addBook(Book book); /** * 根據id刪除一本數 * * @param id 被刪除的書的id * @return 影響行數 */ int deleteBookById(@Param("bookId") int id); /** * 修改一本數 * * @param book 修改後的書籍信息 * @return 影響行數 */ int updateBook(Book book); /** * 根據id查詢書 * * @param id 書的id * @return 查詢後的結果 */ Book queryBookById(@Param("bookId") int id); /** * 查詢全部書 * * @return 存儲書籍信息的集合 */ List<Book> queryAllBook(); /** * 根據書籍名字查詢書籍 * * @param name 書籍名字 * @return 存儲書籍信息的集合 */ List<Book> queryBookByName(@Param("bookName") String name); }
編寫 Service 接口和其實現類
Service 接口
/** * 書本業務層 * * @author xp */ public interface BookService { /** * 增長一本書 * * @param book 待增長的書籍信息 * @return 影響行數 */ int addBook(Book book); /** * 根據id刪除一本數 * * @param id 被刪除的書的id * @return 影響行數 */ int deleteBookById(int id); /** * 修改一本數 * * @param book 修改後的書籍信息 * @return 影響行數 */ int updateBook(Book book); /** * 根據id查詢書 * * @param id 書的id * @return 查詢後的結果 */ Book queryBookById(int id); /** * 查詢全部書 * * @return 存儲書籍信息的集合 */ List<Book> queryAllBook(); /** * 根據書籍名字查詢書籍 * * @param name 書籍名字 * @return 存儲書籍信息的集合 */ List<Book> queryBookByName(String name); }
Service 實現類
/** * Service 實現類 * @author xp */ public class BookServiceImpl implements BookService { private BookMapper bookMapper; public void setBookMapper(BookMapper bookMapper) { this.bookMapper = bookMapper; } @Override public int addBook(Book book) { return bookMapper.addBook(book); } @Override public int deleteBookById(int id) { return bookMapper.deleteBookById(id); } @Override public int updateBook(Book book) { return bookMapper.updateBook(book); } @Override public Book queryBookById(int id) { return bookMapper.queryBookById(id); } @Override public List<Book> queryAllBook() { return bookMapper.queryAllBook(); } @Override public List<Book> queryBookByName(String name) { return bookMapper.queryBookByName(name); } }
建立數據庫文件 db.properties
jdbc.drive=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ssmproject?useTimezone=true&serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=UTF-8&useSSL=false jdbc.username=root jdbc.password=root
編寫 MyBatis 核心配置文件 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- 開啓駝峯映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 開啓log4j日誌 --> <setting name="logImpl" value="LOG4J"/> </settings> <!-- 別名 --> <typeAliases> <package name="com.xp"/> </typeAliases> <!-- 註冊 mapper --> <mappers> <mapper resource="mapper/bookMapper.xml"/> </mappers> </configuration>
配置 log4j.properties
#將等級爲DEBUG的日誌信息輸出到console和file這兩個目的地,console和file的定義在下面的代碼 log4j.rootLogger=DEBUG,console,file #控制檯輸出的相關設置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=【%c】-%m%n #文件輸出的相關設置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/xp.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n #日誌輸出級別 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
建立 spring-dao.xml,配置dao層,並交由 Spring 託管
<?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 https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 關聯數據庫文件 --> <context:property-placeholder location="classpath:properties/db.properties"/> <!-- 鏈接池 dbcp:半自動化操做,不能自動鏈接 c3p0:自動化操做(自動化地加載配置文件,而且能夠自動設置到對象中) druid、hikari --> <bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"> <property name="driverClass" value="${jdbc.drive}"/> <property name="jdbcUrl" value="${jdbc.url}"/> <property name="user" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <!-- SqlSessionFactory --> <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:config/mybatis-config.xml"/> </bean> <!-- 配置dao接口掃描包,動態實現了Dao接口能夠注入Spring容器中 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 要掃描的包 --> <property name="basePackage" value="com.xp.mapper"/> <!-- 注入SqlSessionFactory --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> </beans>
在 mapper 包下建立 bookMapper.xml,編寫映射器映射的sql語句
<?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.xp.mapper.BookMapper"> <insert id="addBook" parameterType="book"> insert into book (book_name, book_counts, detail) values (#{bookName},#{bookCounts},#{detail}); </insert> <delete id="deleteBookById" parameterType="_int"> delete from book where book_id = #{bookId}; </delete> <update id="updateBook" parameterType="book"> update book set book_name = #{bookName},book_counts=#{bookCounts},detail=#{detail} where book_id=#{bookId}; </update> <select id="queryBookById" parameterType="_int"> select * from book where book_id = #{bookId}; </select> <select id="queryAllBook"> select * from book; </select> <select id="queryBookByName" parameterType="String" resultType="book"> select * from book where book_name = #{bookName}; </select> </mapper>
編寫 spring-service.xml,將 Service 層注入到 Spring 容器中,並配置相對應的事務
<?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 https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 掃描service包下的類 --> <context:component-scan base-package="com.xp.service"/> <!-- 將咱們全部的業務類,注入到Spring,能夠經過配置或註解實現,這裏使用配置實現 --> <bean id="bookService" class="com.xp.service.impl.BookServiceImpl"> <property name="bookMapper" ref="bookMapper"/> </bean> <!-- 配置聲明式事務 --> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"> <!-- 注入數據源 --> <property name="dataSource" ref="dataSource"/> </bean> <!-- AOP 事務支持 --> </beans>
建立 springmvc-servlet.xml ,將 Controller 層注入到 Spring 中
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 掃描controller下的包 --> <context:component-scan base-package="com.xp.controller"/> <!-- 靜態資源處理 --> <mvc:default-servlet-handler/> <!-- 註解驅動支持 --> <mvc:annotation-driven> <!-- 解決Jackson亂碼問題 --> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="utf-8"/> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="failOnEmptyBeans" value="false"/> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <!-- 模板解析器 --> <bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/html/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML5" /> <property name="cacheable" value="false" /> <property name="characterEncoding" value="UTF-8"/> </bean> <!-- 模板引擎 --> <bean class="org.thymeleaf.spring5.SpringTemplateEngine" id="templateEngine"> <property name="templateResolver" ref="templateResolver"/> <property name="enableSpringELCompiler" value="true"/> </bean> <!-- 視圖解析器 --> <bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="characterEncoding" value="utf-8"/> <property name="templateEngine" ref="templateEngine"/> </bean> </beans>
建立 applicationContext.xml ,使用 import 標籤將剛剛配置的文件導入到這個主配置中
<?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"> <!-- 整合配置文件 --> <import resource="springmvc-servlet.xml"/> <import resource="spring-dao.xml"/> <import resource="spring-service.xml"/> </beans>
配置 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_4_0.xsd" version="4.0"> <!-- 配置 DispatcherServlet --> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 解決亂碼問題 --> <filter> <filter-name>encoding</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>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
編寫控制器 BookController
@Controller public class BookController { private static final String TO_ALL_BOOK = "/toAllBook"; // Controller 層調用 Service 層 @Autowired private BookService bookService; public void setBookService(BookService bookService) { this.bookService = bookService; } @RequestMapping(TO_ALL_BOOK) public String toAllBook() { return "allBook"; } @RequestMapping("/allBook") @ResponseBody public String allBook() throws JsonProcessingException { List<Book> books = bookService.queryAllBook(); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(books); } }
編寫主頁 index.html
<!DOCTYPE html> <html lang="en"> <head> <title>$Title$</title> <style> a{ text-decoration: none; color: black; font-size: 38px; } h3{ width: 300px; height: 80px; margin: 200px auto; text-align: center; line-height: 80px; background-color: #7afdf1; border-radius: 5px; } </style> </head> <body> <h3><a href="${pageContext.request.contextPath}/book/toAllBook">進入書籍頁面</a></h3> </body> </html>
到這裏,咱們的三層架構就已經搭建完成了。
啓動Tomcat,簡單測試一下配置是否有誤。
配置無誤後,就開始編寫前端頁面和控制器。
allBook.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>書籍展現</title> <script src="../../static/js/jquery-3.4.1.min.js"></script> <script src="../../static/js/bootstrap.min.js"></script> <link type="text/css" rel="stylesheet" href="../../static/css/bootstrap.min.css"/> <script type="text/javascript"> // init 初始化頁面,加載後端的數據 $.get({ url: "/book/allBook", dataType: "json", success: function (data) { booksJsonToString(data); } }); </script> </head> <body> <div class="container"> <!-- 標題 --> <div class="row clearfix"> <div class="col-md-12 column"> <div class="page-header"> <h1> <small>書籍展現</small> </h1> </div> </div> </div> <div class="row clearfix"> <!-- 增長按鈕 --> <div class="col-md-4 column"> <button class="btn btn-primary" onclick="clickToHref('/book/toAddBook')">增長</button> <!-- 顯示全部書籍按鈕 --> <button class="btn btn-primary" onclick="clickToHref('/book/toAllBook')">顯示全部書籍</button> </div> <!-- 搜索框 --> <div class="col-md-8 column" > <form class="form-inline" id="searchBookForm" style="float: right"> <span id="searchError" style="color:red"></span> <input type="text" class="form-control" name="name" placeholder="請輸入查詢的書籍名稱"/> <input id="searchBtn" class="btn btn-secondary" type="button" value="搜索" onclick="searchBtnClick()"/> </form> </div> </div> <!-- 表格 --> <div class="row clearfix"> <div class="col-md-12 column"> <table class="table table-hover table-striped table-responsive-sm"> <thead> <tr> <td>書籍編號</td> <td>書籍名稱</td> <td>書籍數量</td> <td>書籍描述</td> <td>操做</td> </tr> </thead> <tbody id="tbody"> </tbody> </table> </div> </div> </div> <script> // 點擊後跳轉連接 function clickToHref(href) { window.location.href=href; } function deleteClick(id) { var msg = "確認刪除嗎?"; if (confirm(msg) === true){ clickToHref('/book/deleteBook?id='+id) } else { return false; } } // 更新按鈕點擊跳轉 function updateClick(href,id) { clickToHref(href+"?id="+id); } // 搜索按鈕點擊跳轉 function searchBtnClick() { $.get({ url: "/book/searchBook", data: $("#searchBookForm").serialize(), dataType: "json", success: function (data) { var json = JSON.stringify(data); var searchError = $("#searchError"); searchError.empty(); // 判斷數據是否爲空 if (json==="[]"){ searchError.html("搜索不到該書籍"); refreshTBody(""); }else { booksJsonToString(data); } } }) } </script> <script> // 將book集合的json格式轉換成字符串 function booksJsonToString(data) { var html = ''; for (var i=0;i<data.length;i++){ html += bookJsonToString(data[i]); } refreshTBody(html); } // 將book的json格式轉換成字符串 function bookJsonToString(book) { var bookId = book.bookId; var html = ""; html += "<tr><td>" + bookId + "</td><td>" + book.bookName + "</td><td>" + book.bookCounts + "</td><td>" + book.detail + "</td><td><button class='btn btn-sm btn-info' onclick=" + "updateClick('/book/toUpdateBook'," + bookId + ")" + " >修改</button>" + " <button class='btn btn-danger btn-sm' onclick=deleteClick(" + bookId + ")" + " >刪除</button></td></tr>"; return html; } // 刷新表格中的內容 function refreshTBody(html) { var tbody = $("#tbody"); tbody.empty(); tbody.html(html); } </script> </body> </html>
addBook.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>增長書籍</title> <script src="../../static/js/jquery-3.4.1.min.js"></script> <script src="../../static/js/bootstrap.min.js"></script> <link type="text/css" rel="stylesheet" href="../../static/css/bootstrap.min.css"/> </head> <body> <div class="container"> <!-- 標題 --> <div class="row clearfix"> <div class="col-md-12 column"> <h1> <small>增長書籍</small> </h1> </div> </div> <!-- 增長書籍的表單 --> <div class="row clearfix"> <div class="col-md-8 column"> <form action="/book/addBook"> <div class="form-row"> <label>書籍名稱<input type="text" name="bookName" class="form-control input-group mb-3" required/></label> . </div> <div class="form-row"> <label>書籍數量<input type="text" name="bookCounts" class="form-control input-group mb-3" required/> </label> </div> <div class="form-row"> <label>書籍描述<input type="text" name="detail" class="form-control input-group mb-3" required/></label> </div> <div class="form-row"> <input type="submit" class="btn btn-primary form-control" onclick="return addClick()" value="添加" style="width: 200px"/> </div> </form> </div> </div> </div> <script> // 防止誤點操做 function addClick(){ var msg = "確認增長?"; return confirm(msg); } </script> </body> </html>
update.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>修改書籍</title> <script src="../../static/js/jquery-3.4.1.min.js"></script> <script src="../../static/js/bootstrap.min.js"></script> <link type="text/css" rel="stylesheet" href="../../static/css/bootstrap.min.css"/> </head> <body> <div class="container"> <!-- 標題 --> <div class="row clearfix"> <div class="col-md-12 column"> <h1> <small>修改書籍</small> </h1> </div> </div> <!--/*@thymesVar id="book" type="com.xp.entity.Book"*/--> <!-- 更新的表單 --> <div class="row clearfix"> <div class="col-md-6"> <form action="/book/updateBook"> <input type="hidden" name="bookId" th:value="${book.bookId}"> <div class="form-row input-group mb-3"> <label>書籍名稱<input type="text" name="bookName" class="form-control" th:value="${book.bookName} "/></label> </div> <div class="form-row input-group mb-3"> <label>書籍數量<input type="text" name="bookCounts" class="form-control" th:value="${book.bookCounts}"/></label> </div> <div class="form-row input-group mb-3"> <label>書籍描述<input type="text" name="detail" class="form-control" th:value="${book.detail}"/></label> </div> <div class="form-row"> <input type="submit" class="btn btn-primary form-control" onclick="return updateClick()" value="修改" style="width: 200px"/> </div> </form> </div> </div> </div> <script> // 防止誤點 function updateClick(){ var msg = "確認修改?"; return confirm(msg) === true; } </script> </body> </html>
BookController
package com.xp.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.xp.entity.Book; import com.xp.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; @Controller @RequestMapping("/book") @SuppressWarnings("all") public class BookController { private static final String TO_ALL_BOOK = "/toAllBook"; // Controller 層調用 Service 層 @Autowired private BookService bookService; public void setBookService(BookService bookService) { this.bookService = bookService; } @RequestMapping(TO_ALL_BOOK) public String toAllBook() { return "allBook"; } @RequestMapping("/allBook") @ResponseBody public String allBook() throws JsonProcessingException { List<Book> books = bookService.queryAllBook(); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(books); } @RequestMapping("/toAddBook") public String toAddBook() { return "addBook"; } @RequestMapping("/addBook") public String addBook(Book book) { return "redirect:/book" + TO_ALL_BOOK; } @RequestMapping("/deleteBook") public String deleteBook(int id) { bookService.deleteBookById(id); return "redirect:/book" + TO_ALL_BOOK; } @RequestMapping("/updateBook") public String updateBook(Book book) { bookService.updateBook(book); return "redirect:/book" + TO_ALL_BOOK; } @RequestMapping("/toUpdateBook") public String toUpdateBook(int id, Model model) { Book book = bookService.queryBookById(id); model.addAttribute("book", book); return "updateBook"; } @RequestMapping("/searchBook") @ResponseBody public String searchBook(String name) throws JsonProcessingException { List<Book> book = bookService.queryBookByName(name); return new ObjectMapper().writeValueAsString(book); } }
到這裏,咱們的 SSM 框架纔算徹底整合。在之後再次寫 SSM 項目時,能夠將這個做爲模板來進行編寫。