SSM框架流程及原理分析
前言:學ssm框架已經有很長時間,今天來復習一下
SSM圖示流程:
Spring核心:Java反射
Mybatis:動態代理,而動態代理又是基於反射的,所以,ssm框架核心原理在反射。
(1)Spring(對象工廠): 平時開發接觸最多的估計就是這個IOC容器,它可以裝載bean(也就是Java中的類,當然也包括service、dao裡面的),有瞭這個機制,就不用在每次使用這個類的時候為它初始化,很少看到關鍵字new。
(2)SpringMVC(視圖控制器): 核心為一個DispatcherServlet,控制所有請求
這裡奉上手寫SpringMVC的核心DispatcherServlet源碼
package servlet; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import annotation.HController; import annotation.HRequestMapping; /** * 手寫SpringMVC * * @author hxz * @description TODO * @data 2020年1月2日 上午9:39:57 */ public class MyDispatcherServlet extends HttpServlet { //加載屬性文件 private Properties properties = new Properties(); //裝載beanname private List<String> classNames = new ArrayList<String>(); //ioc容器 private Map<String, Object> ioc = new HashMap<String, Object>(); //類似於以前自定義的cache緩存容器,這裡也是起到一個容器的作用 //用於加載各個mapping private Map<String, Method> handlerMapping = new HashMap<String, Method>(); //容器加載所有的controller private Map<String, Object> controllerMap = new HashMap<String, Object>(); @Override public void init(ServletConfig config) throws ServletException { // 1.加載配置文件 doLoadConfig(config.getInitParameter("contextConfigLocation")); // 2.初始化所有相關聯的類,掃描用戶設定的包下面所有的類 doScanner(properties.getProperty("scanPackage")); // 3.拿到掃描到的類,通過反射機制,實例化,並且放到ioc容器中beanName默認是首字母小寫 doInstance(); // 4.初始化HandlerMapping(將url和method對應上) initHandlerMapping(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { // 處理請求 doDispatch(req, resp); } catch (Exception e) { resp.getWriter().write("500!! Server Exception"); } } private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception { if (handlerMapping.isEmpty()) { return; } String url = req.getRequestURI(); String contextPath = req.getContextPath(); url = url.replace(contextPath, "").replaceAll("/+", "/"); if (!this.handlerMapping.containsKey(url)) { resp.getWriter().write("404 NOT FOUND!"); return; } Method method = this.handlerMapping.get(url); // 獲取方法的參數列表 Class<?>[] parameterTypes = method.getParameterTypes(); // 獲取請求的參數 Map<String, String[]> parameterMap = req.getParameterMap(); // 保存參數值 Object[] paramValues = new Object[parameterTypes.length]; // 方法的參數列表 for (int i = 0; i < parameterTypes.length; i++) { // 根據參數名稱,做某些處理 String requestParam = parameterTypes[i].getSimpleName(); if (requestParam.equals("HttpServletRequest")) { // 參數類型已明確,這邊強轉類型 paramValues[i] = req; continue; } if (requestParam.equals("HttpServletResponse")) { paramValues[i] = resp; continue; } if (requestParam.equals("String")) { for (Entry<String, String[]> param : parameterMap.entrySet()) { String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ","); paramValues[i] = value; } } } // 利用反射機制來調用 try { method.invoke(this.controllerMap.get(url), paramValues);// 第一個參數是method所對應的實例 // 在ioc容器中 } catch (Exception e) { e.printStackTrace(); } } private void doLoadConfig(String location) { // 把web.xml中的contextConfigLocation對應value值的文件加載到流裡面 InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(location); try { // 用Properties文件加載文件裡的內容 properties.load(resourceAsStream); } catch (IOException e) { e.printStackTrace(); } finally { // 關流 if (null != resourceAsStream) { try { resourceAsStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } private void doScanner(String packageName) { // 把所有的.替換成/ URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/")); File dir = new File(url.getFile()); for (File file : dir.listFiles()) { if (file.isDirectory()) { // 遞歸讀取包 doScanner(packageName + "." + file.getName()); } else { String className = packageName + "." + file.getName().replace(".class", ""); classNames.add(className); } } } //利用java的反射機制 private void doInstance() { if (classNames.isEmpty()) { return; } for (String className : classNames) { try { // 把類搞出來,反射來實例化 Class<?> clazz = Class.forName(className); if (clazz.isAnnotationPresent(HController.class)) { ioc.put(toLowerFirstWord(clazz.getSimpleName()), clazz.newInstance()); } else { continue; } } catch (Exception e) { e.printStackTrace(); continue; } } } private void initHandlerMapping() { if (ioc.isEmpty()) { return; } try { for (Entry<String, Object> entry : ioc.entrySet()) { Class<? extends Object> clazz = entry.getValue().getClass(); if (!clazz.isAnnotationPresent(HController.class)) { continue; } // 拼url時,是controller頭的url拼上方法上的url String baseUrl = ""; if (clazz.isAnnotationPresent(HRequestMapping.class)) { HRequestMapping annotation = clazz.getAnnotation(HRequestMapping.class); baseUrl = annotation.value(); } Method[] methods = clazz.getMethods(); for (Method method : methods) { if (!method.isAnnotationPresent(HRequestMapping.class)) { continue; } HRequestMapping annotation = method.getAnnotation(HRequestMapping.class); String url = annotation.value(); url = (baseUrl + "/" + url).replaceAll("/+", "/"); handlerMapping.put(url, method); controllerMap.put(url, clazz.newInstance()); System.out.println(url + "," + method); } } } catch (Exception e) { e.printStackTrace(); } } /** * 把字符串的首字母小寫 * * @param name * @return */ private String toLowerFirstWord(String name) { char[] charArray = name.toCharArray(); charArray[0] += 32; return String.valueOf(charArray); } }
從源碼及可看出流程:
1)客戶端發送請求到DispacherServlet(由web.xml攔截所有請求到改servlet);
2)由DispacherServlet(核心servlet)控制器查詢HanderMapping,找到處理請求的Controller;(這裡我用瞭一個map類似於緩存容器,裝載所有的mapping即映射)
3)Controller層調用業務邏輯處理後,返回ModelAndView,即下一層(往往是service、serviceimpl,mapper層)返回的的數據;
4)DispacherSerclet查詢視圖解析器,找到ModelAndView指定的視圖;
5)視圖負責將結果顯示到客戶端。
(3)Mybatis(持久層框架):mybatis是對jdbc的封裝,它讓數據庫底層操作變的透明。mybatis的操作都是圍繞一個sqlSessionFactory實例展開的。mybatis通過配置文件關聯到各實體類的Mapper文件,Mapper文件中配置瞭每個類對數據庫所需進行的sql語句映射。在每次與數據庫交互時,通過sqlSessionFactory拿到一個sqlSession,再通過得到Mapper文件sqlSession.getMapper()最後調用的是JDK的動態代理(exlipse一直Ctrl+鼠標左鍵點擊到最後發現如此,源碼附上)。
protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
發人員隻需要創建Mapper接口,並使用Mapper接口即可。Mybatis會對Mapper接口產生動態代理對象,這個動態代理對象實現瞭Mapper接口,擁有Mapper中定義的所有方法,並對這些方法進行瞭增強。增強的邏輯是獲得sql語句和執行sql語句。
動態代理:
- 通過實現 InvocationHandler 接口創建自己的調用處理器;
- 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來創建動態代理類;
- 通過反射機制獲得動態代理類的構造函數,其唯一參數類型是調用處理器接口類型;
- 通過構造函數創建動態代理類實例,構造時調用處理器對象作為參數被傳入。
框架核心原理
- AOP 面向切面編程(AOP)提供另外一種角度來思考程序結構,通過這種方式彌補瞭面向對象編程(OOP)的不足。除瞭類(classes)以外,AOP提供瞭切面。切面對關註點進行模塊化,例如橫切多個類型和對象的事務管理。Spring的一個關鍵的組件就是AOP框架,可以自由選擇是否使用AOP。提供聲明式企業服務,特別是為瞭替代EJB聲明式服務。最重要的服務是聲明性事務管理,這個服務建立在Spring的抽象事物管理之上。允許用戶實現自定義切面,用AOP來完善OOP的使用可以把Spring AOP看作是對Spring的一種增強。AOP的實現乃至spring框架基本上核心代碼都是基於Java語言的反射機制(所謂的反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。)。
- AOP主要作用就是不通過修改源代碼的方式、將非核心功能代碼織入來實現對方法的增強。那麼Spring AOP的底層如何實現對方法的增強?實現的關鍵在於使用瞭代理模式。代理模式的作用就是為其它對象提供一種代理,以控制對這個對象的訪問,用於解決在直接訪問對象時帶來的各種問題,比如要訪問的對象在遠程的機器上。在面向對象系統中,由於其他某些原因(對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問)等
- 代理主要分為兩種方式:靜態代理和動態代理
- IOC IoC不是一種技術,隻是一種思想,一個重要的面向對象編程的法則,它能指導我們如何設計出松耦合、更優良的程序。有瞭IoC容器後,把創建和查找依賴對象的控制權交給瞭容器,由容器進行註入組合對象,所以相較於傳統的java servlet需要自己request.getParamiter等需要一系列取值,轉換中文,轉換值類型的繁瑣,更重要的是使得程序的整個體系結構變得非常靈活。
- 自定義一個IOC容器的思路:
- Map做一個容器,然後用解析xml文件的工具解析出需要掃描的包。利用Java反射機制拿到鎖具有的方法,屬性,註入到Map容器中
- DI 依賴註入,是組件之間依賴關系由容器在運行期決定,形象的說,即由容器動態地將某個依賴關系註入到組件之中。依賴註入的目的並非為軟件系統帶來更多功能,而是為瞭提升組件重用的頻率,並為系統搭建一個靈活、可擴展的平臺。通過依賴註入機制,我們隻需要通過簡單的配置,而無需任何代碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。
以上就是深入淺出SSM框架流程以及原理的詳細內容,更多關於SSM框架原理的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 使用springmvc運行流程分析,手寫spring框架嘗試
- 手把手寫Spring框架
- Java工具類之@RequestMapping註解
- Java基礎篇之反射機制詳解
- 基於Spring BeanUtils的copyProperties方法使用及註意事項