自己模拟一个SpringMVC框架

在模拟SpringMVC之前,我们先部署一个web项目到Tomcat下,并进行简单的访问

 web.xml文件内容:

<?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
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">
  
</web-app>

index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>hello world</h1>
</body>
</html>

部署到Tomcat后,进行访问

为什么我们通过http://localhost:8080/helloworld,就可以访问到index.jsp呢?

第一,当Tomcat启动后,除了在本地开启8080端口外,还会对项目进行一个虚拟目录映射(Tomcat是server.xml中)

 第二,我们访问Tomcat服务的虚拟目录时,如果没有指定Serlvet时,他会有一个缺省的DefaultServlet(Tomcat的web.xml文件)

 

上面对web项目的访问方式: http://localhost:8080/helloworld

下面我们模拟访问SpringMVC: http://localhost:8080/springmvc/hello/index

模拟SpringMVC总线:

1.自己模拟Controller注解

2.自己模拟RequestMapper注解

3.自己模拟RequestParam注解

4.自己模拟MyService注解

5.自己模拟Autoware注解

6.自己模拟DispatcherServlet

7.搭建web项目测试

模拟Controller注解

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

模拟RequestMapper注解

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

模拟RequestParam注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
    String value() default "";
}

模拟MyService注解

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

模拟Autoware注解

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

 模拟DispatcherServlet

public class DispatcherServlet extends HttpServlet {
     
    private static final long serialVersionUID = 2571175759534212451L;

    private List<String> classNames = new ArrayList<String>(); //指定包下的所有对象

    private Map<String, Object> beans = new HashMap<String, Object>(); //扫描到的所有的对象
     
    private Map<String, Object> handlerMap = new HashMap<String, Object>();  //所有请求URL和对象方法的映射
 
    @Override
    public void init() throws ServletException {
        doScanPackage("cn.enn");  //扫描哪些对象需要创建
        doInstance(); //创建对象实例
        doIoc(); //依赖注入
        handlerMapping(); //URL和方法映射
    }

    //1.把我们指定的cn.enn下的所有class文件加载到集合中
    private void doScanPackage(String basePackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
        String fileStr = url.getFile();
        File file = new File(url.getFile());
        String[] listFiles = file.list();
        for (String path : listFiles) {
            File filepath = new File(fileStr + path);
            if (filepath.isDirectory()) {
                doScanPackage(basePackage + "." + path);
            } else {
                classNames.add(basePackage + "." + path.replace(".class", ""));
            }
        }
    }

    //2.把加载的对象,实例化对象
    private void doInstance() {
        for (String className : classNames) {
            try {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(MyController.class)) {   //映射Controller对象
                    Object obj = clazz.newInstance();
                    MyRequestMapper requestMapping = clazz.getAnnotation(MyRequestMapper.class);
                    String key = requestMapping.value();
                    //  /hello->HelloController
                    beans.put(key, obj);
                } else if (clazz.isAnnotationPresent(MyService.class)) { //映射Service对象
                    Object obj = clazz.newInstance(); 
                    MyService annotation = clazz.getAnnotation(MyService.class);
                    String key = annotation.value();
                    if (key.equals("")) {
                        Class<?>[] interfaces = clazz.getInterfaces();
                        if (interfaces.length > 0) {
                            key = toLowerFirstWord(interfaces[0].getSimpleName()); //如果父类存在,key为父类的名称
                        } else {
                            key = toLowerFirstWord(className.substring(className.lastIndexOf(".") + 1)); //如果父类不存在,则以当前类名称为key
                        }
                    }
                    // 比如HelloService ,名称会映射成 helloService 
                    // helloService -> cn.enn.service.impl.HelloServiceImpl
                    beans.put(key, obj);
                } else {
                    // 可扩展 component 等注解
                    continue;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } 
        }
    }
    
    // 将name 第一个字编变小写
    private String toLowerFirstWord(String name) {
        char[] charArray = name.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }
    
    //基于MyAutowried注解,进行注入对象
    private void doIoc() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            //注入Controller对象中的Filed
            if (clazz.isAnnotationPresent(MyController.class)) {
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(MyAutowried.class)) { //处理MyAutowried注解
                        MyAutowried autowried = field.getAnnotation(MyAutowried.class);
                        String key = autowried.value();
                        if (key.equals("")) { 
                            key = field.getName();  //如果没有设置key的名字,则默认是属性名
                        }
                        try {
                            field.setAccessible(true);
                            field.set(instance, beans.get(key));  //设置值
                        } catch (Exception e) {
                            e.printStackTrace();
                        } 
                    } else {
                        //你可以自己扩展@resource注解
                        continue;
                    }
                }
            }else {
                //可自己扩容Service的属性注入
            }
        }
    }

    //方法映射
    // /hello/index --> HelloController.index()
    private void handlerMapping() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            //只解析加了Controller的注解类
            if (clazz.isAnnotationPresent(MyRequestMapper.class)) { 
                MyRequestMapper clazzRm = clazz.getAnnotation(MyRequestMapper.class);
                String clazzpath = clazzRm.value(); //这个是在Controller类上加的 RequestMapper

                // 获取其方法内 RequestMapper值
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(MyRequestMapper.class)) { 
                        MyRequestMapper methodRm = method.getAnnotation(MyRequestMapper.class);
                        String methodpath = methodRm.value();
                        // /hello/index-->HelloController.index()
                        handlerMap.put(clazzpath + methodpath, method);
                    }
                } 
             }else {
                 //自己扩容RestController注解
             }
        }
    }
    
    //处理请求
    @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);
    }

    //处理请求
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        // /springmvc/hello/index
        String uri = req.getRequestURI();
        // /springmvc
        String content = req.getContextPath();
        // /hello/index
        String path = uri.replaceAll(content, "");
        try { 
            Method method = (Method) handlerMap.get(path); //真实调用的方法
            Object bean = beans.get("/" + path.split("/")[1]); //获取到对应的Controller对象
            Object[] args = handle(req, resp, bean, method);//请求传递的参数
            method.invoke(bean, args); //调用方法
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 获取 http传过来的参数
    private Object[] handle(HttpServletRequest req, HttpServletResponse resp, Object bean, Method method) {
        // 获取方法的参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        // 保存参数值
        Object paramValues[] = new Object[parameterTypes.length];
         
        // 方法的参数列表
        for (int i = 0; i < parameterTypes.length; i++) {
            String requestParam = parameterTypes[i].getSimpleName();
            if (requestParam.equals("HttpServletRequest")) {
                paramValues[i] = req;
                continue;
            }
            if (requestParam.equals("HttpServletResponse")) {
                paramValues[i] = resp;
                continue;
            }
            if (requestParam.equals("String")) {
                paramValues[i] = argumentResolver(req, method, i);
            }
        }
        return paramValues;
    }

    //获取参数的值
    private Object argumentResolver(HttpServletRequest req, Method method, int paramIndex) {
        Annotation[][] annotations = method.getParameterAnnotations();
        Annotation[] paraAns = annotations[paramIndex];
        for (Annotation paramAn : paraAns) {
            //只过滤自定义的MyRequestParam注解
            if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                MyRequestParam ep = (MyRequestParam) paramAn;
                String value = ep.value();
                return req.getParameter(value);
            }
        }
        return null;
    }

}

搭建web项目测试

HelloService

public interface HelloService {
    public String hello(String name);
}

HelloServiceImpl

@MyService
public class HelloServiceImpl implements HelloService {
    public String hello(String name) {
        return "hello: " + name;
    }
}

HelloController

@MyController 
@MyRequestMapper("/hello") 
public class HelloController {

    @MyAutowried 
    private HelloService helloService;

    @MyRequestMapper("/index") 
    public void index(HttpServletResponse response, @MyRequestParam("name") String name) throws IOException {
        String result = helloService.hello(name);
        PrintWriter writer = response.getWriter();
        writer.println(result);
    }

}

web.xml

<?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"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.dispatcher.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  
</web-app>

测试访问

 到此,一个简单的SpringMVC模拟完成了,其实我们从SpringMVC的源码中可以看到,我们的请求会被DispatcherServlet去拦截

spring源码地址: https://github.com/spring-projects/spring-framework

我们在使用SpringMVC时,会在web.xml中配置如下内容:

    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

猜你喜欢

转载自www.cnblogs.com/weishao-lsv/p/12940602.html