手寫Spring框架準備工做之SpringMVC框架理解

JavaEE體系結構包括四層,從上到下分別是應用層、Web層、業務層、持久層。Struts和SpringMVC是Web層的框架,Spring是業務層的框架,Hibernate和MyBatis是持久層的框架。html

爲何要使用SpringMVC?

不少應用程序的問題在於處理業務數據的對象和顯示業務數據的視圖之間存在緊密耦合,一般,更新業務對象的命令都是從視圖自己發起的,使視圖對任何業務對象更改都有高度敏感性。並且,當多個視圖依賴於同一個業務對象時是沒有靈活性的。前端

SpringMVC是一種基於Java,實現了Web MVC設計模式,請求驅動類型的輕量級Web框架,即便用了MVC架構模式的思想,將Web層進行職責解耦。基於請求驅動指的就是使用請求-響應模型,框架的目的就是幫助咱們簡化開發,SpringMVC也是要簡化咱們平常Web開發。web

MVC設計模式

MVC設計模式的任務是將包含業務數據的模塊與顯示模塊的視圖解耦。這是怎樣發生的?在模型和視圖之間引入重定向層能夠解決問題。此重定向層是控制器,控制器將接收請求,執行更新模型的操做,而後通知視圖關於模型更改的消息。 spring

file
file

SpringMVC架構 SpringMVC是Spring的一部分,如圖: 後端

file

SpringMVC的核心架構:設計模式

file

具體流程:

(1)首先瀏覽器發送請求——>DispatcherServlet,前端控制器收到請求後本身不進行處理,而是委託給其餘的解析器進行處理,做爲統一訪問點,進行全局的流程控制;瀏覽器

(2)DispatcherServlet——>HandlerMapping,處理器映射器將會把請求映射爲HandlerExecutionChain對象(包含一個Handler處理器對象、多個HandlerInterceptor攔截器)對象;bash

(3)DispatcherServlet——>HandlerAdapter,處理器適配器將會把處理器包裝爲適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持不少類型的處理器;數據結構

(4)HandlerAdapter——>調用處理器相應功能處理方法,並返回一個ModelAndView對象(包含模型數據、邏輯視圖名);架構

(5)ModelAndView對象(Model部分是業務對象返回的模型數據,View部分爲邏輯視圖名)——> ViewResolver, 視圖解析器將把邏輯視圖名解析爲具體的View;

(6)View——>渲染,View會根據傳進來的Model模型數據進行渲染,此處的Model實際是一個Map數據結構;

(7)返回控制權給DispatcherServlet,由DispatcherServlet返回響應給用戶,到此一個流程結束。

SpringMVC入門程序

(1)web.xml

<web-app>
  <servlet>
  <!-- 加載前端控制器 -->
  <servlet-name>springmvc</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <!-- 
  	   加載配置文件
  	   默認加載規範:
  	   * 文件命名:servlet-name-servlet.xml====springmvc-servlet.xml
  	   * 路徑規範:必須在WEB-INF目錄下面
  	   修改加載路徑:
   -->
   <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>*.do</url-pattern>
  </servlet-mapping>
</web-app>
複製代碼

(2)springmvc.xml

<beans>
	<!-- 配置映射處理器:根據bean(自定義Controller)的name屬性的url去尋找handler;springmvc默認的映射處理器是
	BeanNameUrlHandlerMapping
	 -->
	<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
	
	
	<!-- 配置處理器適配器來執行Controlelr ,springmvc默認的是
	SimpleControllerHandlerAdapter
	-->
	<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
	
	<!-- 配置自定義Controller -->
	<bean id="myController" name="/hello.do" class="org.controller.MyController"></bean>
	
	<!-- 配置sprigmvc視圖解析器:解析邏輯試圖; 
		後臺返回邏輯試圖:index
		視圖解析器解析出真正物理視圖:前綴+邏輯試圖+後綴====/WEB-INF/jsps/index.jsp
	-->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/jsps/"></property>
		<property name="suffix" value=".jsp"></property>		
	</bean>
</beans>
複製代碼

(3)自定義處理器

public class MyController implements Controller{

	public ModelAndView handleRequest(HttpServletRequest arg0,
			HttpServletResponse arg1) throws Exception {
		ModelAndView mv = new ModelAndView();
		//設置頁面回顯數據
		mv.addObject("hello", "歡迎學習springmvc!");
		
		//返回物理視圖
		//mv.setViewName("/WEB-INF/jsps/index.jsp");
		
		//返回邏輯視圖
		mv.setViewName("index");
		return mv;
	}
}
複製代碼

(4)index頁面

<html>
<body>
<h1>${hello}</h1>
</body>
</html>
複製代碼

(5)測試地址

http://localhost:8080/springmvc/hello.do
複製代碼

HandlerMapping

處理器映射器將會把請求映射爲 HandlerExecutionChain 對象(包含一個 Handler 處理器對象、多個 HandlerInterceptor 攔截器)對象,經過這種策略模式,很容易添加新的映射策略。

處理器映射器有三種,三種能夠共存,相互不影響,分別是BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping和ControllerClassNameHandlerMapping;

BeanNameUrlHandlerMapping

默認映射器,即便不配置,默認就使用這個來映射請求。

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
//映射器把hello.do請求映射到該處理器
<bean id="testController" name="/hello.do" class="org.controller.TestController"></bean>
複製代碼

SimpleUrlHandlerMapping

該處理器映射器能夠配置多個映射對應一個處理器.

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
	<property name="mappings">
		<props>
			<prop key="/ss.do">testController</prop>
			<prop key="/abc.do">testController</prop>
		</props>
	</property>
</bean>
//上面的這個映射配置表示多個*.do文件能夠訪問同一個Controller。	
<bean id="testController" name="/hello.do" class="org.controller.TestController"></bean>
複製代碼

ControllerClassNameHandlerMapping

該處理器映射器能夠不用手動配置映射, 經過[類名.do]來訪問對應的處理器.

//這個Mapping一配置, 咱們就可使用Controller的 [類名.do]來訪問這個Controller.

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"></bean>
複製代碼

HandlerAdapter

處理器適配器有兩種,能夠共存,分別是SimpleControllerHandlerAdapter和HttpRequestHandlerAdapter。

SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter是默認的適配器,全部實現了org.springframework.web.servlet.mvc.Controller 接口的處理器都是經過此適配器適配, 執行的。

HttpRequestHandlerAdapter 該適配器將http請求封裝成HttpServletResquest 和HttpServletResponse對象. 全部實現了 org.springframework.web.HttpRequestHandler 接口的處理器都是經過此適配器適配, 執行的. 實例以下所示:

(1)配置HttpRequestHandlerAdapter適配器

<!-- 配置HttpRequestHandlerAdapter適配器 -->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"></bean>
複製代碼

(2)編寫處理器

public class HttpController implements HttpRequestHandler{

	public void handleRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//給Request設置值,在頁面進行回顯
		request.setAttribute("hello", "這是HttpRequestHandler!");
		//跳轉頁面
		request.getRequestDispatcher("/WEB-INF/jsps/index.jsp").forward(request, response);
	}
}
複製代碼

(3)index頁面

<html>
<body>
<h1>${hello}</h1>
</body>
</html>
複製代碼

Adapter源碼分析

模擬場景:前端控制器(DispatcherServlet)接收到Handler對象後,傳遞給對應的處理器適配器(HandlerAdapter),處理器適配器調用相應的Handler方法。

(1)模擬處理器

//如下是Controller接口和它的是三種實現 
public interface Controller {
}

public class SimpleController implements Controller{
	public void doSimpleHandler() {
		System.out.println("Simple...");
	}
}

public class HttpController implements Controller{
	public void doHttpHandler() {
		System.out.println("Http...");
	}
}

public class AnnotationController implements Controller{
	public void doAnnotationHandler() {
		System.out.println("Annotation..");
	}
} 
複製代碼

(2)模擬處理器適配器

//如下是HandlerAdapter接口和它的三種實現
public interface HandlerAdapter {
	public boolean supports(Object handler);
	public void handle(Object handler);
}

public class SimpleHandlerAdapter implements HandlerAdapter{
	public boolean supports(Object handler) {
		return (handler instanceof SimpleController);
	}

	public void handle(Object handler) {
		((SimpleController)handler).doSimpleHandler();
	}
}

public class HttpHandlerAdapter implements HandlerAdapter{
	public boolean supports(Object handler) {
		return (handler instanceof HttpController);
	}

	public void handle(Object handler) {
		((HttpController)handler).doHttpHandler();
	}
}

public class AnnotationHandlerAdapter implements HandlerAdapter{
	public boolean supports(Object handler) {
		return (handler instanceof AnnotationController);
	}

	public void handle(Object handler) {
		((AnnotationController)handler).doAnnotationHandler();
	}
}
複製代碼

(3)模擬DispatcherServlet

public class Dispatcher {
	public static List<HandlerAdapter> handlerAdapter = new ArrayList<HandlerAdapter>();
	
	public Dispatcher(){
		handlerAdapter.add(new SimpleHandlerAdapter());
		handlerAdapter.add(new HttpHandlerAdapter());
		handlerAdapter.add(new AnnotationHandlerAdapter());
	}
	
	//核心功能
	public void doDispatch() {
		//前端控制器(DispatcherServlet)接收到Handler對象後
		//SimpleController handler = new SimpleController();
		//HttpController handler = new HttpController();
		AnnotationController handler = new AnnotationController();
		
		//傳遞給對應的處理器適配器(HandlerAdapter)
		HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
		
		//處理器適配器調用相應的Handler方法
		handlerAdapter.handle(handler);
	}
	
	//經過Handler找到對應的處理器適配器(HandlerAdapter)
	public HandlerAdapter getHandlerAdapter(Controller handler) {
		for(HandlerAdapter adapter : handlerAdapter){
			if(adapter.supports(handler)){
				return adapter;
			}
		}
		return null;
	}
}
複製代碼

(4)測試

public class Test {
	public static void main(String[] args) {
		Dispatcher dispather = new Dispatcher();
		dispather.doDispatch();
	}
}
複製代碼

Handler

這裏只介紹上文提到的兩種處理器, 除此以外還有不少適用於各類應用場景的處理器, 尤爲是Controller接口還有不少實現類, 你們能夠自行去了解.

Controller

org.springframework.web.servlet.mvc.Controller, 該處理器對應的適配器是 SimpleControllerHandlerAdapter.

public interface Controller {

	/**
	 * Process the request and return a ModelAndView object which the DispatcherServlet
	 * will render. A {@code null} return value is not an error: it indicates that
	 * this object completed request processing itself and that there is therefore no
	 * ModelAndView to render.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @return a ModelAndView to render, or {@code null} if handled directly
	 * @throws Exception in case of errors
	 */
	ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;

}
複製代碼

該處理器方法用於處理用戶提交的請求, 經過調用service層代碼, 實現對用戶請求的計算響應, 並最終將計算所得數據及要響應的頁面封裝爲一個ModelAndView 對象, 返回給前端控制器(DispatcherServlet).

Controller接口的實現類:

file

HttpRequestHandler

org.springframework.web.HttpRequestHandler, 該處理器對應的適配器是 HttpRequestHandlerAdapter.

public interface HttpRequestHandler {
    void handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws ServletException, IOException;
}
複製代碼

該處理器方法沒有返回值, 不能像 ModelAndView 同樣, 將數據及目標視圖封裝爲一個對象, 但能夠將數據放入Request, Session等域屬性中, 並由Request 或 Response完成目標頁面的跳轉.

ViewResolver 視圖解析器負責將處理結果生成View視圖. 這裏介紹兩種經常使用的視圖解析器:

InternalResourceViewResolver

該視圖解析器用於完成對當前Web應用內部資源的封裝和跳轉. 而對於內部資源的查找規則是, 將ModelAndView中指定的視圖名稱與視圖解析器配置的前綴與後綴想結合, 拼接成一個Web應用內部資源路徑. 內部資源路徑 = 前綴 + 視圖名稱 + 後綴.

InternalResourceViewResolver解析器會把處理器方法返回的模型屬性都存放到對應的request中, 而後將請求轉發到目標URL.

(1) 處理器

public class MyController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mv = new ModelAndView();
        mv.addObject("hello", "hello world!");
        mv.setViewName("index");
        return mv;
    }
}
複製代碼

(2) 視圖解析器配置

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
   </bean>
複製代碼

固然, 若不指定前綴與後綴, 直接將內部資源路徑寫到 setViewName()中也能夠, 至關於前綴與後綴均爲空串.

mv.setViewName("/WEB-INF/jsp/index.jsp");
複製代碼

BeanNameViewResolver

InternalResourceViewResolver視圖解析器存在一個問題, 就是隻能夠完成將內部資源封裝後的跳轉, 沒法跳轉向外部資源, 如外部網頁.

BeanNameViewResolver 視圖解析器將資源(內部資源和外部資源)封裝爲bean實例, 而後在 ModelAndView 中經過設置bean實例的id值來指定資源. 在配置文件中能夠同時配置多個資源bean.

(1) 處理器

public class MyController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {

        return new ModelAndView("myInternalView");
//        return new ModelAndView("baidu");
    }
}
複製代碼

(2) 視圖解析器配置

<!--視圖解析器-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
<!--內部資源view-->
<bean id="myInternalView" class="org.springframework.web.servlet.view.JstlView">
    <property name="url" value="/jsp/show.jsp"/>
</bean>
<!--外部資源view-->
<bean id="baidu" class="org.springframework.web.servlet.view.RedirectView">
    <property name="url" value="https://www.baidu.com/"/>
</bean>
複製代碼

瞭解完這兩種視圖解析器後是否是有種熟悉感, 是的, 就是請求轉發和重定向.

中文亂碼解決

Get請求亂碼

Tomcat8已經解決了Get請求亂碼, 若是是Tomcat8如下的版本, 可使用如下兩種方法:

更改Tomcat的配置文件server.xml

file

  • 對參數進行從新編碼
String userName =new
String(request.getParamter(「userName」).getBytes(「ISO-8859-1」),「UTF-8」); //ISO-8859-1是Tomcat8如下版本的默認編碼
複製代碼

Post請求亂碼

在web.xml中加入:

<filter>
    <filter-name>characterEncoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
複製代碼

來源: blog.csdn.net/litianxiang…

相關文章

開源項目推薦

做者的開源項目推薦:

  • Pre 基於 Spring Boot 、Spring Security 、Vue 的先後端分離的的 RBAC 權限管理系統
  • Prex 基於 Spring Cloud、Oauth2 、Vue 的先後端分離的微服務架構 RBAC 權限管理系統

關注公衆號回覆開源項目便可獲取

相關文章
相關標籤/搜索