为了熟悉springmvc的执行流程,自己手写代码模拟springmvc流程
1.写一个前端控制器的servlet继承HTTPservlet,并且对所有的访问进行拦截(*.do)
2.为了实现能在一个类中执行多种访问业务我们要拿到所有访问业务的方法(也就是controller中的方法),这里我们通过前端控制器先获取字节码文件的储存路径再加上自己写的包名来获取所有的controller类的class对象(这一步中的包名在spring中是进行了包扫描来获取的而我们这里简单的固定一个包)
3.上面我们就获取了所有的controller类的class对象那么我们就可以获取class对象中的全部方法,在对方法数组里的所有方法进行判断是否有相应的注释,如果是我们要的方法那么就存入一个map对象中键是访问的URI值是method对象。
4。有了所有想要method对象我们在对method对象的参数进行判断根据参数的不同执行也不同
以上就是大体的逻辑思路,下面来实现这部分的代码,具体的说明都注释在代码中了
文件夹结构
注解代码
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface requestMapping {
String value() default "";
}
模拟controller代码
package controller;
import annotation.requestMapping;
public class userController {
@requestMapping("/test.do")
public void test(){
System.out.println("test");
}
@requestMapping("/ttt.do")
public void ttt(){
System.out.println("ttt");
}
}
模拟前端控制器代码
package springmvc;
import annotation.requestMapping;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@WebServlet(value = "*.do",loadOnStartup = 1)
public class DispectherServlet extends HttpServlet {
private Map<String,Method> map = new HashMap<>();
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String requestURI = req.getRequestURI();
System.out.println(requestURI);
String substring = requestURI.substring(requestURI.lastIndexOf("/"));
System.out.println(substring);
//通过对请求字符串的处理获取方法.do,在从map中找到想要的方法
Method targetmethod = map.get(substring);
System.out.println(targetmethod.getName());
//取得方法的所有参数
Class[] parameterTypes = targetmethod.getParameterTypes();
/**
* 取得方法的所属类用于执行方法
* 类<?> getDeclaringClass()
* 返回 类表示声明该对象表示的可执行的类或接口对象。
*/
Class declaringClass = targetmethod.getDeclaringClass();
System.out.println(declaringClass.getSimpleName());
System.out.println(parameterTypes.length);
//按照参数的不同执行方法
if(parameterTypes.length==0){
try {
Object o = declaringClass.newInstance();
targetmethod.invoke(o);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}else {
if (parameterTypes.length==1){
try {
Object o = declaringClass.newInstance();
targetmethod.invoke(o,req);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}if (parameterTypes.length==2){
try {
Object o = declaringClass.newInstance();
targetmethod.invoke(o,req,resp);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}
@Override
public void init(ServletConfig config) throws ServletException {
//获取资源文件的路径
String rooturl = this.getClass().getResource("/").getPath();
System.out.println(rooturl);
//创建文件来访问资源文件路径中的所有controller类
File file = new File(rooturl+"/controller");
System.out.println(rooturl+"/controller");
//获取所有的文件
File[] files = file.listFiles();
//遍历文件一个一个通过反射处理他们的方法
for (File f:files) {
//获取字节码对象,通过字符串拼接获取指定的类
Class<?> fClass = null;
try {
//拼接出全类名controller是配置好的被扫描的包
fClass = Class.forName("controller." + f.getName().replaceAll(".class", ""));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//这边我们可以通过反射生产一个controller对象,这样出来的是单例模式的controller
System.out.println(fClass.getSimpleName());
//获取全部方法
Method[] methods = fClass.getDeclaredMethods();
//判断如果方法被mapping注释标识了则是我们需要的加入到map中
System.out.println(methods.length);
for (Method m:methods) {
if(m.isAnnotationPresent(requestMapping.class)){
String value = m.getAnnotation(requestMapping.class).value();
System.out.println(value);
map.put(value,m);
}
}
}
}
}