Sping MVC不使用任何註解處理(jQuery)Ajax請求(基於XML配置)
- 2019 年 11 月 3 日
- 筆記
1. Spring
Spring框架是一個輕量級的解決方案,是一個潛在的一站式商店,用於構建企業就緒的應用程式。Spring框架是一個Java平台,為開發Java應用程式提供全面的基礎架構支援。Spring處理基礎結構,因此您可以專註於應用程式。Spring使您能夠從“普通的Java對象”(POJO)構建應用程式,並將企業服務非侵入性地應用於POJO。此功能適用於Java SE編程模型以及全部和部分Java EE。但是,Spring是模組化的,允許您僅使用所需的那些部分,而不必引入其餘部分。您可以將IoC容器與頂部的任何Web框架一起使用,但也可以僅使用 Hibernate集成程式碼或JDBC抽象層。Spring框架支援聲明式事務管理,通過RMI或Web服務對邏輯的遠程訪問以及用於持久化數據的各種選項。它提供了功能全面的MVC框架,並使您能夠將AOP透明地集成到軟體中。Spring被設計為非侵入式的,這意味著您的域邏輯程式碼通常不依賴於框架本身。在您的集成層(例如數據訪問層)中,將存在對數據訪問技術和Spring庫的某些依賴關係。但是,將這些依賴項與其餘程式碼庫隔離起來應該很容易。Spring的兩大核心特徵:IoC(控制反轉),AOP(面向切面編程)。IoC作用:把對象的控制權交給容器管理。AOP作用:面向切面編程(比如日誌列印),底層使用動態代理實現。Spring框架包含組織為約20個模組的功能。這些模組分為核心容器,數據訪問/集成,Web,AOP(面向方面的編程),檢測,消息傳遞和測試。Spring的整個完整框 架來說,其設計原則則是”對擴展開放,對修改閉合”(OOP設計原則)。當然Spring還有很多強大的功能,這裡先簡單介紹一下,點到為止。
2. Spring MVC
Spring MVC是基於Servlet API構建的原始web框架,從一開始就已經包含在就包含在Spring框架中,與Spring框架無縫對接。全稱應該為Spring Web MVC,其來源於模組spring-webmvc。但是,通常我們叫它Spring MVC(習慣)。Spring MVC框架是圍繞一個 DispatcherServlet (核心控制器)來設計的,這個 Servlet會把請求分發給各個處理器,並支援可配置的處理器映射、視圖渲染、本地化、時區 與主題渲染等,甚至還能支援文件上傳。”對擴展開放”是Spring Web MVC框架一個重要的設計原則。Spring Web MVC核心類庫中的一些方法被定義為 final 方法。Sp’ring MVC的數據綁定非常靈活方便,視圖解析也設計的非常靈活與方便,並且提供了好多功能強大的註解機制。當然,Spring MVC的強大之處不是一兩句話可以搞定的,我們應該參考其文檔,並且深入學習鑽研,最好研究研究源碼。這裡話不多說,點到為止。
3. Spring MVC處理(jQuery)Ajax請求(前台不發送數據,後台返回普通字元串)
開發環境:Eclipse+Tomcat+Spring MVC+Jackson+JSP+jQuery+Ajax
本篇部落格比較長,原本是一個小的demo,寫這麼長不值,不,我認為值,因為我感覺自己寫的還可以(哈哈!裝一波),這些都是筆者一字一句寫的,當然有參考一些東西(本人最討厭複製粘貼),加上自己的理解,涉及到程式碼完全認真實踐過,自己從頭到尾寫一遍,也是對自己知識的積累以及經驗的提升,Spring MVC文檔和Spring文檔等,請大家認真看下去,看完這篇,你不僅掌握了Ajax,認識了Spring的好處,還會熟知Spring MVC的執行流程,還會接觸到有趣的日誌列印。當然也可以免費獲取jar包(有源碼鏈接),百度都有(基本都免費)。所以普通的一些jar包都是免費的,沒有必要花一些代價去活得開發包。本質來說,框架只是個模板,我們遵從框架的規範來開發就行了,畢竟使用一些框架是我們的開發變得簡單高效,筆者認為還是應該了解一下底層核心的原理比較好,這樣便於在我們開發的時候遇到bug的時候可以迅速做出決策和解決。寫部落格,我是認真的。
(1)搭建環境
Eclipse中新建Java Web項目,並把項目部署到Tomcat容器中。下面是項目的結構:
項目基本結構很簡單,沒啥可說的。這裡說一下lib裡面的jar包。既然我們想使用Spring MVC進行開發,必須導入其開發包。上面說了,Spring MVC其實是集成在Spring中的,所以也是導入Spring開發包。本次使用spring-framework-4.0.0.RELEASE開發包。
Spring開發包:spring-aop面向切面編程所用到的包。spring-aspects提供對AspectJ(面向切面的一個框架)的支援,spring-beans包含訪問配置文件,創建和管理Bean以及進行控制反轉和依賴注入操作相關的所有類。spring-core是Spring的核心包(核心工具類)。spring-expression是Spring的表達式語言,spring-jdbc它包含了spring 與 JDBC 數據訪問時進行封裝的所有類,提供使用springjdbc的最佳實現(利用jdbc template)。spring-orm是Spring對DAO特性進行擴展,支援一些ORM(對象關係映射)框架(比如MyBatis和Hibernate等)。spring-test提供了對Junit等測試框架的簡單封裝,這讓我們在對Spring的程式碼進行測試時更加方便和快捷。spring-tx包為JDBC、Hibernate、JDO、JPA等提供了一致的聲明式的編程式事物管理。spring-web包含web應用研發時用到Spring框架時所需要的的核心類。spring-webmvc包含了Spring webmvc框架相關的所有類。
log4j日誌資訊包。不導這個包的話會報錯。
Jasckson三個包(前幾篇部落格說過好多遍)。這裡導入Jackson包的原因說一下。1. Spring MVC內置的json與對象轉換器依賴於Jackson類庫。(底層通過對Jackson的一些方法進行封裝實現)。2. 簡單好用我感覺,效率也還可以。當然也可以實現自己的json與對象的轉換器(題外話)。
com.springsource三個jar包,主要是為了提供Spring對Apache的一些服務的支援。Tomcat就是Apache的,不導這3個包的話會報錯。當然,這裡只是導入了spring的一部分常用的包,還有其他的(自己百度下spring開發包,在裡面找找),本次開發也沒把這些spring包全部用到,主要是為了著重介紹一下。好了,jar包說完了。下面進行開發。這裡不使用任何註解的目的說一下,只是基於XML配置實現。其實我們使用註解會簡單方便很多(後面會有更新的),此篇文章主要是為了便於大家熟悉Spring MVC的執行流程,並使用它最最原始的一些配置以及方法。方便大家了解更深層次的東西,以及深入了解Spring MVC一些核心的東西。因為我們天天使用註解,但是估計我們都沒了解過註解的機制以及原理,註解有什麼用。以及註解代替了那些我們之前開發比較繁雜的操作以及方法。有時候我們研究一些東西,不能只停留在表面會使用就行了,我們深入到底層的話會有意想不到的收穫及理解。
(2)編寫jsp文件
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/"; %> <!DOCTYPE html> <html> <head> <base href="<%=basePath%>"> <title>I LOVE YOU</title> <link rel="stylesheet" type="text/css" href=""> <script type="text/javascript" src="index.js"></script> <script type="text/javascript" src="jquery-3.2.1.min.js"></script> </head> <body> <button id="mybutton1" value="springmvc處理ajax請求" onclick="fun1()" >(jquery)ajax請求(不發送數據)</button> <spand id="show1" /> <br/> <hr/> </body>
很簡單,定義了一個按鈕。並調用js文件相關的函數進行處理,<span>標籤是為了顯示內容。頁面引入自定義的js文件和jQeury的js文件(注意名稱和路徑)。
(3)編寫js文件
/** * */ //使用jquery提交ajax請求(不攜帶數據) function fun1(){ $.ajax({ type:"POST", //發送方式 url:"UserController1", //請求地址 data:"", //數據為空 success:function(data){ //成功後的回調函數 $("#show1").html(data); //頁面展示內容 } }); }
很簡單,就寫了一個fun1()函數。用來響應按鈕。雖然說,討論客戶端不發送數據沒多大意義,這裡我們還是討論一下吧。在學習java基礎的時候,我們還經常討論”空”呢。不是嗎?這裡主要是為了熟悉一下程式碼執行的流程,畢竟我們學習的過程是由淺入深的。閑話少敘。
(4)編寫controller類
package com.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.HttpRequestHandler; public class UserController1 implements HttpRequestHandler{ @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setCharacterEncoding("UTF-8"); String str = "我是一個 springmvc"; try { response.getWriter().print(str); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
這裡定義了一個controller類,並且實現了HttpRequestHandler介面,並實現介面中的抽象方法。方法中定義了一個字元串,利用response獲取列印流進行輸出即可。Spring MVC中。一般在我們編寫controller(也就是一個handler,用來處理客戶端的請求),要能被Sping MVC識別它是一個controller類,因為這裡沒有用到註解。所以我們有2種方法。
第一種:編寫controller類,實現org.springframework.web.servlet.mvc.Controller介面,然後交給核心控制器調用相對應的處理器適配器進行處理。不過,實現了Controller,就必須實現其中的抽象方法public abstract ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)。這個方法必須返回一個ModelAndView對象,而我們所作的是對字元串數據和json數據的處理。所以在這裡我們並沒有選擇實現Controller介面。
第二種:編寫controller類,實現org.springframework.web.HttpRequestHandler介面,然後交給核心控制器調用相應的處理器適配器進行處理,並實現其中的抽象方法。自己可以百度一下Spring MVC的執行流程,這裡不多說。
public abstract void handleRequest(HttpServletRequest request, HttpServletResponse response) 我們注意到,這個方法是沒有返回值的,所以就方便了我們對字元串數據和json數據的處理。注意到有2個參數request和response(這是Servlet的東西)。Spring MVC支援的支援的參數類型:
HttpServletRequest 對象,HttpServletResponse 對象,HttpSession 對象,Model/ModelMap 對象。這裡我們用到了其中的2個。
(5)配置web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>MySpringMVCAjax</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 配置springmvc核心控制器 --> <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.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 對靜態資源進行放行 --> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/static/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <!-- 配置編碼過濾器 --> <filter> <filter-name>SpringCharacterEncodingFilter</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>SpringCharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
這裡我們配置了三個東西。
第一個,配置Spring核心控制器。Sping MVC核心控制器(前端控制器或中央處理器)org.springframework.web.servlet.DispatcherServlet,DispatcherServlet 其實就是個 Servlet (它繼承自 HttpServlet 基類),同樣也需要在你 web應用的 web.xml 配置文件下聲明。你需要在 web.xml 文件中把你希望 DispatcherServlet 處理的請求映射到對應的URL上去。這就是標準的Java EE Servlet配 置。DispatcherServlet就是一個Sping MVC處理請求的一個中央處理器,負責調度處理器映射器,處理器適配器以及視圖解析器。配置初始化參數來載入Sping MVC的主配置文件springmvc.xml,這裡我們把它放在src目錄下。
第二個,給靜態資源放行。這裡注意一下。我們在核心控制器<url-pattern>標籤下配置的是 / ,表示所有訪問的url都交給核心控制器去處理,這樣導致有可能載入不到靜態資源文件(js,css,img)。解決辦法:
第一種:在springmvc.xml文件中配置<mvc:default-servlet-handler /><!– 表示對所有靜態資源進行放行 –>(比較高級的方法)
第二種:對靜態資源進行放行,如我們web.xml所示。當然,也可以在spring.xml文件進行放行,語法為:
<resources location="/js/" mapping="/js/**" /> <resources location="/css/" mapping="/css/**" /> <resources location="/images/" mapping="/images/**" />
這裡使用web.xml對靜態資源進行放行,主要是因為這個方法簡單易懂。後面我會對部落格內容進行更新升級,再採取比較高級的方法。
第三種:配置核心控制器時,把<url-pattern>標籤設置為 *.do 或者 .action就好了。表示以.do結尾的或者以.action結尾的URL都由前端控制器DispatcherServlet來解析。這種方法也可以,但是到時訪問路徑的時候要加上這個後綴,看起來不太舒服(有一點強迫症的人應該懂)。當然筆者估計沒有吧。筆者只是採取一些簡單說不上高效的方法。
第三個,就是配置spring提供編碼過濾器。web.xml配置的編碼過濾器為了防止前端傳入的中文數據出現亂碼問題,使用Spring提供的編碼過濾器來統一編碼。配置為UTF-8。當然也可以使用GBK等其他編碼。GBK編碼方式的編碼是以中國國情而創造的,在國際上的兼容性不好,這也是為什麼大多數的網頁是使用UTF-8編碼而不是GBK。UTF-8好處:兼容ASCII,存儲英文文件都是單位元組,文件小。在eclipse中把項目的編碼也設置為UTF-8,JSP頁面中字元編碼也設置為UTF-8。瀏覽器網頁字元編碼也設置為UTF-8。反正就是統統UTF-8的樣子。做這麼多主要是統一編碼,解決中文亂碼亂碼問題。因為以前使用servlet和struts2的時候,經常出現中文亂碼問題,讓人摸不著什麼頭腦。所以。Spring提供的這個編碼過濾器就比較方便好用了。所以說Spring是一個神奇的東西。我們研讀一下Spring源碼,看看Spring開發的文檔,就可以汲取一些精華(精髓)。Spring提供的編碼過濾器,org.springframework.web.filter.CharacterEncodingFilter類,注意把路徑寫正確。初始化參數(也就是編碼)encoding為UTF-8。Spring里的字元過濾器CharacterEncodingFilter是針對請求的,forceEncoding=true是意思是指無論客戶端請求是否包含了編碼,都用過濾器里的編碼來解析請求。forceEncoding默認為false。forceEncoding為true效果: request.setCharacterEncoding(“UTF-8”);forceEncoding為false的效果:
request.setCharacterEncoding(“UTF-8”); response.setCharacterEncoding(“UTF-8”)。 <url-pattern>標籤配置的/*代表過濾所有請求,從而將編碼設置為UTF-8。好了。web.xml到此結束。其實有時候我們只是開發一個簡單的小例子demo,看起來很簡單。但是我們要了解底層到底是怎麼實現的。比如Java的底層,JVM(Java虛擬機)用c/c++編寫,Java類庫用Java語言編寫。還有比如native本地方法,其底層本質也是c/c++編寫的,Java只是調用了它的介面。這也就解釋了為什麼native方法效率一般比較高。那麼問題來了,c語言用什麼編寫的 ? 這個可以自行Google或百度,這裡不多說。當然,一個項目或者工作的執行流程我們也必須熟知。這次開發看起來很簡單,但是我們把涉及到的知識內容都會為大家詳細講解,並且都是本人一字一句經過推敲實踐進行編寫的。當然,本人也是菜鳥一枚。知識有限。但是我們可以經過學習來獲取更多的知識,提升自己的能力,從而達到人生的巔峰。不多說了(跑題了,哈哈!)。
(6)配置springmvc.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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置user實體類 --> <bean id="user" class="com.pojo.User" /> <!-- 配置handler--> <bean name="/UserController1" class="com.controller.UserController1" /> <bean name="/UserController2" class="com.controller.UserController2" /> <bean name="/UserController3" class="com.controller.UserController3" /> <!-- 配置處理器映射器 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- 配置處理器適配器 --> <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/> <!-- 配置json與對象的轉換器 --> <bean id="myconverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> <!-- 配置視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
springmvc.xml是本次開發的主配置文件。通過觀察得知,頂層標籤<beans>包含了許多bean模組。Spring Bean其實就是是被實例的,組裝的及被Spring 容器管理的Java對象。一個bean相當於一個實體類對象,通過IoC(控制反轉,也叫依賴注入)我們可以把bean注入到容器當中,交給Spring容器管理(比如bean的實例化)。這樣做的目的其實解耦合。因為軟體設計的原則就是高內聚,低耦合。Spring默認的bean實例是單例的,我們可以用scope屬性來設置bean的作用域。scope可以接受Singleton(單例模式,每次獲取bean只有一個實例)、prototype(原型模式,每次獲取bean都會產生新的實例)、request(每次HTTP request請求都會產生不同的Bean實例)、session(每次HTTP session請求都會產生不同的Bean實例)、global session (每個全局的HTTPSession對應一個Bean實例。僅在portlet Context的時候才有效。)5個值。我們常用單例模式,因為創建對象本身代價比較高,耗費資源,單例的本身也滿足我們的需求了。當然我們也可以根據不同的需求設置不同的scope,這本無可厚非。還有,注意一下引入的xsd文件,Spring文件中的引用的xsd文件是用於校驗xml文件的格式用的。Spring默認在啟動時是要載入XSD文件來驗證xml文件的。相當於是約束文件。這裡我們引入了三個xsd文件,spring-beans-xsd包含對bean的解釋。sprng-mvc.xsd包含了對<mvc>等標籤的解釋,spring-context.xsd包含對context的解釋,這裡我們其實只用到了spring-beans.xsd約束文件。
我們具體看一下配置了什麼東西。
1. 把User實體類注入到容器(下面會貼User類的程式碼,這裡暫時還沒用到)。相當於是通過<bean>標籤來裝配一個User類,我們就可以通過獲取bean來達到獲取User實體對象的目的。設置bean的id(一個bean的唯一標識)和class(類的全包路徑 包.類)。
2. 配置controller(也就是handler,處理客戶端請求)。這裡我們配置了好三個controller。我們看UserController1(對應上面的方法UserController1)的配置,配置了一個name和class。Spring MVC要識別請求的url,就是通過調用處理器映射器獲取到這個name的名稱,找到controller類,然後交給處理器設配器去處理controller。相當於就是映射路徑。注意名稱前要加上/ 不然無法識別。這裡我們設置name=”/UserController1“和類名保持一致(為了方便),當然也可以起其他名字,別忘了/,前台提交的url和此處name的名稱要保持一致。class為UserController1的全包類路徑。
3. 配置處理器映射器BeanNameUrlHandlerMapping。不需要指定id,只配置全類路徑即可,即class。這個處理器映射器,將bean的name作為url進行查找,需要在配置Handler時指定bean的 name(url)。
查看spring-webmvc包下面的DispatcherServlet.properties 資源文件:
# Default implementation classes for DispatcherServlet's strategy interfaces. # Used as fallback when no matching beans are found in the DispatcherServlet context. # Not meant to be customized by application developers. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping, org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter, org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter, org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver, org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver, org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
Spring MVC的處理器映射器,常用的有2個,一個是org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,繼承了
AbstractDetectingUrlHandlerMapping抽象類,它又向上繼承了AbstractUrlHandlerMapping抽象類,它又向上繼承了AbstractHandlerMapping,
AbstractHandlerMapping抽象類實現了HandlerMapping介面。
另一個是org.springframework.web.servlet.handler.SimpleUrlHandlerMapping,繼承了AbstractUrlHandlerMapping抽象類,
AbstractUrlHandlerMapping繼承了AbstractHandlerMapping抽象類,AbstractHandlerMapping實現了org.springframework.web.servlet.HandlerMapping
介面。觀察資源文件發現,BeanNameUrlHandlerMapping是Spring MVC的默認處理器映射器,這裡我們就使用這個。若要使用SimpleUrlHandlerMapping,
我們根據它的語法來就行了。可以這樣配置:
bean id="UserController1" class="com.controller.UserController1" /> <!-- 簡單URL配置處理器映射器 --> <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="mappings"> <props> <prop key="/UserController1">UserController1</prop> </props> </property> </bean>
當然這兩種處理器映射器配置可以並存,核心控制器會正確的去判斷 url 用哪個 Handler 去處理。
注意這個上面資源文件的處理器映射器DefaultAnnotationHandlerMapping,通過查看源碼,它已經被廢棄了。被拋棄的感覺不好受啊。
(估計DefaultAnnotationHandlerMapping的內心是崩潰的)。
4. 配置處理器適配器。不需要指定id,class為全類路徑。核心控制器調用處理器映射器找到了Controller類,那麼誰來處理這個Controller呢。那麼此時處理器適配器就閃亮登場了。什麼是處理器適配器呢。且聽下回分解,本章完。。。(皮一下)。通過觀察以上資源文件。我們發現。Spring MVC的處理器適配器,常用的有2個。
一個是org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,一個是org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
這2個類都實現了HandlerAdapter介面。SimpleControllerHandlerAdapter處理的Handler必須實現Conreoller介面,一般返回一個ModelAndView對象。
HttpRequestHandlerAdapter處理的Handler必須實現HttpRequestHandler介面,一般用來處理字元串或者json數據。因為其抽象方法沒有返回值。AnnotationMethodHandlerAdapter已經廢棄了,不多說。當然兩種適配器可以共存,配置不同的映射器找到不同的controller。
Spring MVC默認的處理器適配器是HttpRequestHandlerAdapter 。
5. 配置視圖解析器。不需要指定id,class為全類路徑。我們這裡用的是InternalResourceViewResolver,配置它的class路徑,注意有2個屬性。prefix表示返回視圖頁面的前綴,suffix表示返回視圖頁面的後綴。比如一般我們要視圖解析完成後返回一個頁面index.jsp時,prefix相當於配置的是其根目錄(在哪兒),suffix就是.jsp,這樣在我們使用註解進行開發的時候,只需要返回一個字元串”index”就行了。上面那樣配置,表示在 Handler 中只需要返回在 WebContent根目錄下的jsp 文件名就ok了(為了簡單方便,開發比較高效)。視圖解析器作用它負責將一個代表邏輯視圖名的字元串 (String)映射到實際的視圖類型 View 上。通過以上資源文件發現。注意HandlerExceptionResolver處理器異常解析器。它負責將捕獲的異常映射到不同的視 圖上去,此外還支援更複雜的異常處理程式碼。Spring MVC的視圖解析器這裡介紹3種:
第一種:使用ViewResolver介面解析視圖
org.springframework.web.servlet.view.InternalResourceViewResolver類,通過連續向上繼承,實現org.springframework.web.servlet.ViewResolver介面。在實際應用中InternalResourceViewResolver也是使用的最廣泛的一個視圖解析器。本次開發就用的是這個視圖解析器。這個比較常用。InternalResourceViewResolver解析器可以解釋為內部資源視圖解析器。InternalResourceViewResolver會把返回的視圖名稱都解析為InternalResourceView對象,InternalResourceView會把Controller處理器方法返回的模型屬性都存放到對應的request屬性中,然後通過RequestDispatcher在伺服器端把請求forword重定向到目標URL。
介面中的方法:通過傳來的參數解析視圖,並返回一個View對象。注意Handler類實現Conteoller介面方法時,就返回了一個ModelAndView對象。ModelAndView 是SpringMVC 框架的一個底層對象,包括 Model 和 View。
public abstract interface ViewResolver
{
public abstract View resolveViewName(String paramString, Locale paramLocale)
throws Exception;
}
第二種:使用RequestToViewNameTranslator介面解析視圖。DefaultRequestToViewNameTranslator為這個介面的實現子類。
這個介面定義了一個抽象方法
public abstract interface RequestToViewNameTranslator
{
public abstract String getViewName(HttpServletRequest paramHttpServletRequest)
throws Exception;
}
表示根據request請求返回一個視圖名稱的字元串。
第三種:使用FlashMapManager介面解析視圖。SessionFlashMapManager為這個介面的實現子類。
FlashMap管理器。它能夠存儲並取回兩次請求之間 的 FlashMap 對象。後者可用於在請求之間傳遞數據,通常 是在請求重定向的情境下使用。
這個介面定義的方法:自己看看就好了,在這裡不做過多介紹。
public abstract interface FlashMapManager
{
public abstract FlashMap retrieveAndUpdate(HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse);
public abstract void saveOutputFlashMap(FlashMap paramFlashMap, HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse);
}
Map肯定時用來保存數據的。RedirectView在頁面跳轉,數據的保存依賴於FlashMap和FlashMapManger,FlashMapManger在容器初始化時被填入,而FlashMap從Manger可以獲取。
5. 配置json與對象的轉換器。這裡暫時用不到。下面會解釋。先跳過。其實Spring MVC內置的json與對象的轉換器底層還是用Jasckson類庫實現。
(7)配置log4j.properties日誌文件
### set log levels ### log4j.rootLogger = INFO , console , D ### console ### log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [%p]-[%c] %m%n ### log file ### log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File =../logs/IvaDubboWeb-info.log log4j.appender.D.Append = true log4j.appender.D.Threshold = INFO log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %m%n ### out zhiding log file ### log4j.logger.haha = INFO, haha log4j.additivity.haha = false log4j.appender.haha = org.apache.log4j.DailyRollingFileAppender log4j.appender.haha.File =D:/logs/mylog.log log4j.appender.haha.Append = true log4j.appender.haha.Threshold = INFO log4j.appender.haha.layout = org.apache.log4j.PatternLayout log4j.appender.haha.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %m%n
Log4j簡單介紹:
Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日誌資訊輸送的目的地是控制台、文件、GUI組件,甚至是套介面伺服器、NT的事件記錄器、UNIX Syslog守護執行緒等;我們也可以控制每一條日誌的輸出格式;通過定義每一條日誌資訊的級別,我們能夠更加細緻地控制日誌的生成過程。Log4j有三個主要的組件:Loggers(記錄器),Appenders (輸出源)和Layouts(布局)。這裡可簡單理解為日誌類別,日誌要輸出的地方和日誌以何種形式輸出。綜合使用這三個組件可以輕鬆地記錄資訊的類型和級別,並可以在運行時控制日誌輸出的樣式和位置。
1. 配置根logger,日誌輸出級別為INFO級別。log4j的輸出級別:TRACE < DEBUG < INFO < WARN < ERROR < FATAL。比INFO級別高的也會列印輸出,比INFO低的不會輸出。
2. 配置console,在eclipse的控制台列印日誌資訊。
3. 配置輸出到文件。一個是輸出到一般性文件中,另一個是輸出到我們指定的文件中。很簡單。(筆者對log4j還琢磨了半天,主要是了解日誌列印到底是什麼玩意兒)。
對上面log4j簡單測試一下(注意要導入log4j開發包和單元測試JUinit包,並把log4j.properties文件放在src目錄下)。列印到控制台:
package com.log4j; import org.apache.log4j.Logger; import org.junit.Test; public class Log4jTest { @Test public void test() { // BasicConfigurator.configure(); //自動快速地使用預設Log4j環境。 Logger logger = Logger.getLogger(Log4jTest.class); logger.info("log4j"); logger.info("是"); logger.error("什麼"); logger.debug("呢"); } }
運行效果:debug級別比INFO低,無輸出。
列印日誌到指定文件:把方法放在一個類中就行了
@Test public void test1() { // BasicConfigurator.configure(); //自動快速地使用預設Log4j環境。 Logger logger = Logger.getLogger("haha"); logger.info("我"); logger.info("要"); logger.info("學"); logger.info("java"); logger.info("哈"); logger.info("哈"); }
運行效果:
(8)運行程式
啟動Tomcat伺服器,在瀏覽器地址欄上輸入:localhost/MySpringAjax/
完美運行,不做過多解釋。
4. Spring MVC處理(jQuery)Ajax請求(前台發送key/value數據,後台返回json數據)
(1)編寫jsp頁面
重複步驟我們一筆帶過就好了。比如jar包都已經導好了,還有log4j.properties文件,springmvc.xml主配置文件,這些都是項目公用的。這裡就不多說了。
<button id="mybutton2" value="springmvc處理ajax請求" onclick="fun2()" >發送數據格式為key/value的(jquery)ajax請求</button> <spand id="show2" />
(2)編寫js頁面
//使用jquery提交key/value數據(ajax請求) function fun2(){ $.ajax({ type:"POST", url:"UserController2", data:"username=wly&password=1314520", //key/value數據 success:function(data){ $("#show2").html(data.username+" "+data.password); } }); }
(3)編寫User類
package com.pojo; public class User { private String username; private String password; private Integer age; public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User [username=" + username + ", password=" + password + ", age=" + age + "]"; } }
這裡我們給User類增加了一個age屬性,前台發送的還是username和password。(增加age屬性主要是為了對User類進行重新的一些操作,就是方便我們演示不同的效果)。
(4)配置web.xml文件
上面已經配置好了,配置三個東西。1. Spring MVC核心控制器;2. 對靜態資源進行放行;3. 配置Spring MVC提供的編碼過濾器
(5)配置springmvc.xml文件
上面已經配置好了,需要配置的bean有,User實體類,Controller類,處理器映射器,處理器適配器,視圖解析器,json與對象的轉換器(本次開發就調用Spring MVC內置的json與對象的轉換器進行進行json數據與對象的轉換)。配置json與對象轉換器的id(唯一識別這個bean)和class(全包類路徑)。
<!-- 配置json與對象的轉換器 --> <bean id="myconverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
這裡我們看一下配置的Spring MVC內置的json與對象的轉換器,這裡我們使用的是MappingJackson2HttpMessageConverter類。我們先介紹一下Spring MVC的2個常用的消息轉換器。
1. org.springframework.http.converter.json.MappingJackson2HttpMessageConverter消息轉換器類,這個類繼承了AbstractHttpMessageConverter<T>抽象類,而這個抽象類實現了package org.springframework.http.converter.HttpMessageConverter<T>介面。我們看一下這個介面的抽象方法。
package org.springframework.http.converter; import java.io.IOException; import java.util.List; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; public abstract interface HttpMessageConverter<T> { public abstract boolean canRead(Class<?> paramClass, MediaType paramMediaType); public abstract boolean canWrite(Class<?> paramClass, MediaType paramMediaType); public abstract List<MediaType> getSupportedMediaTypes(); public abstract T read(Class<? extends T> paramClass, HttpInputMessage paramHttpInputMessage) throws IOException, HttpMessageNotReadableException; public abstract void write(T paramT, MediaType paramMediaType, HttpOutputMessage paramHttpOutputMessage) throws IOException, HttpMessageNotWritableException; } /* Location: F:eclipseWorkspacemyworkspaceMySpringMVCAjaxWebContentWEB-INFlibspring-web-4.0.0.RELEASE.jar * Qualified Name: org.springframework.http.converter.HttpMessageConverter * Java Class Version: 6 (50.0) * JD-Core Version: 0.7.0.1 */
一般來說,我們在Spring MVC實現自定義的json與對象的轉化器時,就應該實現HttpMessageConverter<T>這個介面。MediaType在網路協議的消息頭裡面叫做Content-Type,使用兩部分的標識符來確定一個類型,我們用的時候其實就是為了表明我們傳的東西是什麼類型。MediaType類就是一個媒體類型,定義了好多靜態常量,定義的就是數據的格式。相當於ContentType。靜態常量值有application/json,text/html,image/png,application/xml等等。有好多呢。自己去看源碼好了。canRead()方法表示檢測能不能把java對象轉化為json數據,canWrite表示檢測能不能把json數據轉化為java對象。read()方法用來讀,表示把json數據轉化為java對象,write方法表示寫,把java對象轉換為json數據輸出。MappingJackson2HttpMessageConverter是HttpMessageConverter<T>的實現子類,先來看一下AbstractHttpMessageConverter<T>抽象類定義的2個重要的抽象方法。實現HttpMessageConverter<T>介面中的read()和write()抽象方法。但是調用的還是本類定義的2個抽象方法。再交給起子類去實現這2個方法。
/* */ public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage) /* */ throws IOException /* */ { /* 158 */ return readInternal(clazz, inputMessage); /* */ } /* */ /* */ /* */ /* */ /* */ /* */ /* */ /* */ public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage) /* */ throws IOException, HttpMessageNotWritableException /* */ { /* 170 */ final HttpHeaders headers = outputMessage.getHeaders(); /* 171 */ if (headers.getContentType() == null) { /* 172 */ if ((contentType == null) || (contentType.isWildcardType()) || (contentType.isWildcardSubtype())) { /* 173 */ contentType = getDefaultContentType(t); /* */ } /* 175 */ if (contentType != null) { /* 176 */ headers.setContentType(contentType); /* */ } /* */ } /* 179 */ if (headers.getContentLength() == -1L) { /* 180 */ Long contentLength = getContentLength(t, headers.getContentType()); /* 181 */ if (contentLength != null) { /* 182 */ headers.setContentLength(contentLength.longValue()); /* */ } /* */ } /* 185 */ if ((outputMessage instanceof StreamingHttpOutputMessage)) { /* 186 */ StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage)outputMessage; /* */ /* */ /* 189 */ streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() /* */ { /* */ public void writeTo(final OutputStream outputStream) throws IOException { /* 192 */ AbstractHttpMessageConverter.this.writeInternal(t, new HttpOutputMessage() /* */ { /* */ public OutputStream getBody() throws IOException { /* 195 */ return outputStream; /* */ } /* */ /* */ public HttpHeaders getHeaders() /* */ { /* 200 */ return AbstractHttpMessageConverter.1.this.val$headers; /* */ } /* */ }); /* */ } /* */ }); /* */ } /* */ else { /* 207 */ writeInternal(t, outputMessage); /* 208 */ outputMessage.getBody().flush(); /* */ } /* */ } /* */ /* */ /* */ protected abstract T readInternal(Class<? extends T> paramClass, HttpInputMessage paramHttpInputMessage) /* */ throws IOException, HttpMessageNotReadableException; /* */ /* */ protected abstract void writeInternal(T paramT, HttpOutputMessage paramHttpOutputMessage) /* */ throws IOException, HttpMessageNotWritableException; /* */ }
接下來,MappingJackson2HttpMessageConverter就出現了,其實現了抽象類定義的方法readInternal()和writeInternal,通過觀察發現。readInternal()方法其調用了ObjectMapper(Jackson核心操作類)的readValue()方法,表示將json字元串轉化為java對象。read()方法其實是本類擴展的一個方法,作用和readInternal一樣。writeInternal()方法其調用了ObjectMapper(Jackson核心操作類)的writeValue()方法,表示將java對象轉化為json字元串。
公共介面HttpInputMessage,擴展HttpMessage,表示HTTP輸入消息,由表頭 和可讀主體組成。通常由伺服器端的HTTP請求句柄或客戶端的HTTP響應句柄實現。其實現子類ServletServerHttpRequest封裝了請求頭header和請求體body。
公共介面HttpOutputMessage ,擴展HttpMessage表示HTTP輸出消息,由標頭 和可寫主體組成。通常由客戶端的HTTP請求句柄或伺服器端的HTTP響應句柄實現。其實現子類ServletServerHttpResponse相當於封裝了響應頭和響應體。
/* */ protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) /* */ throws IOException, HttpMessageNotReadableException /* */ { /* 168 */ JavaType javaType = getJavaType(clazz, null); /* 169 */ return readJavaType(javaType, inputMessage); /* */ } /* */ /* */ /* */ public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) /* */ throws IOException, HttpMessageNotReadableException /* */ { /* 176 */ JavaType javaType = getJavaType(type, contextClass); /* 177 */ return readJavaType(javaType, inputMessage); /* */ } /* */ /* */ private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) { /* */ try { /* 182 */ return this.objectMapper.readValue(inputMessage.getBody(), javaType); /* */ } /* */ catch (IOException ex) { /* 185 */ throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex); /* */ } /* */ } /* */ /* */ /* */ protected void writeInternal(Object object, HttpOutputMessage outputMessage) /* */ throws IOException, HttpMessageNotWritableException /* */ { /* 193 */ JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType()); /* */ /* */ /* */ /* */ /* 198 */ JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding); /* */ /* */ /* */ /* 202 */ if (this.objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) { /* 203 */ jsonGenerator.useDefaultPrettyPrinter(); /* */ } /* */ try /* */ { /* 207 */ if (this.jsonPrefix != null) { /* 208 */ jsonGenerator.writeRaw(this.jsonPrefix); /* */ } /* 210 */ this.objectMapper.writeValue(jsonGenerator, object); /* */ } /* */ catch (JsonProcessingException ex) { /* 213 */ throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex); /* */ } /* */ } /* */
2. org.springframework.http.converter.StringHttpMessageConverter消息轉換器類,繼承了AbstractHttpMessageConverter<String>抽象類,而這個抽象類實現了HttpMessageConverter<T>介面。主要用於以讀寫字元串。其實就是一個消息轉換器。默認情況下,該轉換器支援所有介質類型(*/*
),並用寫Content-Type
的text/plain
。可以通過設置supportedMediaTypes該屬性來覆蓋。原理:通過輸入和輸出流的方式進行讀和寫操作。
設計哲學:StringHttpMessageConverter的哲學便是:你想要什麼類型的數據,我便發送給你該類型的數據。
其實很簡單的道理,比如我希望接受的數據類型是Accept: application/json;charset=UTF-8,發送的數據類型Content-Type: application/json;charset=UTF-8 當然也要保持一致。相當於以什麼格式輸入的字元串,就得以相應的格式進行轉換。這裡要深入了解的話請看具體源碼。
(6)配置編寫Controller類
package com.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.web.HttpRequestHandler; import com.pojo.User; public class UserController2 implements HttpRequestHandler{ public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setCharacterEncoding("UTF-8"); //載入spring.xml文件初始化ApplicationContext ApplicationContext ac = new ClassPathXmlApplicationContext("springmvc.xml"); //得到User類的實例(對象),可以看出IoC(把對象交給容器管理)機制(工廠模式+Java反射機制) User user = (User) ac.getBean("user"); //獲取username和password,進行手動封裝 String username=request.getParameter("username"); String password=request.getParameter("password"); user.setUsername(username); user.setPassword(password); //獲取消息轉換器MappingJackson2HttpMessageConverter類對象,實現了HttpMessageConverter介面 HttpMessageConverter converter= (HttpMessageConverter) ac.getBean("myconverter"); //獲取媒體類型,傳入的是"application/json;charset=UTF-8"這裡我們直接new MediaType mediaType=new MediaType(MediaType.APPLICATION_JSON, null); //實例化響應資訊類。ServletServerHttpResponse是HttpOutputMessage的實現子類 //這裡直接new,要是配置到springmvc裡面顯得配置文件冗餘。 HttpOutputMessage out=new ServletServerHttpResponse(response); converter.write(user, mediaType, out); } }
首先要獲取2個bean,一個是配置的轉換器,一個是User類對象。通過載入spring.xml初始化Spring的一個應用上下文ApplicationContext對象,調用方法getBean(“id”)。我們可以獲得bean對象,其實這是通過Java反射機制完成的。這個傳入的id是我我們spring.xml中所配置的id,注意一定要對應,保持一致。關於獲取Spring Bean還有其他方法,自己可以百度。得到user對象後,手動進行封裝。程式碼都有註解,很容易理解。MediaType.APPLICATION_JSON是一個靜態常量。MediaType類中定義的這個靜態常量(還有好多,這裡只列舉一個)public static final MediaType APPLICATION_JSON = valueOf(“application/json”)。HttpOutputMessage封裝輸出的資訊。最後通過消息轉換器的write()方法把對象轉化為json數據進行輸出,響應客戶端。
ApplicationContext是一個中央介面,為應用程式提供配置。在應用程式運行時,它是只讀的,但是如果實現支援,則可以重新載入。
ApplicationContext提供:
1. 用於訪問應用程式組件的Bean工廠方法。繼承自ListableBeanFactory。
2.以通用方式載入文件資源的能力。從ResourceLoader
介面繼承。
3.將事件發布給註冊的偵聽器的能力。從ApplicationEventPublisher介面繼承。
4.解決消息的能力,支援國際化。從MessageSource介面繼承。
5.從父上下文繼承。在後代上下文中的定義將始終優先。例如,這意味著整個Web應用程式都可以使用單個父上下文,而每個servlet都有其自己的子上下文,該子上下文獨立於任何其他servlet的子上下文。
存在問題:這裡的User類對象其實最後還是通過Spring容器new的實例(反射),完全不用註解的話,怎麼自動完成User類對key/value類型數據的封裝呢,我們怎麼得到user類呢。有一種思路(深入了解spring mvc的參數綁定機制,這就要又得研究研究源碼了)。不知道大家還有什麼好的辦法。歡迎交流哈。
(7)運行程式
5. Spring MVC處理(jQuery)Ajax請求(前台發送json數據,後台返回json數據)
(1)編寫jsp頁面
<button id="mybutton3" value="springmvc處理ajax請求" onclick="fun3()" >發送數據格式為json的(jquery)ajax請求</button> <spand id="show3" /><br/>
(2)編寫js文件
//使用jquery提交json數據(ajax請求) function fun3(){ var user={ //符合json數據格式規範的javascript對象 "username":"我是誰", "password":"1314520" }; $.ajax({ type:"POST", url:"UserController3", contentType:"application/json;charset=UTF-8", //發送數據到伺服器時所使用的內容類型 data:JSON.stringify(user), //將javascript對象轉化為json字元串 //預期的伺服器響應的數據類型。伺服器返回json字元串。jquery會自動把json轉化為js對象 dataType:"json", //相當於調用JSON.parse(data)方法。此時我們省去即可。 success:function(data){ $("#show3").html(data.username+" "+data.password+" "+data.age); } }); }
列印資訊時,我們加了一個age屬性注意一下。這裡username設置為中文,主要為了測試會不會出現亂碼(看web.xml配置的編碼過濾器生效沒)。
(3)編寫User類
User實體類和上面一樣。有3個屬性,username,password,age。提供getters和setters方法,toString方法。
(4)配置web.xml
一樣
(5)配置springmvc.xml文件
同理
(6)編寫Controller類
package com.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.stereotype.Controller; import org.springframework.web.HttpRequestHandler; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.fasterxml.jackson.databind.ObjectMapper; import com.pojo.User; public class UserController3 implements HttpRequestHandler{ @Override public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.setCharacterEncoding("UTF-8"); ////載入spring.xml文件初始化ApplicationContext ApplicationContext ac = new ClassPathXmlApplicationContext("springmvc.xml"); ////獲取消息轉換器MappingJackson2HttpMessageConverter類對象,實現了HttpMessageConverter介面 HttpMessageConverter converter=(HttpMessageConverter) ac.getBean("myconverter"); ////獲取媒體類型,傳入的是"application/json;charset=UTF-8"這裡我們直接new對象 MediaType mediaType=new MediaType(MediaType.APPLICATION_JSON, null); //請求資訊類,封裝輸入資訊 HttpInputMessage in=new ServletServerHttpRequest(request); //響應資訊類,封裝輸出資訊 HttpOutputMessage out=new ServletServerHttpResponse(response); //把前台傳來傳來json數據轉化為User對象(read接受的參數:這裡可以把json數據轉化為User,得益於HttpInputMessage已經把請求的資訊已經封裝好了,包括請求頭和請求體) User user= (User) converter.read(User.class, in); //設置年齡 user.setAge(666); System.out.println(user); //把User對象轉化為json數據輸出 converter.write(user, mediaType, out); } }
程式碼都有註解。很簡單。消息轉換器對象的write()方法讀客戶端發送的json數據,把json數據轉換為User類對象,為什麼要轉換?(方便我們程式碼進行操作和維護。比如我們登陸時,要查資料庫看這個user存在不,設置其他屬性什麼的,比如age。所以是有一定需求的。java語言是面向對象程式設計語言,肯定操作java對象方便快捷)。
(7)跑一下程式
完美運行,沒有出現亂碼,好了,收工。列印出了username,password,age的內容。
6. 理解Spring MVC的執行流程
(1)用戶發送請求到Spring MVC核心控制器(DispatcherServlet)。
(2)前端控制器請求 HandlerMapping 查找 Controller,可以根據 xml 配置、註解進行查找。
(3) 處理器映射器 HandlerMapping 查找完成後向核心控制器返回 Controller。
(4)前端控制器調用處理器適配器去執行 Controller。
(5)處理器適配器執行 Controller。
(6)Controller執行完成後給適配器返回 ModelAndView對象。
(7)處理器適配器向前端控制器返回 ModelAndView。ModelAndView 是SpringMVC 框架的一個底層對象,包括 Model 和 View。
(8)前端控制器請求試圖解析器去進行視圖解析根據邏輯視圖名來解析真正的視圖。
(9)視圖解析器向前端控制器返回 view。
(10)核心控制器進行視圖渲染。就是將模型數據(在 ModelAndView 對象中)填充到 request 域
(11)核心控制器向用戶響應結果。
7. 總結
(1)導包。不到log4j日誌包會出現錯誤,Spring框架支援log4j日誌輸出。支援Apache服務的包也要導入(com.springresource)。Jackson包導全。Spring開發基本包。
(2)載入不到靜態資源,要對靜態資源文件進行放行。
(3)Spring的IoC機制,可以把對象交給容器管理。IoC核心:通過配置文件或者是Annonation的方式,將實現類注入到調用者那裡去。
IoC實現原理:工廠模式+java反射機制。IOC好處:
第一:資源集中管理,實現資源的可配置和易管理。
第二:降低了使用資源雙方的依賴程度,也就是我們說的耦合度。
第三:通過配置文件,可以做到改變實現類,而不改變任何一個調用者的程式碼(IOC)。
(4)本次開發我們使用到了Spring MVC內置的消息轉換器MappingJackson2HttpMessageConverter(用於java對象與json數據的轉換)。其內部實現原理還是使用到了Jackson開發包,這就是為什麼要導入Jackson包的原因。當然,我們也可以直接使用Jackson類庫進行java對象與json數據的轉換(我的前幾篇部落格)。這裡調用內置的消息轉換器,方便大家理解。Spring MVC的執行流程,以及底層到底是怎麼進行java對象與json數據的。當然也可以在Spring MVC自定義自己的消息轉換器類,讓其實現HttpMessageConverter<T>介面。
(5)本次開發的強大之處在於沒有使用任何Spring的任何註解,基於純XML配置,走原生模式。方便大家了解底層的實現原理。
(6)學習的時候可以多參考Spring和Spring MVC的開發文檔。有不懂的類可以參考Spring API文檔。
(7)通過本次開發,發現Spring MVC的優點:Spring MVC與Spring無縫對接,使用簡單,易擴展,靈活性比較強,可適配,非侵入,清晰的角色劃分,強大的配置方式,可訂製的handler mapping和view resolver。當然我覺得Spring MVC最好的地方在於提供了非常靈活的參數綁定機制和強大的註解機制。