• 手写springmvc(基础功能)


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

    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 + "
    age" + 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效果。 

    得到以下结构,则为成功

  • 相关阅读:
    vue路由懒加载
    Git文档
    Redis启动多个实例,并以windows服务方式运行
    windwos service安装命令
    sqlserver随机查询
    Redis 主从配置
    Unity3D 学习资料
    MAC常用终端命令
    服务器证书安装配置指南(IIS7)
    sql Exists与in 的区别
  • 原文地址:https://www.cnblogs.com/laolei11/p/11128569.html
Copyright © 2020-2023  润新知