一直以来,逃避学习。不喜欢看源代码,不学新技术。呆在一个公司无所事事。面临找工作时,心里很慌,于是从新开始学习源码。从零开始直接开始撸代码。

1、建立一个Maven项目,项目结构如下图所示。

 

pom.xml 导入 servlet jar包

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>

 

 

2、建立一些springmvc常用注解RequestMapper 、Controller、Server、Autowried、RequestMapper等等注解。我们自己手写可以使用自己独特的注解,区分原版的。我使用的是视频老师定义的注解。

EnjoyController

EnjoyServer

EnjoyRequestMapper

EnjoyAutowried

EnjoyRequestParam

 

注解@EnjoyController

@Target(ElementType.TYPE) //作用域  类
@Retention(RetentionPolicy.RUNTIME)  // 保留至运行时
@Documented
public @interface EnjoyController {
    String value() default "";
}

注解@EnjoyServer

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

@EnjoyRequestMapper

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

@EnjoyAutowried

@Target(ElementType.FIELD) //作用域 成员变量
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnjoyAutowried {
    String value() default "";
}

@EnjoyRequestParam

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

 

3、创建Controller类和Service类 (当然你也可以扩展Dao类) 暂时模拟二个类  模拟正常的springmvc   

HelloController  controller层

package com.enjoy.controller;

import java.io.IOException;
import java.io.PrintWriter;

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

import com.enjoy.annation.EnjoyAutowried;
import com.enjoy.annation.EnjoyController;
import com.enjoy.annation.EnjoyRequestMapper;
import com.enjoy.annation.EnjoyRequestParam;
import com.enjoy.server.HelloServer;

@EnjoyRequestMapper("/hello")
@EnjoyController
public class HelloController {

    @EnjoyAutowried
    HelloServer helloServer;

    @EnjoyRequestMapper("/query")
    public void query(HttpServletRequest request, HttpServletResponse response, @EnjoyRequestParam("name") String name,
            @EnjoyRequestParam("age") String age) throws IOException {
        String result = helloServer.query(name, age);

        // ctrl +2
        PrintWriter writer = response.getWriter();
        writer.println(result);
    }

}

 

HelloServer  server业务层

package com.enjoy.server;

public interface HelloServer {

    public String query(String name, String age);
}

HelloServerImpl

package com.enjoy.server.impl;

import com.enjoy.annation.EnjoyServer;
import com.enjoy.server.HelloServer;

@EnjoyServer
public class HelloServerImpl implements HelloServer {

    public String query(String name, String age) {
        return "name:" + name + "\r\nage" + age;
    }

}

4、创建DispatcherServlet类,并通过web.xml加载

本质上DIspatcherServlet就是一个Servlet,我们通过web.xml让其容器一启动就开始加载。

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>com.enjoy.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具有其最核心的几个功能

1、把用Controller、Server修饰的类 统一用容器管理。容器一般用HashMap储存。

2、依赖注入

3、控制纷发

对应Dispatcher也应做到以上几点。

1、扫描包路径 doScanPackage

2、进行容器管理 doInstance

3、依赖注入 doIoc

4、控制纷发 handlerMapping

具体代码

package com.enjoy.dispatcher;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
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 javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.enjoy.annation.EnjoyAutowried;
import com.enjoy.annation.EnjoyController;
import com.enjoy.annation.EnjoyRequestMapper;
import com.enjoy.annation.EnjoyRequestParam;
import com.enjoy.annation.EnjoyServer;

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

    private List<String> classNames = new ArrayList<String>();

    // 存在当前加载的所有的类
    private Map<String, Object> beans = new HashMap<String, Object>();

    // 存放所有请求 Object->method.invoke()调用方法
    private Map<String, Object> handlerMap = new HashMap<String, Object>();

    @Override
    public void init() throws ServletException {
        // 1.扫描需要的实例化的类
        doScanPackage("com.enjoy");
        // 2.实例化
        doInstance();
        if (!beanEmpty()) {
            // 3.将IOC容器中的service对象设置给controller层定义的field上
            doIoc();
            // 4.建立path与method的映射关系
            handlerMapping();
        }
    }

    private boolean beanEmpty() {
        if (beans.isEmpty()) {
            System.out.println("bean is empty......");
            return true;
        }
        return false;
    }

    // 获取EnjoyRequestMapper hello/query
    private void handlerMapping() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            // 获取实例
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            // 获取注解当前为EnjoyRequestMapper的类
            if (clazz.isAnnotationPresent(EnjoyRequestMapper.class)) {
                EnjoyRequestMapper clazzRm = clazz.getAnnotation(EnjoyRequestMapper.class);
                String clazzpath = clazzRm.value(); // 第一个 RequestMapper值 /hello

                // 获取其方法内 RequestMapper第二的值
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    //// 判断方法上注解为RequestMappin
                    if (method.isAnnotationPresent(EnjoyRequestMapper.class)) {
                        EnjoyRequestMapper methodRm = method.getAnnotation(EnjoyRequestMapper.class);
                        String methodpath = methodRm.value();
                        // key: /hello/query value:method
                        handlerMap.put(clazzpath + methodpath, method);
                    }

                }

            }
        }
    }

    private void doIoc() {
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            // 获取实例
            Object instance = entry.getValue();
            Class<?> clazz = instance.getClass();
            // 我们的代码只有HelloController有依赖注入 故只考虑注解EnjoyController
            if (clazz.isAnnotationPresent(EnjoyController.class)) {
                // 获取全部的成员变量
                Field[] fields = clazz.getDeclaredFields();

                for (Field field : fields) {
                    // 如果当前的成员变量使用注解EnjoyAutowried 自动注入 则给其赋值
                    if (field.isAnnotationPresent(EnjoyAutowried.class)) {
                        // 获取自动注入的值 当然为空
                        EnjoyAutowried autowried = field.getAnnotation(EnjoyAutowried.class);
                        String key = autowried.value();
                        if (key.equals("")) {
                            // key: helloServer 自动注入 别写错 要根据key去容器取对象
                            key = field.getName();
                        }
                        try {
                            // 给filed赋值
                            field.setAccessible(true);
                            field.set(instance, beans.get(key));
                        } catch (IllegalArgumentException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    } else {
                        continue;
                    }
                }
            }
        }
    }

    // 加载扫描类
    private void doInstance() {
        for (String className : classNames) {
            try {
                // 获取反射
                Class<?> clazz = Class.forName(className);

                // 判断当前类是否注解EnjoyController类
                if (clazz.isAnnotationPresent(EnjoyController.class)) {
                    // 反射 生成对象
                    Object obj = clazz.newInstance();
                    // 通过EnjoyRequestMapper获取值,作为beans的key
                    EnjoyRequestMapper requestMapping = clazz.getAnnotation(EnjoyRequestMapper.class);
                    String key = requestMapping.value();
                    // key:/hello value:对象
                    beans.put(key, obj);
                } else if (clazz.isAnnotationPresent(EnjoyServer.class)) {
                    Object obj = clazz.newInstance(); // value 反射 生成类对象
                    // 我们代码 key为空
                    EnjoyServer annotation = clazz.getAnnotation(EnjoyServer.class);
                    String key = annotation.value();
                    if (key.equals("")) {
                        // 约定 key取Server父类名,第一个字母小写
                        Class<?>[] interfaces = clazz.getInterfaces();
                        if (interfaces.length > 0) {
                            // 父类存在
                            key = toLowerFirstWord(interfaces[0].getSimpleName());
                        } else {
                            // 父类不存在
                            key = toLowerFirstWord(className.substring(className.lastIndexOf(".") + 1)); // HelloController
                        }
                    }
                    // key:helloServer value:helloServerImpl对象
                    beans.put(key, obj);
                } else {
                    // 可扩展
                    continue;
                }
            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

    }

    private void doScanPackage(String basePackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
        /**
         * 第一次fileStr:E:/apache-tomcat-7.0.94/webapps/enjoyMvc/WEB-INF/classes/com/enjoy
         */
        String fileStr = url.getFile();
        File file = new File(url.getFile());
        /**
         * 第一次 listFiles:{annation,controller,dispatcher,server}
         */
        String[] listFiles = file.list();
        for (String path : listFiles) {
            File filepath = new File(fileStr + path);
            if (filepath.isDirectory()) {
                // 遍历
                doScanPackage(basePackage + "." + path);
            } else {
                /**
                 * 第一次 basePackage:com.enjoy.annation path:Autowried.class
                 */
                classNames.add(basePackage + "." + path.replace(".class", ""));
            }
        }
    }

    // 将name 第一个字编变小写
    private String toLowerFirstWord(String name) {
        char[] charArray = name.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }

    @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) {
        // /enjoyMvc/hello/query
        String uri = req.getRequestURI();
        // /enjoyMvc
        String content = req.getContextPath();
        // /hello/query
        String path = uri.replaceAll(content, "");
        try {
            // 根据 约定 去取对应调取的方法
            Method method = (Method) handlerMap.get(path);
            // 根据约定 去取对应 Bean对象
            Object bean = beans.get("/" + path.split("/")[1]);
            // 获取方法参数
            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<?>[] paramClazzs = method.getParameterTypes();
        // 保存参数值
        Object paramValues[] = new Object[paramClazzs.length];
        // 获取方法的参数列表
        Class<?>[] parameterTypes = method.getParameterTypes();

        // 方法的参数列表
        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();
        // 获取 自定义 EnjoyRequestParam 设置的值 如:name ,age
        Annotation[] paraAns = annotations[paramIndex];
        for (Annotation paramAn : paraAns) {
            if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) {
                EnjoyRequestParam ep = (EnjoyRequestParam) paramAn;
                String value = ep.value();
                return req.getParameter(value);
            }
        }
        return null;
    }

}

 

 一切就绪,开始启动Tomcat,在浏览器输入http://localhost:8080/enjoyMvc/hello/query?name=laolei&age=22。测试字写springmvc效果。 

得到以下结构,则为成功

 

posted on 2019-07-03 20:40  dalaolei  阅读(298)  评论(0编辑  收藏  举报