模拟Springmvc 手写一个HandlerMapping完成一个前端的url请求处理

模拟Springmvc 手写一个HandlerMapping完成一个前端的url请求处理

工程是 web工程2.5

pom文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>duzhicheng.spring</groupId>
  <artifactId>springmvc</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>springmvc</name>
  <description>springmvc</description>
  
  
   <dependencies>
  	<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>javax.servlet-api</artifactId>
	    <version>3.1.0</version>
	    <scope>provided</scope>
	</dependency>
  </dependencies>
</project>

只引入了一个servlet的包

web.xml servlet的入口配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>springmvc</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  
  
   <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>duzhicheng.spring.mode.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- servlet路径映射 -->
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

整个工程做到的仅仅只是完成一个url到 controller的方法的调用,并且存在很多的问题待处理

首先先定义四个注解 @controller @server @Autowired @requeatMapping

//在这里列举一个

@Documented  
@Retention(RetentionPolicy.RUNTIME)  //注解在什么时候作用  有三个时间段   1 源码阶段  2 编译阶段  3 运行中  
@Target({ ElementType.TYPE})  //注解所作用的域   类  方法 属性 参数 .....
public @interface Controller {
	
	String Value() default "";

}


然后我们自己写一个DispatcherServlet

package duzhicheng.spring.mode.servlet;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
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.Set;

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

import duzhicheng.spring.mode.annotation.Autowired;
import duzhicheng.spring.mode.annotation.Controller;
import duzhicheng.spring.mode.annotation.RequestMapping;
import duzhicheng.spring.mode.annotation.Server;

public class DispatcherServlet extends HttpServlet{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1456062885004779439L;
	
	//存放包下的字节码的
	private final ArrayList<String> array = new ArrayList<>();
	
	//存放   类路径 与类实例之间的映射        /XXXX   ===>  /XXXX这个类的实例
	private final Map<String, Object> classMappers = new HashMap<String, Object>();
	
	//存放  类路径+方法路径 与方法名之间的映射        /XXXX   ===>  /XXXX这个类的实例
	private final Map<String, Method> methodsMappers = new HashMap<String, Method>();   //这个东西 就相当于一个HandlerMapping
	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp ) {
		try {
			//获取到访问的全路径
			String requestURI = req.getRequestURI(); //   /工程名+类路径+方法路径
			//获取工程路径
			String contextPath = req.getContextPath();
			//把工程路径删掉 , 只留下后面的  类路径和方法路径
			String url = requestURI.replace(contextPath, "");
			//循环我们的map  里面存的就是  路径 <===> 和需要调用的方法
			for (Entry<String, Method> Entry : methodsMappers.entrySet()) {
				//如果访问路径和我们存的路径相等
				if(Entry.getKey().equals(url)) {
					//获取相对应的方法
					Method Method = Entry.getValue();
					//通过这个方法获取这个类的类名
					String string = Method.toString().substring(0,Method.toString().lastIndexOf("."));
					string = string.substring(string.lastIndexOf(".")+1);
					//获取对象
					//通过url  /aaa/bbb   取到 /aaa   通过前面的classMapper中存的  类路径    ===   类的实例   取到controller的实例
					Object object = classMappers.get("/"+url.split("/")[1]);
					if(object == null) {
						//代表类上面没有requestMapping注解
						object = classMappers.get(string);
					}
					//调用方法
					Method.invoke(object, null);
				}
			}
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp ) {
		doGet(req, resp);
	}
	
	@Override
	public void init() {
		try {
			//扫包
			scanPackage("duzhicheng.spring.mode");
			if(array.isEmpty()) {
				System.out.println("兄弟,怕是出了点问题哟");
				return;
			}
			//类路径映射
			classMapper();
			//依赖注入
			fieldMapper();
			//方法映射    
			methodsMapper();   // 做到这里就相当于完成了一个HandlerMapping
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void fieldMapper() throws InstantiationException, IllegalAccessException {
		for (Entry<String, Object> Entry : classMappers.entrySet()) {
			Object object = Entry.getValue();
			Field[] fields = object.getClass().getDeclaredFields();
			for (Field field : fields) {
				//设置为强制访问
				field.setAccessible(true);
				//判断属性上面是不是有  @Autowired  注解
				if(field.isAnnotationPresent(Autowired.class)) {
					//这里取到被注解标识属性的类型calss 这里我们一般都是注入的接口 
					Class<?> class1 = field.getType();
					// 再次循环 取到路面的每一个value值  就是里面的每一个实例
					for (Entry<String, Object> Entry1 : classMappers.entrySet()) {
						Object object2 = Entry1.getValue();
						//如果class1 ==> 这个接口的class  是object2 的被实现接口
						//如果 object2   是 class1 的实现类
						if(class1.isInstance(object2)) {
							//给这个属性重新赋值
							field.set(object, object2);
						}
					}
				}
			}
		}
	}
	
	private void methodsMapper() throws Exception {
		if(classMappers.isEmpty()) {
			System.out.println("兄弟 , 你怕是掉了什么注解哟");
			return;
		}
		for (Entry<String, Object> Entry : classMappers.entrySet()) {
			//这里的ClassMapperName   就是我们类上面的路径   
			String ClassMapperName = Entry.getKey();
			if(!ClassMapperName.contains("/")) {
				ClassMapperName = "";
			}
			Class<? extends Object> class1 = Entry.getValue().getClass();
			Method[] methods = class1.getMethods();
			for (Method method : methods) {
				if(method.isAnnotationPresent(RequestMapping.class)) {
					//获取到方法上面requestmapeer里面的值
					RequestMapping mapping = method.getAnnotation(RequestMapping.class);
					//value  ===>  方法上的路径
					String value = mapping.value();
					methodsMappers.put(ClassMapperName+value, method);
				}
			}
		}
	}

	public void classMapper() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		for (String string : array) {
			//获取这个类的字节码 ,在加载的时候是不需要后缀的,仅需要完整的名包+文件名
			Class<?> clazz = Class.forName(string.replace(".class", ""));
			//获取这个类的类名
			String string2 = clazz.toString().substring(clazz.toString().lastIndexOf(".")+1);
			//找到所有带controller的类
			if(clazz.isAnnotationPresent(Controller.class)){
				//进行实例化
				Object object = clazz.newInstance();
				//获取类上面的mapping注解        mapping = @RequestMapping(index='/xxxx')
				RequestMapping mapping = clazz.getAnnotation(RequestMapping.class);
				//获取到里面的值   value = /xxxx
				String value = "";
				//判断类上面是否存在RequestMapping注解
				if(null != mapping) {
					//存在  map的key为 对应的value值
					value = mapping.value();
				}else {
					//不存在 就用类名进行代替 , 同一个包下面类名是唯一的 
					value = string2;
				}
				//这一步就是完成类上面的路径映射
				//例如 我们的访问路径是  localhost:8080/test/index
				//那我们这里的value值就是    /test
				//当我们吧 value  和我们已经实例化出来的类   put到一个map里面 是不是以为着  当我们访问/test的时候 我们就可以直接访问这个类
				classMappers.put(value, object);
			}
			if(clazz.isAnnotationPresent(Server.class)){//找到的是实现类
				//进行实例化
				Object object = clazz.newInstance();
				classMappers.put(clazz.getName(), object);
			}
		}
	}
	
	private void scanPackage(String string) {
		//这里我们传入的路径是   aaa.bbb.ccc   我们需要换成aaa/bbb/ccc  目录结构的形式
		String packagePath = string.replaceAll("\\.", "/");
		//获取全路径 盘符开始 
		URL url = this.getClass().getClassLoader().getResource("/"+packagePath);
		File[] files = new File(url.getFile()).listFiles();
		for (File file : files) {
			if(file.isDirectory()) {
				//如果是目录
				String name = file.getName();
				scanPackage(string+"."+name);
			}else {
				//例如包是XXX.XXX.XXX.AAA
				//在这个我们获取的文件是AAA
				//当类加载的时候是全限命名的,所以要加上包名
				array.add(string+"."+file.getName());
			}
		}
	}
}


写完之后大概测了一下 , 还存在很多的问题

但基本的还是可以跑得通的

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值