自己手写一个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处理请求的流程:

自己手写一个springmvc框架,理解ioc容器

具体的执行步骤如下:

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中使用到了很多的自定义的注解,这里需要自己去定义这些注解,代码如图中。

自己手写一个springmvc框架,理解ioc容器

自己手写一个springmvc框架,理解ioc容器

自己手写一个springmvc框架,理解ioc容器

自己手写一个springmvc框架,理解ioc容器

 

③需要定义下自己的Action和自己的 service层,并且互相之间通过 注解互相依赖注入

controller代码:

自己手写一个springmvc框架,理解ioc容器

service代码:

自己手写一个springmvc框架,理解ioc容器

写完之后可以进行测试,工程的项目结构如下:

自己手写一个springmvc框架,理解ioc容器

在浏览器输入localhost:8080/MySpringMVC这个时候便可以看到程序会加载我们写的 MyDispatcherServlet