代碼實現SpringMvc

来源:https://www.cnblogs.com/chewq/archive/2018/12/23/10165497.html
-Advertisement-
Play Games

偶然看到一篇100多行實現SpringMvc的博客,閱讀後整理加實現出來。大家共勉!(紙上得來終覺淺,絕知此事要躬行。) 實現Spring的部分。 代碼解析如下: 1、首先創建Servelt,繼承HttpServlet。覆蓋init/doGet/doPost方法。 2、配置web.xml 2.1、配 ...


  偶然看到一篇100多行實現SpringMvc的博客,閱讀後整理加實現出來。大家共勉!(紙上得來終覺淺,絕知此事要躬行。)

  實現Spring的部分。

  1. Bean工廠,統一創建Bean;
  2. IOC,實現Bean的依賴註入;
  3. DispatchServlet,SpringMVC的路徑映射。

  代碼解析如下:

  1、首先創建Servelt,繼承HttpServlet。覆蓋init/doGet/doPost方法。

    @Override
    public void init() throws ServletException {
		 try {
			// 初始化配置文件
			 initConfig(super.getServletConfig().getInitParameter(CONFIG_NAME));
			 // 掃描類
			 doScanPackage(configProperties.getProperty("packageScan"));
			 // 載入類
			 initBeanInitializing();
			 // 依賴註入
			 initBeanAutowired();
			 // 路徑映射
			 initServletDispatch();
		} catch (Exception e) {
			e.printStackTrace();
		}
		 System.out.println("====>beanFactory:\n"+beanFactory);
		 System.out.println("====>requestMap:\n"+requestMap);
    }
	 


	@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{
		doDispatch(req,resp);
	}

  2、配置web.xml

    <servlet>
  	<servlet-name>cwDispatchServlet</servlet-name>
  	<servlet-class>com.cw.servlets.CWDispatchServlet</servlet-class>
  	<init-param>
  		<param-name>dispatchConfig</param-name>
  		<param-value>config.properties</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>cwDispatchServlet</servlet-name>
  	<url-pattern>*.htm</url-pattern>
  </servlet-mapping>

  2.1、配置文件config.properties在根目錄,如下

  

# 設置配置文件
packageScan=com.cw.demo

  

  3、定義支持的註入,如

  Autowired

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Inherited
public @interface CWAutowired {
	String value() default "";
}

  Controller

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface CWController {
	String value() default "";
}

  RequestMapping

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Inherited
public @interface CWRequestMapping {
	String value() default "";
}

  Service

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface CWService {
	String value() default "";
}

  4、在Servlet的init方法中,依次實現

  4.1、載入配置文件

private void initConfig(String configName){
		InputStream inputStream = null;
		try {
			System.out.println("------->configName:"+configName);
			inputStream = this.getClass().getClassLoader().getResourceAsStream(configName);
			configProperties.load(inputStream);
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(null != inputStream) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

  4.1、掃描所有Java類

  

        /**
	 * 掃描類
	 * @Title: doScanPackage 
	 * @param packagePath
	 * @return void
	 */
	private void doScanPackage(String packagePath) {
		System.out.println("path:"+packagePath.replaceAll("\\.", "/"));
		URL url = this.getClass().getClassLoader().getResource("/"+packagePath.replaceAll("\\.", "/"));
		File file = new File(url.getFile());
		for(File sub:file.listFiles()) {
			if(sub.isDirectory()) {
				doScanPackage(packagePath+"."+sub.getName());
			}else {
				String clsName = packagePath+"."+sub.getName().replace(".class","");
				this.clsList.add(clsName);
			}
		}
	}        

  4.3、初始化類

      private void initBeanInitializing() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		for(String clsName:clsList) {
			Class cls = Class.forName(clsName);
			if(cls.isAnnotationPresent(CWController.class)) {
				String name = cls.getSimpleName();
				beanFactory.put(lowerFirstChar(name) , cls.newInstance());
			}else if(cls.isAnnotationPresent(CWService.class)) {
				CWService service = (CWService) cls.getAnnotation(CWService.class);
				String defaultName = service.value();
				if(!"".equals(defaultName)) {
					beanFactory.put(defaultName, cls.newInstance());
				}else {
					Class[] interfaces = cls.getInterfaces();
					Object instance = cls.newInstance();
					for(Class inter:interfaces) {
						beanFactory.put(lowerFirstChar(inter.getSimpleName()), instance);
					}
				}
			}else  if(cls.isAnnotationPresent(CWComponent.class)){
				String name = cls.getName();
				beanFactory.put(lowerFirstChar(name), cls.newInstance());
			}
		}
	}

  4.4、完成類的依賴註入

      private void initBeanAutowired() throws IllegalArgumentException, IllegalAccessException {
		for(Map.Entry<String, Object> entry:beanFactory.entrySet()) {
			Object bean = entry.getValue();
			Field[] fields = bean.getClass().getDeclaredFields();
			if(null == fields || fields.length ==0 ) {
				continue;
			}
			for(Field field:fields) {
				if(field.isAnnotationPresent(CWAutowired.class)) {
					String fieldTypeName = field.getType().getSimpleName();
					field.setAccessible(true);
					field.set(bean, beanFactory.get(this.lowerFirstChar(fieldTypeName)));
				}
			}
		}
	}

  4.5、完成request的路徑映射

      private void initServletDispatch() {
		for(Map.Entry<String, Object> entry:beanFactory.entrySet()) {
			Object bean = entry.getValue();
			System.out.println(entry.getKey()+":"+entry.getValue());
			if(!bean.getClass().isAnnotationPresent(CWController.class)) {
				continue;
			}
			// 基本路徑
			String basePath = "";
			if(bean.getClass().isAnnotationPresent(CWRequestMapping.class)) {
				basePath = ((CWRequestMapping)bean.getClass().getAnnotation(CWRequestMapping.class)).value();
			}
			System.out.println("=========>basePath:"+basePath);
			// 方法路徑
			Method[] methods = bean.getClass().getMethods();
			for(Method method:methods) {
				if(method.isAnnotationPresent(CWRequestMapping.class)) {
					String path = ((CWRequestMapping)method.getAnnotation(CWRequestMapping.class)).value();
					requestMap.put(("/"+basePath +"/"+path).replaceAll("/+", "/"), method);
				}
			}
		}
	}

  5、處理頁面請求

        private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException, IOException {
		String url = req.getRequestURI();
		String contextPath = req.getContextPath();
		url = url.replace(contextPath, "").replaceAll("/+", "/");
		System.out.println("=========>request url:"+url);
		Method method = this.requestMap.get(url);
		if(null == method) {
			resp.getOutputStream().write("404 not found!".getBytes("UTF-8"));
			return;
		}
		String beanName = this.lowerFirstChar(method.getDeclaringClass().getSimpleName());
		try {
			method.invoke(beanFactory.get(beanName), req,resp);
		}catch (Exception e) {
			e.printStackTrace();
			resp.getOutputStream().write("404 not found!".getBytes("UTF-8"));
		}
	}    

  6、添加測試類了

  HelloWorldAction.java

@CWController
@CWRequestMapping("/h")
public class HelloWorldAction {
	@CWAutowired
	private IUserService userService;
	
	@CWRequestMapping("/hello.htm")
	public void hello(HttpServletRequest request,HttpServletResponse response) {
		try {
			System.out.println(userService.getUserNameById(1L));
			response.getWriter().write("hello World!");
			response.flushBuffer();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

  IUserService.java

public interface IUserService {
	
	public String getUserNameById(Long id);
}

  UserServiceImpl.java

@CWService
public class UserServiceImpl implements IUserService {

	@Override
	public String getUserNameById(Long id) {
		return "master";
	}

}

  7、tomcat啟動,大功告成

  http:127.0.0.1:8080/h/hello.htm

總結:

  1、看別人的代碼,很簡單,自己實現就發現很多坑。關鍵還是要自己實踐。實踐!實踐!實踐!

備註:

  1、參考來源

  https://www.toutiao.com/i6636629045259796995/

  2、工程源碼

  https://pan.baidu.com/s/1F0el_nwDJ99-AYueeH0esw


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、資料庫的安裝和連接 2、創建表操作 3、查詢操作 Python查詢Mysql使用 fetchone() 方法獲取單條數據, 使用fetchall() 方法獲取多條數據。 fetchone(): 該方法獲取下一個查詢結果集。結果集是一個對象 fetchall(): 接收全部的返回結果行. rowc ...
  • 轉載:https://www.cnblogs.com/gonjan-blog/p/6685611.html 關於Java中的動態代理,我們首先需要瞭解的是一種常用的設計模式--代理模式,而對於代理,根據創建代理類的時間點,又可以分為靜態代理和動態代理。 一、代理模式 代理模式是常用的java設計模式 ...
  • 發生原因:運行javac編譯時沒有加上擴展名。解決方法:加上.java擴展名重新編譯即可,"xxxxxx.java"。 ...
  • IDE:Integrated Develop Environment,集成開發環境。利用IDE編程的好處不用多說,節省大量的時間和精力。 現在總結一下利用IDE編寫一個HelloWorld的具體流程。首先是建立一個項目,得有一個項目名。 具體就是:File->New->Java Project 接下 ...
  • 使用迭代器remove方法刪除元素 public class InteratorDemo { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("老師"); list.ad ...
  • 在spring 3.2中,新增了@ControllerAdvice 註解,可以用於定義@ExceptionHandler、@InitBinder、@ModelAttribute,並應用到所有@RequestMapping中。@ControllerAdvice官方文檔。創建全局異常處理類:通過使用@C... ...
  • 簡介 本系列基於 的官方文檔,除去了文檔中一些冗餘的東西,加上了一些自己的理解,意圖是在於幫助更多初識 的人來進行一次探險。 本系列建議具有Java基礎和Spring使用經驗的同學學習。 什麼是Spring Boot 在使用之前,我們先來看看 到底是個啥,從字面意義上去理解,是Spring的一個引導 ...
  • 一、spring概述 Spring是一個開放源代碼的設計層面框架,他解決的是業務邏輯層和其他各層的松耦合問題,因此它將面向介面的編程思想貫穿整個系統應用。Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson創建。簡單來說,Spring是一個分層的JavaSE/E ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...