Sping MVC不使用任何註解處理(jQuery)Ajax請求(基於XML配置)
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語言用什麼編寫的 ? 這個可以自行谷歌或百度,這裡不多說。當然,一個專案或者工作的執行流程我們也必須熟知。這次開發看起來很簡單,但是我們把涉及到的知識內容都會為大家詳細講解,並且都是本人一字一句經過推敲實踐進行編寫的。當然,本人也是菜鳥一枚。知識有限。但是我們可以經過學習來獲取更多的知識,提升自己的能力,從而達到人生的巔峰。不多說了(跑題了,哈哈!)。
(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:\eclipseWorkspace\myworkspace\MySpringMVCAjax\WebContent\WEB-INF\lib\spring-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最好的地方在於提供了非常靈活的引數繫結機制和強大的註解機制。
&n