1. 程式人生 > >自己手寫一個springmvc框架,理解ioc容器

自己手寫一個springmvc框架,理解ioc容器

首先我們在手寫springmvc這個框架之前,我們首先要回顧一下springmvc的原理:

2.1、Spring Web MVC是什麼

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

Springmvc 也是服務到工作者模式的實現,但是也進行可優化,前端控制器是 DispatcherServlet,應用控制器其實拆分為處理器對映器(HandlerMapping)和進行處理器管理的檢視解析器(View Resolver)進行檢視層的管理;頁面控制,動作,處理器為Controller介面(僅包含 ModelAndView handleRequest(request,response) 方法)的實現(也可以是任何的POJO類);

2.Springweb mvc架構

Spring Web MVC也是一個基於請求驅動的web框架,並且使用了前端控制器進行設計,再根據請求對映規則分發給頁面的控制器(動作/處理器)進行處理,首先,讓我們整體看一下Springwebmvc處理請求的流程:

具體的執行步驟如下:

1.首先使用者傳送請求------->前端控制器,前端控制器根據請求的資訊,url來決定選擇哪一個頁面的控制器記性處理並且把請求委託給他,即以前的控制器的控制邏輯部分

2.頁面控制器接收到請求後,進行功能處理,首先需要收集和繫結請求引數到一個物件,這個物件在spring web mvc中叫做命令物件,並且進行驗證,然後將命令物件委託給業務物件進行處理;處理完畢之後返回一個ModelAndView(模型資料和邏輯檢視名);圖2-1中的3,4,5步驟:

3.前端控制器收回控制權,然後根據返回的邏輯檢視名,選擇相應的檢視進行渲染,並把模型資料傳入以便檢視進行渲染;途中步驟6、7

4.前端控制器再次收回控制權,將響應返回給使用者,至此整個流程結束

接下來,我們用程式碼去實現一下springmvc這個框架:

①首先我們要明確我們的步驟,首先需要一個 DispathcerServlet,用於處理請求並且派發請求:

我們手寫一個  MyDispatcherServlet,裡面包含 servlet的初始化 init 方法,servlet中原有的 doGet和doPost方法。

當然,我們在初始化這個servlet的時候需要執行下面的步驟:

1.載入系統中的一些配置檔案,properties等檔案,或者web.xml當中配置的一些資訊.

2.掃描一下系統中的所有的類

3.掃描以下系統當中所有的類,並且通過反射機制,實現初始化,並且放置到IOC容器當中去。

4.實現依賴注入,把含有@Autowired的欄位都依賴注入進其應該存在的類當中。

5.初始化HandlerMapping,就是把url和Method關聯上

6.請求的派發,當請求到達伺服器的時候,需要讓不同的路徑分別對映到不同的方法上面去執行。

這裡貼出了自己寫的模仿 springmvc 的 DispatcherServlet 

package com.zwz.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;


import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import com.zwz.annotation.GPAutowired;
import com.zwz.annotation.GPController;
import com.zwz.annotation.GPRequestMapping;
import com.zwz.annotation.GPService;




/*
 自定義的派發請求的servlet
 */
public class MyDispatcherServlet extends HttpServlet {


/**
* Constructor of the object.
*/
public MyDispatcherServlet() {
super();
}


/**
* Destruction of the servlet. <br>
*/
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}




//這個Properties的作用是用來載入配置檔案
public static Properties prop = new Properties();
//這裡寫一個list集合,用於存放掃描出來所有的檔案的類名
public static List<String>classNames = new ArrayList<String>();
//這裡定義一個IOC的Map容器,用於存放實體類物件
public static Map<String,Object>iocmap = new HashMap<String,Object>();
//這裡再定義一個Map容器,用來記錄,使用的是ConcurrentHashMap是執行緒安全的,這裡定義的這個Map是用來記錄  HandlerMapping,處理器對映器的
ConcurrentHashMap<String,Method> hm = new ConcurrentHashMap<String,Method>();


@Override
public void init(ServletConfig config) throws ServletException {
// TODO Auto-generated method stub
super.init(config);
//1.載入配置檔案
loadConfig(config);

System.out.println("demo.mvc");   // prop.get("scannerPackage");

//2.初始化所有的相關的類。掃描使用者設定的包下面的所有的類
doScanner((String) prop.get("scannerPackage"));
//3.把這些掃描到的類通過反射機制,實現初始化,並且放到IOC容器之中, (Map beanName)
doIOCInstance(classNames);

//4.實現依賴注入
doAutowired();

//5.初始化 HandlerMapping 就是將url和Method關聯上
doHandlerMapping();

for(Entry<String,Method>entry:hm.entrySet()){
System.out.println( entry.getKey() );
System.out.print( "  :  " );
System.out.println(  entry.getValue()  );
}


//initHandlerMapping();
}


/*做一個對映,
* 把請求的url地址和  handlerMapping關聯上
* */
public void doHandlerMapping(){
//先通過ioc容器獲取到容器中的所有的bean物件
for(Entry<String,Object>entry:iocmap.entrySet()){

String key = entry.getKey();

Object instance = entry.getValue();

//獲取bean物件,取出bean物件裡面的所有方法上面帶了requestMapping的註解
//判斷例項是否是annotation

//如果類上面沒有controller註解則跳過繼續下一個
if(!instance.getClass().isAnnotationPresent( GPController.class )){ continue; }
//baseUrl獲取到類上面的註解
String baseUrl = "";
if( instance.getClass().isAnnotationPresent(GPRequestMapping.class) ){
GPRequestMapping requestMapping = instance.getClass().getAnnotation( GPRequestMapping.class );
baseUrl = requestMapping.value();
}

Method[] methods = instance.getClass().getMethods();
for(Method method:methods){
String mname = method.getName();
//方法上面是否有RequestMapping註解
if(method.isAnnotationPresent(  GPRequestMapping.class )){

String annotationValue = method.getAnnotation(GPRequestMapping.class).value();

hm.put( baseUrl+"/"+annotationValue , method );
}

}


//GPRequestMapping requestMapping = 

}



}

///進行依賴注入的操作
public void doAutowired(){

if(iocmap.isEmpty()){return;}

//遍歷所有ioc容器中的物件
for(Entry<String,Object>entry:iocmap.entrySet()){
String key = entry.getKey();
Object instance = entry.getValue();

//獲取到實體的所有的欄位
Field[] fields = instance.getClass().getDeclaredFields();

for(Field field:fields){
//判斷欄位上是否有 Autowired註解
if(!field.isAnnotationPresent(GPAutowired.class))continue;
//獲取Autowired註解
GPAutowired gPAutowired = field.getAnnotation(GPAutowired.class);

String beanName = gPAutowired.value();

if("".equals(beanName)){
beanName = field.getType().getName();
}
//強吻
field.setAccessible(true);

Object obj = iocmap.get(beanName);

System.out.println(instance);
System.out.println(obj);


try {
field.set(instance, iocmap.get(beanName));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

}

}


//doIOCInstance(iocmap);

//進行ioc容器
public void doIOCInstance( List<String>classNames ){

for(String classname:classNames){

System.out.println(classname);

//通過反射載入類
try {
/*
Class clazz = Class.forName("demo.mvc.action.DemoAction");

Object instance = clazz.newInstance();
*/

Class clazz = Class.forName(classname);

Object instance = clazz.newInstance();
//if(instance instanceof )


//如果是一個註解 
if(clazz.isAnnotationPresent( GPController.class )){
String simpleName = lowerFirst(clazz.getSimpleName());

iocmap.put( simpleName , instance );

}else if(clazz.isAnnotationPresent( GPService.class )){

//通過類的位元組碼檔案獲取到類上的註解
GPService service = (GPService) clazz.getAnnotation(GPService.class);

String beanName = service.value();

if("".equals(beanName)){
beanName = lowerFirst( clazz.getSimpleName() );
}

iocmap.put( beanName , instance );

}




//進行判斷:如果  自己有名字,那麼就要用自己的名字優先

//如果自己沒有設定名字,那麼預設首字母小寫

//如果@Autowired標註的是一個介面的話,那麼預設要將其實現類的例項注入進來



} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}

}



//這裡寫一個工具類的方法,用於將元素的第一個首字母變成小寫
public static String lowerFirst(String oldStr){

char[]chars = oldStr.toCharArray();

chars[0] += 32;

return String.valueOf(chars);

}


//載入配置檔案
public void doScanner(String packageName){
//通過類載入器載入一個資源,這裡是根路徑  demo/mvc  下的檔案

//System.out.println(  scannerPackage.lastIndexOf("\\/") );

//scannerPackage = scannerPackage.substring( scannerPackage.lastIndexOf("\\")+1 );

URL url = this.getClass().getClassLoader().getResource( "/" + packageName.replaceAll("\\." , "\\/") );
//通過資源的url獲取到當前的檔案
File file = new File(url.getFile());
//遍歷檔案路徑下的每一個檔案,獲取所有的檔案
for(File f:file.listFiles()){
if( f.isDirectory() ){
doScanner( packageName+"."+f.getName() );
}else{
// iocmap.put( f.getName() , value);
classNames.add( packageName+"."+f.getName().replace(".class", "" ) );
}
}
//file.isDirectory()
}



//載入配置檔案
public void loadConfig(ServletConfig config){

String whatproperties = config.getInitParameter("initconfig");

InputStream in = this.getClass().getClassLoader().getResourceAsStream(whatproperties);

try {
prop.load(in);

String scannerPackage = (String) prop.get("scannerPackage");

System.out.println(scannerPackage);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//System.out.println(scannerPackage);
}




/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.

* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {


System.out.println("執行doGET方法");


}


/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to post.

* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {


doGet( request,response );

}


/**
* Initialization of the servlet. <br>
*
* @throws ServletException if an error occurs
*/
public void init() throws ServletException {
// Put your code here
}


}

②DispatchServlet中使用到了很多的自定義的註解,這裡需要自己去定義這些註解,程式碼如圖中。

③需要定義下自己的Action和自己的 service層,並且互相之間通過 註解互相依賴注入

controller程式碼:

service程式碼:

寫完之後可以進行測試,工程的專案結構如下:

在瀏覽器輸入localhost:8080/MySpringMVC這個時候便可以看到程式會載入我們寫的 MyDispatcherServlet

相關推薦

自己一個springmvc框架理解ioc容器

首先我們在手寫springmvc這個框架之前,我們首先要回顧一下springmvc的原理: 2.1、Spring Web MVC是什麼 Spring Web MVC是一種基於Java的實現了Web MVC設計模式的請求驅動型別的輕量級Web框架,即使用了MVC架構

springmvc流程圖 轉載 自己一個SpringMVC框架

轉載 https://blog.csdn.net/lang_programmer/article/details/71598042 轉載 https://www.cnblogs.com/java1024/p/8556519.html 轉載 https://www.cnblogs.com

自己一個SpringMVC框架

Spring框架對於Java後端程式設計師來說再熟悉不過了,以前只知道它用的反射實現的,但瞭解之後才知道有很多巧妙的設計在裡面。如果不看Spring的原始碼,你將會失去一次和大師學習的機會:它的程式碼規範,設計思想很值得學習。我們程式設計師大部分人都是野路子,不懂什麼叫程式碼

看年薪50W的架構師如何一個SpringMVC框架(文末附視訊)

前言 做 Java Web 開發的你,一定聽說過SpringMVC的大名,作為現在運用最廣泛的Java框架,它到目前為止依然保持著強大的活力和廣泛的使用者群。 本文介紹如何用eclipse一步一步搭建SpringMVC的最小系統,所謂最小系統,就是足以使專案在SpringMVC框架下成功

400行程式碼一個SpringMVC框架-咕泡學院VIP視訊下載,百度網盤

  400行程式碼手寫一個SpringMVC框架 https://pan.baidu.com/s/1SAKk2etjQ_1M4cNBXBjk2Q  密碼:f2zv  咕泡學院Java架構師每日錄播視訊索取加QQ群:788692365 咕泡學院Java架構師

自己一個springmvc

但是原文中有個問題,就是DispatcherServlet類中,doPost方法有一句 : SpringmvcController controller = (SpringmvcControlle

IDEA 中 一個SpringMVC框架

不說了直接上程式碼吧(裡面也有相應的說明)程式碼地址 https://github.com/dongfucai/handerMVC自己自己java 。如果有幫助,可以點贊,點星。有問題可以提出,一起學習改正。後面是設計思路梳理SpringMVC的設計思路       本文只實

簡易springmvc框架

程式碼:https://github.com/JZWen/writeSpringMvc 回顧一遍: 準備工作:先寫兩個註解類 controller層的類 並寫上註解 自己寫dispatcherServlet  寫的也不是很完善 在裡面完成 init()&n

利用netty自己一個tomcat

基於javaee基礎上執行的一個web容器 http  伺服器 socktet上支援 TCP/IP HTTP FTP 接受客戶端的一個http url請求,對應後臺有一個servlet netty屬於協議層,底層框架。 request  response 4.0版本 pu

自己一個 VB 的 DateAdd 函式(VB/C 雙語言版本)

         可能有些朋友覺得這是多餘的事情,既然 VB 有了 DateAdd 函式來進行日期運算,幹嘛還要自己手寫一個? 其實這一點也不多餘,因為有些時候我們的開發環境不一定就在 VB 裡,而那些個開發環境不一定就有那麼多現成的函式可以使用,那麼這個時候要做個日期運算那

一個Spring框架(不含AOP)

spring 手寫分三個階段: 1.配置階段: web.xml配置 servlet初始化 2.初始化階段: 載入配置檔案 ioc容器初始化 掃描相關的類 類例項化,並注入ioc容器 將url路徑和相關method進行對映關聯 3執行階段 dopost作為入

Spring系列之一個SpringMVC

目錄 Spring系列之IOC的原理及手動實現 Spring系列之DI的原理及手動實現 Spring系列之AOP的原理及手動實現 Spring系列之手寫註解與配置檔案的解析 引言 在前面的幾個章節中我們已經簡單的完成了一個簡易版的spring,已經包括容器,依賴注入,AOP和配置檔

Redux自己一個簡化版全面的redux

import React from 'react' //播放器 const renderScreen = (screen)=>{ console.log("=============>renderScreen"); //獲取頁面元素 c

PHP MVC框架基礎小白(自己動手一個PHP框架示例)

一個示例專案,具體展示了PHP,MVC模式框架開發的全過程本人小白一個,希望各位大神多多指教,由於教程文字過多而且不易解釋我直接將示例專案打包,下面附上鍊接各位對PHP框架學習可以借鑑專案內帶有其他方法和註釋,希望能對大家有所幫助

迷你SpringMVC框架

前言 學習如何使用Spring,SpringMVC是很快的,但是在往後使用的過程中難免會想探究一下框架背後的原理是什麼,本文將通過講解如何手寫一個簡單版的springMVC框架,直接從程式碼上看框架中請求分發,控制反轉和依賴注入是如何實現的。 建議配合示例原始碼閱讀,github地址如下: https://g

一個React-Redux玩轉React的Context API

[上一篇文章我們手寫了一個Redux](https://juejin.im/post/5efec81be51d4534942dd589),但是單純的Redux只是一個狀態機,是沒有UI呈現的,所以一般我們使用的時候都會配合一個UI庫,比如在React中使用Redux就會用到`React-Redux`這個庫。這

一個HTTP框架:兩個類實現基本的IoC功能

> [jsoncat](https://github.com/Snailclimb/jsoncat): 仿 Spring Boot 但不同於 Spring Boot 的一個輕量級的 HTTP 框架 國慶節的時候,我就已經把 jsoncat 的 IoC 功能給寫了,具體可以看這篇文章《手寫“Spring

曹工說Tomcat4:利用 Digester 一個輕量的 Spring IOC容器

一、前言 一共8個類,擼一個IOC容器。當然,我們是很輕量級的,但能夠滿足基本需求。想想典型的 Spring 專案,是不是就是各種Service/DAO/Controller,大家互相注入,就組裝成了我們的業務bean,然後再加上 Spring MVC,再往容器裡一放,基本齊活。 我們這篇文章,就是要照著

動態代理:如何深入理解和分析不如一個(原始碼包分析、樓主親測)

如何分類Java語言? Java是靜態的強型別語言,但是因為提供了類似反射等機制,也具備了部分動態語言的能力。 一、動態代理的簡單描述 動態代理是一種方便執行時動態構建代理、動態處理代理方法呼叫的機制,很多場景都是利用類似的機制做到的,比如用來包裝RPC呼叫、面向切面的程式設

Java反射:如何正確理解不如一個(反射包分析、樓主親測)

Java反射機制、動態代理是基於什麼原理? 這個問題可謂是老生常談的一個熱門問題了,如果沒有深入的思考還真的是很難回到上來。那麼今天我們一起來看看,如何正確清晰的認識這個熱門卻又說簡單又不簡單說複雜又比較複雜的問題。 一、什麼是反射 反射機制是Java語言提供的一種基礎功能