使用 @RequestParam 注解Spring MVC 提供有 @RequestParam 注解,通過給定參數名,可以自動綁定請求包中的同名參數的數據。代碼如下:@Controller@RequestMapping("/user")public class UserAction { @RequestMapping("/register") public String register(@RequestParam("userName") String userNmae, @RequestParam("userPassword") String userPassword) { System.out.println(userNmae); System.out.println(userPassword); return null; }}此處,使用 @RequestParam 注解綁定請求包中的數據,有 2 個弊端:如果請求包中傳過來的數據較多,控制器中響應方法的參數也會增多,代碼臃腫不好維護;Java 語言最大的特色是面向對象編程(OOP)。很顯然,userName 和 userPassword 都是用戶的信息,以一種拆離的方式分別注入數據沒有體現出 OOP 的優點。那么,有沒有一種更好的替代方案或者說有一種很 OOP 的方案呢?以 OOP 方式綁定數據從 OOP 的角度分析,在應用程序中必然會存在一個描述用戶的類。public class User { private String userName; private String userPassword; //……}能不能直接把請求包中提交的數據綁定到 User 類型中?答案是肯定的,而且實現起來非常簡單,只需要把控制器方法的參數修改成對象類型便可。@RequestMapping("/register",method = RequestMethod.POST)public String register(User user) { System.out.println(user); return null;}不需要使用額外的任何注解,就可以直接綁定表單中的數據。為什么表單中的數據能自動綁定到對象上?原理很簡單,表單中數據以 key=value&key=value 的方式提交,此處的 key 實質是表單控件的名稱。前面的注冊表單中的數據在請求包中的格式形式如下:userNname=abc&userPassword=123456如上圖所示,Spring MVC 能自動解析這個數據,然后自動注入到對象的同名屬性中。所以一定要保證對象的屬性名與表單中提交數據時使用的參數名(key)一致。數據解析成功后,理論上講應該要把數據送到數據庫中,本章節暫不涉及到數據庫操作。只做業務邏輯模擬。@RequestMapping(value="/register",method = RequestMethod.POST)public String register(User user) { if("abc".equals(user.getUserName()) && "123456".equals(user.getUserPassword()) ) { return "success"; }else { return "fail"; } }
Spring MVC 提供有一種所謂的模板方法,和前面的以查詢字符串方法進行附加沒有多大區別。如下面的代碼,數據模型中的 data 對應數據會以 URL 變量方式傳遞。數據模型中其它數據則以查詢字符串方式進行傳遞。@RequestMapping("/response04") public String response04(ModelMap model) throws IOException { // 發送給客戶端的響應數據 String hello = "Hello"; model.addAttribute("data", hello); model.addAttribute("id", 1); return "redirect:/test/{data}"; } ? @RequestMapping("/test/{data}") public String response05(@PathVariable("data") String data,@RequestParam("id") int id) throws IOException { System.out.println(data); System.out.println(id); return null; }當在瀏覽器中請求 http://localhost:8888/sm-demo/response04 后,瀏覽器的地址欄中會變成 :http://localhost:8888/sm-demo/test/Hello?id=1。模板方式其本質和查詢字符串沒有太多區別。
在學習了 ScrollView 及 Adapter 兩節內容之后,大家應該對 ListView 有了一些基本的了解,它是一個列表樣式的 ViewGroup,將若干 item 按行排列。ListView 是一個很基本的控件也是 Android 中最重要的控件之一。它可以幫助我們完成多個 View 的垂直排列并支持滾動顯示效果,而它比 ScrollView 更靈活也更易擴展,Adapter 作為 UI 控件和數據源之間的橋梁,會幫我們實現 MVC 模式,所以在實際開發中大多數的列表場景我們會優先考慮使用 ListView 來實現(目前 Google 推出了新的更強大的列表控件——RecyclerView,不過基本原理和 ListView 類似)。
WEB 程序的應用層使用的是 HTTP 協議,HTTP 協議有一個特點,無狀態。所謂無狀態指上一次請求與下一次請求之間是隔離的,沒有內在的聯系。更通俗的講,可理解為一個患有健忘癥的人,只記得當前自己在做什么,不記得自己曾經做過什么,更不會知道自己將來要做什么。HTTP 協議的這種無狀態,最初設計時是從安全角度考慮。但是,在某些應用場景下,如購物車的應用場景下,卻顯得無能為力。購物車中的商品不一定是一次請求下的結果,往往是多次請求下的結果。也就是說,購物車需要保存每一次請求獲取到的數據。顯然,直接使用 HTTP 協議是無法做到的。就需從技術層面上提供解決方案。原生 Servlet 提供了 3 個作用域,可以根據用戶的需要來保存每一次請求過程中產生的數據。請求作用域: 使用 HttpServletRequest 組件存儲的數據可以在每一次的請求周期內存在。 請求結束,數據也將消失;會話作用域: 使用 HttpSession 組件保存的數據能在整個會話生命周期內存在。如購物車就可以保存在會話作用域中;應用程序作用域: 使用 ServletContext 組件保存的數據在整個應用程序生命周期之內存在。Spring MVC 中,把數據保存在請求作用域,或是說在整個請求過程中數據都有效。有 2 種解決方案直接使用 HttpServletRequest 組件;使用 Spriing MVC 提供的高級數據模型組件。
項目創建成功后,會發現項目上有紅色的錯誤提示,這個不用緊張。因為項目自動創建了一個 jsp 文件,而 jsp 所依賴的 servlet 包我們還沒有加進來。打開項目中的 pom.xml 文件,在文件中添加項目所需要的依賴 jar 包:javax.servlet-api: servlet 規范包;spring-context: spring 最基礎、最重要的核心包。Maven 有一個依賴傳遞功能,會自動添加 spring-context 所依賴的其它包;spring-web: spring 對 web 項目的支持包,這個不用解釋,肯定不能少;spring-webmvc: 從名字上看,知道它應該是主角,Spring MVC 核心包。完整的依賴信息如下:<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.5</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.13.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.13.RELEASE</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.13.RELEASE</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency></dependencies>Maven 會自動從遠程倉庫下載這些依賴包,項目中也可以看到下載下來的依賴包:大家會看到,除了在 pom.xml 文件中指定的依賴包外,還有其它的包,這個就是 Maven 的依賴傳遞功能實現的。好了! 有了這些包后,就可以使用 Spring 提供的 WEB MVC API 了。
WEB 應用程序的交互模式通過請求包與響應包來完成,請求包一般由瀏覽器構建,并發送給服務器端。服務器端解析請求包,并根據具體的業務邏輯處理請求包中的數據,并構建響應包回應請求方。響應包的實體部分中的內容可以由控制器的方法決定,或者說由需求來決定。根據響應包中的內容不同,可以分兩者響應方式:轉發:響應包直接包含響應給瀏覽器的數據;重定向:響應包僅包含數據視圖的地址。本節課程將向大家講解 Spring MVC 中如何實現轉發和重定向。本章節的重點是了解轉發和重定向的本質。
Django定義了服務發布、路由映射、模板編程、數據處理的一整套功能。這也意味著Django模塊之間緊密耦合,開發者需要學習Django自己定義的這一整套技術。Django是遵循MVC架構的Web開發框架,其主要由以下幾部分組成。管理工具(Management):一套內置的創建站點、遷移數據、維護靜態文件的命令工具;模型(Model):提供數據訪問接口和模塊,包括數據字段、元數據、數據關系等的定義及操作;視圖(View):Django 的視圖層封裝了 HTTP Reques t和 Response 的一系列操作和數據流,其主要功能包括URL映射機制、綁定模板等;模板(Template):是一套 Django 自己的頁面渲染模板語言,用若干內置的 tags 和 filters 定義頁面的生成方式;表單(Form):通過內置的數據類型和控件生成 HTML 表單;管理站(Admin):通過聲明需要管理的 Model,快速生成后臺數據管理網站。
如果是轉發,數據模型只需要是請求作用域級別的,視圖解析器便能從數據模型中拿到所需要的數據。對于重定向,因為跨了請求,視圖無法讀出請求作用域級別的數據模型中的數據。如果要讓數據模型中的數據被視圖識別出來,則需要提升數據模型的作用域,如升級為會話作用域級別。所謂會話作用域,意味著數據會存放在服務器的會話緩存區。如果數據使用的頻率不是很高,會產生空間上的浪費。有沒有一種較佳的方案,即不浪費服務器空間,又能傳遞數據了?答案是肯定的。最原始的方式便是采用查詢字符串的方式。如下面的實例:@RequestMapping("/response03") public String response03(ModelMap model) throws IOException { // 發送給客戶端的響應數據 String hello = "Hello"; model.addAttribute("data", hello); return "redirect:/hello.jsp"; }model 中的數據只是請求作用域級別,重定向后的 hello.jsp 中無法獲取此數據, Spring MVC 內部處理方式是把數據附加在 hello.jsp 后面。打開瀏覽器,輸入請求 http://localhost:8888/sm-demo/response03 地址,查看瀏覽器的地址欄中的 URL 變成 http://localhost:8888/sm-demo/hello.jsp?data=Hello。Tips:數據附加在 URL 后面是 Spring MVC 自動完成的。把 hello.jsp 頁面中的數據讀取方式改成下面的方式(從查詢參數中讀取)。<div style="color:red">${param.data} </div> 這種方式有 2 個缺點:安全性低,數據赤裸裸地暴露在地址欄中;如果數據量多,則整個 URL 變得臃腫不易維護。多數據實例:@RequestMapping("/response03") public String response03(ModelMap model) throws IOException { // 發送給客戶端的響應數據 String hello = "Hello"; model.addAttribute("data", hello); model.addAttribute("id", 1); return "redirect:/hello.jsp"; }當請求后,URL 會變成 http://localhost:8888/sm-demo/hello.jsp?data=Hello&id=1 。
如果瀏覽器中請求的是一個靜態資源(瀏覽器能解釋的資源,如 Html、Css、Js、圖片……),有必要經過前端控制器嗎?當然不需要。但是,你可以試著在 WEB 項目的 根目錄下創建名為 static.html 的靜態資源,然后在瀏覽器直接請求一下(http://localhost:8888/sm-demo/static.html)。會發現請求不到,那是因為你的請求還是經過了前端控制器。所以,咱們要告訴 Spring MVC 靜態資源還是交回給 Servlet 容器處理吧, 就不勞您大駕了。打開 WebConfig 配置類,讓其實現 WebMvcConfigurer 接口;public class WebConfig implements WebMvcConfigurer{ }重寫 configureDefaultServletHandling() 方法,啟動 Servlet 的 default Servlet 來處理靜態資源;public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable();}再次請求 http://localhost:8888/sm-demo/static.html ,你應該能看到靜態頁面的內容。
Django是 Full-Stack Web 框架的代表,功能非常全面和成熟,開發文檔很完備。它在Python Web開發框架的占有率應該是第一。Django 的宗旨就是盡可能的提供所有功能,讓你用盡可能少的代碼完成業務。Django 遵循了 MVC 開發模式,并將這個模式命名為 MTV ( MTV 模式是 Python 中獨有的):M Model(數據模型,用于后端數據庫模型定義和處理模塊);T Templates(模版,用于前端顯示信息);V View(視圖,用于接收客戶端請求、處理Model、渲染返回信息給客戶端等)。優點:各種組件集成高度成熟,配置齊全;用戶模型、權限認證體系健全;ORM數據庫管理功能簡單方便;自帶后臺管理功能。缺點:配置相對復雜;數據庫 ORM 組裝出來的 sql 語句性能較差。Django安裝通過 pip 直接安裝:pip install Django 通過源碼下載并安裝:git clone https://github.com/django/django.git Tips: 我們將在下一節講解如何在 PyCharm 創建基于Django框架的 Web 項目。
對于剛開始學習 PHP 的初級程序員來說,把基礎的 PHP 知識掌握之后,就需要進階到框架層面的知識了。ThinkPHP 框架上手容易,學習資料豐富,對新手朋友來說非常友好。ThinkPHP 采用 MVC 思想開發的,它的主要活躍社區在國內,免費開源,其配置開發相對容易、迅捷,非常適合新手的 PHP 框架。ThinkPHP 是一個輕量級的開發框架(比較小,僅提供一部分功能,可拓展很多第三方功能),學習 ThinkPHP 之后,可以快速發開一些中小型項目,并且能對 PHP 語言基礎有更深入的理解。作為一個整體開發解決方案,ThinkPHP 能夠解決應用開發中的大多數需要,因為其自身包含了底層架構、兼容處理、基類庫、數據庫訪問層、模板引擎、緩存機制、插件機制、角色認證、表單處理等常用的組件,并且對于跨版本、跨平臺和跨數據庫移植都比較方便。
Activity 的邏輯其實和 ListView 中的例子完全一樣,只需要將所有的 ListView 類型改成 GridView 即可。這里體現了 MVC 設計思路的靈活性,我們想要替換一個樣式其實只需要修改布局文件,主邏輯和數據層完全不需要修改,這就是前面所說的 UI 和數據解耦的強大優勢。MainActivity 代碼如下:package com.emercy.myapplication;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.GridView;import android.widget.Toast;public class MainActivity extends Activity { GridView mGridView; String[] mDataName = {"蘋果", "梨", "香蕉", "桃子", "西瓜", "荔枝", "橘子"}; int[] mDataImage = {R.drawable.apple, R.drawable.pear, R.drawable.banana, R.drawable.peach, R.drawable.watermelon, R.drawable.lychee, R.drawable.orange, R.drawable.orange}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mGridView = findViewById(R.id.gridview); MyAdapter adapter = new MyAdapter(this); adapter.setData(mDataName, mDataImage); mGridView.setAdapter(adapter); mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Toast.makeText(getApplicationContext(), mDataName[i % mDataName.length], Toast.LENGTH_LONG).show(); } }); }}編譯之后,可以發現從一維列表變成了網格列表,水果的樣式循環 10 次,效果如下:
以表單的方式提交數據有一個優點,可以把邏輯上具有內在聯系的多個數據同時發送給服務器。原生 Servlet 開發時,服務器端的響應組件(Servlet)中,需要開發者編寫代碼從請求包一個一個解析出提交過來的數據。如果數據量不多,倒還好,如果數據量較多,一個一個解析,枯燥乏味的工作除了讓人厭煩,且會耽誤開發效率。來!通過一個案例,體會 Spring MVC 是如何一步到位解析大量數據的。案例需求描述: 實現用戶注冊功能。流程分析: 通過注冊頁面,用戶輸入個人信息。表單中的數據以 POST 的請求方式提交至服務器,服務器端的控制器對提交過來的數據進行處理。實現流程:
原生 Servlet 開發過程中,開發者定義的 Servlet 具有請求響應功能。因為 J2EE 中的 Servlet 僅僅只是提供了編寫企業級應用程序的規范,并沒有提供更多實質性的功能。在執行響應邏輯之前或之后需要開發者編寫一些通用的功能代碼。如解析請求包中的數據、構建響應路徑……Spring MVC 提供了很多實用的注解,用來解析請求包、自動綁定請求包中不同位置的數據。在使用注解解析請求包中的數據前,先了解一下請求包的格式。WEB 應用程序中, 所謂的請求包是指遵循 HTTP 協議的一種數據格式包。在某些文章中,稱請求包為報文。一個完整的請求包應該由 3 個部分組成:請求行: 包括請求方法、請求的資源地址(URL) 、HTTP 協議版本號;消息頭: 消息頭是傳送給服務器的信息,以 key:value 的格式描述;實體部分或報文體: 以 key=value 的數據格式保存客戶端傳遞給應用程序的數據。
@RequestMapping 注解的映射描述具有多樣性:支持標準的 URL 格式;支持 Ant 風格。什么是 Ant 風格?所謂 Ant 風格指在 URL 中支持 通配符的語法結構描述。 Ant 的通配符主要是 3 種:?: 匹配任何單字符;* : 匹配 0 個或者任意數量的字符;** : 匹配 0 個或者更多的目錄。如下面的地址請求映射:/user/*/saveUser:可匹配類似于 /user/aaa/saveUser、/user/bbb/saveUser 等請求 URL;/user/**/saveUser:可以匹配 /user/saveUser、/user/aaa/bbb/saveUser 等請求 URL;Tips : ** 可以表示多個目錄。/user/saveUser??:匹配 /user/saveUseraa、/user/saveUserbb 等請求 URL;Spring MVC 的地址請求映射除了支持通配符外,還支持帶 {xxx} 占位符的 URL。如下面的地址請求映射:/user/{userId}:可以匹配 user/1、user/2、user/123 等請求 URL;/user/**/{userId}:可以匹配 user/aa/bbb/1、user/aaa/45 等請求 URL;company/{companyId}/user/{userId}/detail:可以匹配 company/1/user/2/detail 等請求 URL。
瀏覽器向 Spring MVC 程序發起的所有請求都會匯流給 DispatcherServlet 組件。再由 DispatcherServlet 分流到具體的用戶控制器;為什么要對所有請求集中分流?可以從 2 個維度理解:安全性: 如同去拜訪某一個公司,所有的來訪人員都要經由前臺工作人員登記、確認后才會被引導到具體的會客室,前臺可以對來訪人員的身份進行初步認定和篩選。請求分流的性質也是如此,確保只能通過一個入口進入程序;標準化: 每一個請求都會以相同的方式進行分流處理。如同造訪公司,如果對每一個人的來訪有區別或者說沒有統一的接待標準,一定會產生額外的工作量。統一協調,標準化項目,可以高度簡化處理流程。
Spring MVC 中使用重定向很簡單,只需要在返回值中添加 redirect 關鍵字。@RequestMapping("/response03")public String response03(ModelMap model) throws IOException { //發送給客戶端的響應數據 String hello="Hello"; model.addAttribute("data", hello); return "redirect:/hello";}重定向和轉發的本質區別在于如何找到視圖。轉發是控制器自己找的,也就是在服務器端找的。重定向則是先把視圖地址寫入響應包,然后發送瀏覽器,意思是說,瀏覽器,麻煩你自己找一下。瀏覽器獲取到響應包中的地址后再發送一次請求,找到視圖,然后,把數據模型中的數據讀出來顯示在頁面中。相比較轉發,重定向會多一次請求,也意味著數據模型中的數據需要在跨請求間被解析到。另外,視圖文件必須放置在瀏覽器能訪問到的位置。如果視圖文件放在 WEB-INF 目錄下,則重定向是不能訪問到的。
限定請求參數和限定請求方法同工異曲。所謂限定請求參數,Spring MVC 會檢查請求包中是否包含符合要求的請求參數。通過 @RequestMapping 注解中的 params() 方法實現參數篩選。如下面的實例:@RequestMapping(value="/test", params="userId") public String test(){ ... }test()方法只會響應請求包中包含有 userId 參數的 URL。params()方法支持條件運算符構建的表達式。params=“userId”: 指揮響應請求包中包含有名為 userId 參數的請求;params="!userId" : 如果請求包中有名為 userId 的請求參數,則不響應,否則響應;params=“userId!=1”: 對響應的請求有更多的要求,除了請求包中必須包含 userId 參數外,其值必須是 1;params={“userId=1”,“userName”}: 響應的條件是,請求包中必須包含名為 userId 和 userName 這兩個參數,且 userId 參數的值必須為 1。Tips : 方法、參數限制可同時使用。@RequestMapping(value="/test",method = RequestMethod.POST,params = {"userId"})public String test() { return null;}
Tips: 不要看錯了,是 JSR 不是 JSP。JSR: Java 官方提供的數據合法性標準驗證框架,它只是一個規范?,F有多個版本:Bean Validation 2.0 (JSR 380) ;Bean Validation 1.1 (JSR 349);Bean Validation 1.0 (JSR 303)。準確的講,Spring MVC 驗證框架只是集成了 JSR 驗證框架,并沒有太多自己的具體實現。 JSR 提供了很多驗證注解,一般放在要驗證的 Bean 類型的屬性前面。@Null: 被注解的屬性的值必須為空;@NotNull: 被注解的屬性的值可以不為空;@Min(value): 注解數字類型的屬性,其值大于等于指定的值;@Max(value): 注解數字類型的屬性,其值小于等于指定值;@Size(max, min): 注解的屬性值的大小必須是在給定的范圍內(包括邊界數字);@Past: 注解日期類型屬性,必須是一個過去的日期;@Future: 注解日期類型屬性,必須是一個將來的日期;@Pattern(regexp): 使用正則表達式驗證屬性的值;@Length(min,max): 屬性值的長度在給定的范圍之內(包括邊界數字)。Tips: 更多的驗證注解大家可以查閱 JSR 官方文檔。了解了這些注解后,現在開始使用。
Thymeleaf 與其它的視圖技術相比較,最大的優點在于動靜結合。何謂動靜給合?Thymeleaf 的模板是純正的 html 代碼。Thymeleaf 提供的模板語法指令都是以 HTML 標簽屬性方式嵌入進去的,沒有為頁面添加額外的元素,頁面的整體風格不會被破壞。當運行在服務器端時,才會由模板引擎解析。如果直接由瀏覽器打開,瀏覽器會忽視 Thymeleaf 指令屬性,把模板文件當成 HTML 代碼,可以直接在瀏覽器顯示。動靜結合的優點:不影響前后端工程師對頁面的設計和調整;沒有在頁面中侵入非 HTML 語言代碼,保持了原始頁面的風格。除了動靜給合,還有一個較大的特點就是與 Spring MVC 或 Spring Boot 完美結合,Spring 提供有對接 Thymeleaf 的接口組件,使用起來方便直接。正因為 Thymeleaf 的優點,建議作為項目開發中的首選方案。
Spring MVC 項目中的用戶控制器用來處理用戶的請求,無論處理的結果如何,都需要給用戶一個響應,HTTP 響應包可以說是這個響應結果的載體。理論上講,用戶控制器處理完請求,得到的結果數據可以直接寫入到響應包中。@Controllerpublic class ResponseAction {@RequestMapping("/response01")public void response01(HttpServletResponse response) throws IOException { //發送給客戶端的響應數據 String hello="hello"; PrintWriter out =response.getWriter(); out.write(hello); out.close();}}把需要響應給客戶端的數據寫入響應包中便是響應的本質。如果僅僅只是把數據發送給客戶端,數據在瀏覽器中顯示時,出來的樣式會過于簡單、甚至丑陋。要解決這個問題,也好辦,發送數據時,也附帶發送數據格式。Tips:如果客戶端只需要純數據,如 JSON 格式,則可以直接使用上面的方法。修改上面的響應數據:String hello="<font color=\"red\">hello</font>";這時,在瀏覽器中不僅能看到數據,還能用設計好的樣式顯示出來。初期 WEB 開發,便采用了這種 “數據 + 樣式” 的方式。因初期頁面中數據并不是很多,人為對于頁面無素顯示也沒有多大需求。但是,隨著項目功能越來越大,數據量成倍增加,比如說商城首頁,需要顯示當前登錄者信息、商品信息、推薦的商品信息、用戶瀏覽信息…… 并且用戶對最終顯示結果也提出了更多要求,如美觀、大方、整潔……如果還是如前面一樣,把數據和 HTML 一起編織在一起,然后響應給客戶端,代碼將變得丑陋不堪。新的解決方案是采用組件化開發思想:控制器處理數據,視圖組件提供模板樣式用來顯示最終數據。所以在構建響應包時,控制器需要 2 方面信息:數據:由控制器返回;視圖:由視圖解析器組件維護。Spring MVC 提供數據模型組件充當數據和視圖之間的橋梁??刂破飨劝烟幚砗蟮臄祿4娴綌祿P椭校徽业揭晥D,由視圖從數據模型中取得數據,并顯示在視圖中。重定向和轉發的區別在于尋找視圖的方式。
在項目的 WEB-INF 目錄下新建 templates 目錄,并在此目錄下新建名為 hello.html 的文件。Tips: hello.html 雖然擴展名是 html。 其實是遵循 Thymeleaf 模板語法規范的模板文件。本課程主要講解在 Spring MVC 中如何使用 Thymeleaf , 會講解一點 Thymeleaf 模板語法,但更多的了解需要你移步到 Thymeleaf 的官方網站:https://www.thymeleaf.org/。hello.html 文件的內容:<div> <table> <thead> <tr> <th>name</th> <th>password</th> </tr> </thead> <tbody> <tr> <td th:text="${user.userName}"></td> <td th:text="${user.userPassword}"></td> </tr> </tbody> </table></div>Tips: hello.html 文件內容和普通的 HTML 頁面沒有多大的區別,區別于在 HTML 頁面標簽中使用了 Thymeleaf 提供的一些語法元素,如:th:text 用來動態讀取作用域中的數據。編寫控制器;@Controller@RequestMapping("/thymeleaf")public class ThymeleafAction { @RequestMapping("/hello") public String thymeleaf(ModelMap map) { User user=new User("mk", "123456"); map.addAttribute("user", user); return "hello"; }}發布項目、啟動 tomcat、打開瀏覽器,在瀏覽器中輸入: http://localhost:8888/sm-demo/thymeleaf/hello。Tips: 再聲明一下,為了讓 Thymeleaf 的測試更干凈,注釋或刪除掉原來配置過的視圖技術相關信息。Thymeleaf 的語法元素也稱其為指令,以 th 開頭,如前面用到的 th:text。
每逢春暖花開的時節,我都會想起大學時代。那時候的我,在陽光明媚的日子里,坐在圖書館的落地窗前。桌子上是一疊 Java Web 書本,還有我那破破卻可愛的筆記本電腦。你是否也偶爾懷念,大學時代的似水流年(圖片來源于網絡,版權歸原作者所有)那是 SSH 風華正茂的年代,Spring 如日中天,負責整合各種框架,儼然一副老大哥的樣子;Hibernate 是數據持久層的不二之選,iBatis 在它面前就像個小老弟;Struts 則是 MVC 框架的形象代言,不懂點 Struts 都不好意思說在做 Web 開發。而我卻總是,被 SSH 繁瑣的配置困擾。SSH 各有一大堆配置,當他們碰到一起,還需要額外互相配置。就像三個老朋友,每次再重逢,還要互相介紹。做一個簡單的項目,竟有一大半時間在配置。不是在編輯配置文件的路上,就是在修復配置錯誤的途中。程序開發不應該是簡單而優雅的嗎?正如我們所追求的生活。
本小節主要介紹在 Spring MVC 項目中如何集成 log4j 2 日志系統。打開項目的 pom.xml 文件,添加 log4j 的依賴包;<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.10.0</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.10.0</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-web</artifactId> <version>2.10.0</version></dependency>Tips: 這里有 3 個依賴包,log4j-web 是針對于 WEB 應用程序的依賴包。新建名為 log4j2.xml 的配置文件;Tips: log4j2 不再支持 properties 格式的文件,只支持 xml,json 或是 yaml,不指定位置的情況下默認在 src/main/resources 下查找。提供如下最基礎的配置內容:<?xml version="1.0" encoding="UTF-8"?><Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{yyyy/MM/dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" /> </Console> <RollingRandomAccessFile name="smlog" fileName="sm.log" filePattern="$${date:yyyy-MM}/sm-%d{yyyy-MM-dd}-%i.log.gz"> <PatternLayout pattern="%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n" /> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="10 MB" /> </Policies> <DefaultRolloverStrategy max="20" /> </RollingRandomAccessFile> </Appenders> <Loggers> <Logger name="org.springframework" level="DEBUG" /> <Root level="info"> <AppenderRef ref="Console" /> <AppenderRef ref="smlog" /> </Root> </Loggers></Configuration>log4j2 配置內容簡要說明:Appender: 信息輸出位置,可以有多個輸出口。 SYSTEM_OUT 的目標是 Console 。表示把日志內容輸出到控制臺上。Root Logger 的級別是 info。所有 info 及以上級別的日志才會記錄;Tips: 日志級別分別有 TRACE、 DEBUG 、NFO 、WARN 、ERROR 、 FATAL 這幾種,日志級別從左向右依次增加。日志信息的輸出由當前日志級別決定,只有比當前級別高的信息才能輸出。RollingRandomAccessFile: 表示以文件方式記錄??梢栽O置日志文件的文件名以及格式,一般會加上時間戳;Tips: 本文側重于講解在 Spring MVC 項目中如何使用 log4j 日志系統。log4j 其它的配置信息大家可以查閱官方文檔: 。如果希望日志信息既輸出到控制臺,又能輸出到文件中,務必使用 AppenderRef 標簽引用控制臺配置名稱和文件配置名稱。<Root level="info"> <AppenderRef ref="Console" /> <AppenderRef ref="smlog" /></Root>log4j2 的配置文件開發者可以根據需要存放在其它位置,但需要在 web.xml 文件中配置 log4j 提供的監聽器。<context-param> <param-name>log4jConfiguration</param-name> <param-value>classpath:log4j2.xml</param-value></context-param><listener> <listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class></listener>Tips: 最佳方案是把 log4j 的 log4j2.xml 配置文件放在默認位置,避開上面的配置。Servlet2.5 以上,可以不用配置監聽器。使用測試。使用很簡單,在你需要使用日志的地方,創建一個日志對象。public class Test { static Logger logger = LogManager.getLogger(Test.class); public static void main(String[] args) { logger.info("info","messgae"); }}運行上面代碼,除了在控制臺上輸出信息外,且還會把內容寫入到日志文件中。日志對象的 i 常用方法如下:debug():輸出 debug 級別信息;error():輸出 error 級別的信息;fatal():輸出 fatal 級別的信息;info():輸出 info 級別的信息;trace():輸出 trace 級別的信息;warn():輸出 warn 級別的信息。以上幾個方法除了語義上的區別,使用起來沒有本質的區別。通過語義上的差異性,log4j 可以控制信息的輸出級別。
大家對于使用 Spring 框架開發項目已經司空見慣了… 但是對于它的功能或者作用,描述出來總是差點什么,那么現在咱們詳細聊一聊它的核心功能。核心功能:控制反轉(IoC): 簡單理解 IoC 是一種設計模式,將實例化對象的控制權 由手動的 new 變成了 Spring 框架通過反射機制實例化;依賴注入(DI): 首先理解依賴,程序運行的需要可以稱之為依賴。由于 Spring 框架通過反射技術實例化了對象,并將對象的實例存入在容器進行管理。那么如果一個類中的屬性為某個其余的類,屬性無需手動賦值,通過 spring 的配置文件,或者 Spring 提供的注解,通過 spring 框架可以實現直接注入屬性;面向切面編程 (AOP): 何謂切面,切面是數學中的一個概念,表示只有一個點接觸到球體的一個平面稱呼為切面,而接觸點稱呼為切點。那么在 Spring 中,切面編程指的就是在程序運行某個方法的時候,不修改原始執行代碼邏輯,由程序動態地執行某些額外的功能,對原有的方法做增強,這就叫做面向切面編程,那個被監測的執行方法,稱呼為切入點。知識小結:Spring 是分層的 Java SE/EE 應用 輕量級開源框架,以 IoC(Inverse of Control:控制反轉)和 AOP(Aspect Oriented Programming:面向切面編程)為內核,提供了展現層 Spring MVC 和持久層 Spring JDBC 以及業務層事務管理等眾多的企業級應用技術,還能整合開源世界眾多 著名的第三方框架和類庫, 是使用最多的 Java EE 企業應用開源框架。使用 Spring 的意義在于:對于 bean 對象的實例管理更加方便,代碼編寫更加優雅,降低代碼的耦合性,提升代碼的擴展性。
準備工作完成,現在實現文件上傳。編寫提交表單;<form action="upload" method="POST" enctype="multipart/form-data"> 選擇文件:<input type="file" name="file" value="" /> <br /> <input type="submit" name="upload" value="上傳" /></form>表單的 enctype 屬性有如下幾個選擇:application/x-www-form-urlencoded : 在發送前編碼所有字符,數據以字符串的方式發送;multipart/form-data: 不對字符編碼,使用包含文件上傳控件的表單時,必須使用該值;text/plain: 空格轉換為 “+” 加號,但不對特殊字符編碼。Tips: 因為表單中包含有文件上傳控件,所以,一定要設置表單的 enctype 值為 multipart/form-data。獲取項目上下文絕對路徑;編寫控制器之前,先打開 web.xml ,注冊一個 org.springframework.web.util.WebAppRootListener 監聽器,通過此監聽器獲取到 Spring MVC 項目的發布的絕對路徑,用來為上傳的文件指定存儲位置。<context-param> <param-name>webAppRootKey</param-name> <param-value>webapp.root</param-value></context-param><listener> <listener-class>org.springframework.web.util.WebAppRootListener</listener-class></listener>Tips: webapp.root 相當于一個變量,可以是符合規范的任意命稱。當程序啟動時,此監聽器會把服務器絕對路徑保存在此變量中。使用監聽器的方式獲取路徑可以有效地減少開發者的編碼量。當然,完全可以不使用此監聽器,開發者可以在控制器中通過注入原生 Servlet API 編碼獲取。編寫控制器:@Controllerpublic class UpLoadAction { @RequestMapping("/upload") public String upload(@RequestPart("upFile") byte[] file) throws IOException { String path = System.getProperty("webapp.root"); String filePath = path + "\\upload\\temp.png"; FileOutputStream fileOutputStream = new FileOutputStream(filePath); FileCopyUtils.copy(file, fileOutputStream); return "success"; }}解釋上面的代碼:System.getProperty(“webapp.root”) 可以得到監聽器組件得到的項目上下文路徑。在項目的根目錄下新建 upload 目錄,用來存儲上傳過來的文件;@RequestPart(“upFile”) 注解能注入表單提交上來的文件數據,此數據以 byte[] 類型保存。使用 @RequestPart(“upFile”) 注解的方式存在些問題,不能獲取上傳文件的文件名等其它元數據信息。實例測試。打開瀏覽器,顯示上傳頁面。在本地首先選擇好需要上傳的文件,然后點擊上傳。找到 tomcat 中的項目發布目錄,可以找到剛上傳的文件。
目前,主流的 Web 框架可以按照語言類型進行分類,比如基于 Java 開發的 Web 框架、基于 Python 開發的 Web 框架和基于 Go 開發的 Web 框架等等。每種語言領域內的 Web 框架也是各有特色,有大而全,有小而精,還有專注異步高性能等等。熱門的 Python Web 框架有:Django:基于 MTV 的框架模式,有強大的數據庫功能、強大的后臺管理功能、模板系統、緩存系統等;Flask:小而精的 Web 框架典范,可擴展性強;Tornado: 輕量級的 Web 框架,其特點是非阻塞和高性能,是實時 Web 服務的一個 理想框架。主流的 Java Web 框架有:Spring/Spring Boot/Spring MVC 等:幾乎是大部分 Java web 開發者的首選和必選,占據了大部分市場?;?Spring 及其衍生框架,我們能迅速開發一個 Java Web 服務,幾乎不需要任何 Web 開發基礎;Dubbo:阿里巴巴的開源的高性能 RPC 框架、特點是分布式、高性能以及高度可擴展;Struts2:老一代的 Java Web 框架,特點是高度成熟。不過目前趨勢來看,已經很少人使用 Struts2 來開發新的 Web 服務。Go 作為近幾年快速崛起的后端開發語言,也受到了廣大后端開發者的追捧,Go Web 框架也隨之而來,其中的典型代表有:Beego 框架:它類似于 Python Web 框架 Django,走大而全的風格,具備各種 Web 應用程序的通用功能;Gin 框架:Gin 是 Go 的一個微框架,封裝優雅,接口友好。具有快速靈活,容錯方便、性能優異等特點;Echo 框架:Go 的微型 Web 框架。其具備快速 HTTP 路由器、支持擴展中間件,同時還支持靜態文件服務。
JAVA 中的數據很多時候都是以 OOP 的形式存在的,如學生對象數據、老師對象數據、用戶對象數據……那么,控制器中的方法能不能直接把數據以對象為單位寫入響應包后返回給瀏覽器了?如下面的實例:@RequestMapping("/test02")@ResponseBodypublic User testJson01() { return new User("mk", "123");}打開瀏覽器,在地址欄上輸入:http://localhost:8888/sm-demo/json/test02 。在瀏覽器你將看到如下圖所示結果:拋異常了。對于出錯,大家應該有預感。User 是 JAVA 語言中的類類型,對于瀏覽器而言對它的了解是一片空白。但是,為什么前面返回字符串時卻可以了?那是因為字符串也是一種通用類型,瀏覽器沒有不認識的道理,但是,User 類型,瀏覽器只能摸后腦勺了。如果想讓瀏覽器識別出 User 類型數據。想想也簡單,自己編碼,把對象數據轉換成字符串格式。@RequestMapping("/test03")@ResponseBodypublic String testJson03() { User user=new User("mk", "123"); return user.getUserName()+","+user.getUserPassword();}經過上面的修改后,瀏覽器中能顯示出數據。但是,這里會有 2 個問題需要思考一下:如前所述,前后端分離最主要的思想是讓前端承擔一部分數據業務邏輯。一串沒有特定格式的字符串傳遞給前端,真要交給 JS 處理,你還真不怕 JS 煩心,你叫它如何從中識別出誰是誰;直接返回值給瀏覽器之前,需要通過手工編碼的方式把 OOP 數據格式轉換成字符串,這番折騰,勞心勞力。好!先解決第一個問題。字符串數據類型是非結構化的,但是,可以把它轉換成具有特定結構格式的 JSON 字符串。@RequestMapping("/test04")@ResponseBodypublic String testJson04() { User user = new User("mk", "123"); String json = "{\"userName\":\"" + user.getUserName() + "\",\"userPassWord\":" + "\"" + user.getUserPassword() + "\"" + "}"; return json; }打開瀏覽器,地址欄中輸入 http://localhost:8888/sm-demo/json/test04 。瀏覽器中將顯示如下信息:傳遞給瀏覽器的雖然還是字符串,但是是具有特定格式的 JSON 字符串,如果要交給 JS 處理,JS 表示很開心。是的,數據格式的問題解決了,但是,編碼的工作量增加了很多。其實,你所想要的結果,Spring MVC 能輕松幫你實現。