SpringMVC原理解析

SpringMVC

Spring MVC的工做原理

①客戶端的全部請求都交給前端控制器DispatcherServlet來處理,它會負責調用系統的其餘模塊來真正處理用戶的請求。前端

② DispatcherServlet收到請求後,將根據請求的信息(包括URL、HTTP協議方法、請求頭、請求參數、Cookie等)以及HandlerMapping的配置找處處理該請求的Handler(任何一個對象均可以做爲請求的Handler)。java

③在這個地方Spring會經過HandlerAdapter對該處理器進行封裝。web

④ HandlerAdapter是一個適配器,它用統一的接口對各類Handler中的方法進行調用。spring

⑤ Handler完成對用戶請求的處理後,會返回一個ModelAndView對象給DispatcherServlet,ModelAndView顧名思義,包含了數據模型以及相應的視圖的信息。數據庫

⑥ ModelAndView的視圖是邏輯視圖,DispatcherServlet還要藉助ViewResolver完成從邏輯視圖到真實視圖對象的解析工做。 ⑦ 當獲得真正的視圖對象後,DispatcherServlet會利用視圖對象對模型數據進行渲染。瀏覽器

⑧ 客戶端獲得響應,多是一個普通的HTML頁面,也能夠是XML或JSON字符串,還能夠是一張圖片或者一個PDF文件。安全

SpringMVC的運行機制

一、用戶發送請求時會先從DispathcherServler的doService方法開始,在該方法中會將ApplicationContext、localeResolver、themeResolver等對象添加到request中,緊接着就是調用doDispatch方法。服務器

二、進入該方法後首先會檢查該請求是不是文件上傳的請求(校驗的規則是是不是post而且contenttType是否爲multipart/爲前綴)即調用的是checkMultipart方法;若是是的將request包裝成MultipartHttpServletRequest。mvc

三、而後調用getHandler方法來匹配每一個HandlerMapping對象,若是匹配成功會返回這個Handle的處理鏈HandlerExecutionChain對象,在獲取該對象的內部其實也獲取咱們自定定義的攔截器,並執行了其中的方法。app

四、執行攔截器的preHandle方法,若是返回false執行afterCompletion方法並理解返回

五、經過上述獲取到了HandlerExecutionChain對象,經過該對象的getHandler()方法得到一個object經過HandlerAdapter進行封裝獲得HandlerAdapter對象。

六、該對象調用handle方法來執行Controller中的方法,該對象若是返回一個ModelAndView給DispatcherServlet。

七、DispatcherServlet藉助ViewResolver完成邏輯試圖名到真實視圖對象的解析,獲得View後DispatcherServlet使用這個View對ModelAndView中的模型數據進行視圖渲染。

什麼是Servlet

Java Servlet 是運行在 Web 服務器或應用服務器上的程序,它是做爲來自 Web 瀏覽器或其餘 HTTP 客戶端的請求和 HTTP 服務器上的數據庫或應用程序之間的中間層。

使用 Servlet,您能夠收集來自網頁表單的用戶輸入,呈現來自數據庫或者其餘源的記錄,還能夠動態建立網頁。

Java Servlet 一般狀況下與使用 CGI(Common Gateway Interface,公共網關接口)實現的程序能夠達到殊途同歸的效果。可是相比於 CGI,Servlet 有如下幾點優點:

性能明顯更好。

Servlet 在 Web 服務器的地址空間內執行。這樣它就沒有必要再建立一個單獨的進程來處理每一個客戶端請求。

Servlet 是獨立於平臺的,由於它們是用 Java 編寫的。

服務器上的 Java 安全管理器執行了一系列限制,以保護服務器計算機上的資源。所以,Servlet 是可信的。

Java 類庫的所有功能對 Servlet 來講都是可用的。它能夠經過 sockets 和 RMI 機制與 applets、數據庫或其餘軟件進行交互。

Servlet生命週期

SpringMVC是基於servlet,控制器基於方法級別的攔截,處理器設計爲單實例,因此應該瞭解一下Servlet的生命週期。

Servlet 加載—>實例化—>服務—>銷燬。

init():

在Servlet的生命週期中,僅執行一次init()方法。它是在服務器裝入Servlet時執行的,負責初始化Servlet對象。能夠配置服務器,以在啓動服務器或客戶機首次訪問Servlet時裝入Servlet。不管有多少客戶機訪問Servlet,都不會重複執行init()。

service():

它是Servlet的核心,負責響應客戶的請求。每當一個客戶請求一個HttpServlet對象,該對象的Service()方法就要調用,並且傳遞給這個方法一個「請求」(ServletRequest)對象和一個「響應」(ServletResponse)對象做爲參數。在HttpServlet中已存在Service()方法。默認的服務功能是調用與HTTP請求的方法相應的do功能。

destroy():

僅執行一次,在服務器端中止且卸載Servlet時執行該方法。當Servlet對象退出生命週期時,負責釋放佔用的資源。一個Servlet在運行service()方法時可能會產生其餘的線程,所以須要確認在調用destroy()方法時,這些線程已經終止或完成。

手寫SpringMVC思路

1.web.xml加載

爲了讀取web.xml中的配置,咱們用到ServletConfig這個類,它表明當前Servlet在web.xml中的配置信息。經過web.xml中加載咱們本身寫的MyDispatcherServlet和讀取配置文件。

二、初始化階段

在前面咱們提到DispatcherServlet的initStrategies方法會初始化9大組件,可是這裏將實現一些SpringMVC的最基本的組件而不是所有,按順序包括:

  • 加載配置文件
  • 掃描用戶配置包下面全部的類
  • 拿到掃描到的類,經過反射機制,實例化。而且放到ioc容器中(Map的鍵值對 beanName-bean) beanName默認是首字母小寫
  • 初始化HandlerMapping,這裏其實就是把url和method對應起來放在一個k-v的Map中,在運行階段取出

三、運行階段

每一次請求將會調用doGet或doPost方法,因此統一運行階段都放在doDispatch方法裏處理,它會根據url請求去HandlerMapping中匹配到對應的Method,而後利用反射機制調用Controller中的url對應的方法,並獲得結果返回。按順序包括如下功能:

  • 異常的攔截
  • 獲取請求傳入的參數並處理參數
  • 經過初始化好的handlerMapping中拿出url對應的方法名,反射調用

手寫SpringMVC基本實現

/** * * 1.自定義DispatcherServlet<br> * 2.servlet init()方法初始化###只會執行一次<br> * ######2.1獲取當前包下全部的類<br> * ######2.2初始化當前包下全部的類,使用Java反射機制初始化對象存放在SpringMVC容器中key(beanId)- * value( 當前實例對象) <br> * ######2.3初始化HandlerMapping方法,將url和方法對應上 <br> * ########2.3.1使用Java反射技術讀取類的信息,存放在map集合中key爲url請求地址,value爲對應方法 * <br> * ########2.3.2使用Java反射技術讀取類的信息,存放在map集合中key爲url請求地址,value爲對應實例對象 * <br> * 3.servlet get或者post請求<br> * ######## 3.1.1獲取請求地址,使用Java反射技術找到對應的方法和實例對象進行執行 <br> */
public class ExtDispatcherServlet extends HttpServlet {
	// mvc bean key=beanid ,value=對象
	private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<String, Object>();
	// mvc 請求方法 key=requestUrl,value=對象
	private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<String, Object>();
	// mvc 請求方法 key=requestUrl,value=方法
	private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<String, String>();

	/** * 初始化自定義SpringMVC容器 */
	public void init() throws ServletException {
		try {
			// 1.獲取當前包下全部的類
			List<Class<?>> classes = ClassUtil.getClasses("com.itmayiedu.ext.controller");
			// 2.初始化當前包下全部的類,使用Java反射機制初始化對象存放在SpringMVC容器中key(beanId)-value(
			// 當前實例對象)
			findClassMVCBeans(classes);
			// 3.初始化HandlerMapping方法,將url和方法對應上
			handlerMapping(mvcBeans);

		} catch (Exception e) {

		}
	}

	// 2.初始化當前包下全部的類,使用Java反射機制初始化對象存放在SpringMVC容器中key(beanId)-value(
	// 當前實例對象)
	public void findClassMVCBeans(List<Class<?>> classes) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		mvcBeans = new ConcurrentHashMap<String, Object>();
		for (Class<?> classInfo : classes) {
			ExtController extController = classInfo.getDeclaredAnnotation(ExtController.class);
			if (extController != null) {
				// 默認類名小寫 做爲bean的名稱
				String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
				mvcBeans.put(beanId, ClassUtil.newInstance(classInfo));
			}
		}

	}

	// 3.初始化HandlerMapping方法,將url和方法對應上
	public void handlerMapping(ConcurrentHashMap<String, Object> mvcBeans) {
		// 遍歷mvc bean對象
		for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
			// springmvc 注入object對象
			Object mvcObject = entry.getValue();
			// 判斷類上是否有@ExtRequestMapping註解
			Class<? extends Object> classInfo = mvcObject.getClass();
			String requestBaseUrl = null;
			ExtRequestMapping classExtRequestMapping = classInfo.getAnnotation(ExtRequestMapping.class);
			if (classExtRequestMapping != null) {
				requestBaseUrl = classExtRequestMapping.value();
			}
			// 遍歷當前類的全部方法,判斷方法上是否有註解
			Method[] declaredMethods = classInfo.getDeclaredMethods();
			for (Method method : declaredMethods) {
				ExtRequestMapping methodExtRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class);
				if (methodExtRequestMapping != null) {
					String httpRequestUrl = methodExtRequestMapping.value();
					mvcBeanUrl.put(requestBaseUrl + httpRequestUrl, mvcObject);
					mvcMethodUrl.put(requestBaseUrl + httpRequestUrl, method.getName());
				}
			}
		}
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		try {
			doDispatch(req, resp);
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	public void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		// 1.獲取請求url地址
		String requestUrl = req.getRequestURI();
		// 2.使用請求url查找對應mvc 控制器bean
		Object object = mvcBeanUrl.get(requestUrl);
		if (object == null) {
			resp.getWriter().println("http ext not found controller 404");
			return;
		}
		// 3.獲取對應的請求方法
		String methodName = mvcMethodUrl.get(requestUrl);
		if (StringUtils.isEmpty(methodName)) {
			resp.getWriter().println("http ext not found Method 404");
			return;
		}
		// 4.使用java反射技術執行方法
		Class<? extends Object> classInfo = object.getClass();
		String resultPage = (String) methodInvoke(classInfo, object, methodName);
		// 5.視圖展現
		viewdisplay(resultPage, req, resp);
	}

	// 執行方法
	public Object methodInvoke(Class<? extends Object> classInfo, Object object, String methodName) {
		try {
			Method method = classInfo.getMethod(methodName);
			Object result = method.invoke(object);
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	// 視圖展現
	public void viewdisplay(String pageName, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
		// 獲取後綴信息
		String suffix = ".jsp";
		// 頁面目錄地址
		String prefix = "/";
		req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);
	}

}

複製代碼

OnRefresh 是FrameworkServlet類中的提供的模塊方法,在其之類DispatchServlet中進行了重寫,

主要用於刷新Spring在web功能實現中所必須使用的全局變量。

相關文章
相關標籤/搜索