手写Mini版SpringMVC

一.SpringMVC前期准备

1.IOC基本执行原理(流程):ioc就是一个MAP(key:beanName,value:Object(反射的实例))

a.先配置包扫描路径(例:cn.com.bardream,demo->扫描demo包下所有.class类)

b.识别出被标记上的@MYController@MYService注解的类

c.通过反射实例化被标记的类后,加载到ioc容器中(就是一个map(beanName,Object(反射的实例)))

d.beanName;有两种情况,一种注解中设置了别名,就以别名为beanName,另一种没有设置别名,那就以类名首字母小写为beanName

2.DI基本执行原理(流程):通过ioc给被标记的@MYAutoWrite的属性赋值

a.遍历ioc容器中的Object,看类中字段属性上是否标记上@MYAutoWrite的注解

b.再根据类中的字段属性的类型,去ioc容器中,找到对应的实例,然后赋值给当前被标记的类

3.MVC基本执行原理(流程):通过handlerMapping,做URI与Method的映射

a.扫描被@RequestMapping标记的类,以及方法,获取注解中所定义的路径

b.用一个handlerMapping去存储,url和method的对应关系(map<key:uri,value:method(反射包中的Method类)>)

c.当请求进入时,通过请求uri 对比 handlerMapping中的key:uri,去获取,需要调用的方法

d.在组装参数,采用,method.invoke(Object,params),通过反射去调用,请求对应的方法

二.开写

1.照抄并简化@Controller @Service... 等

//@Target(ElementType.TYPE)//接口、类、枚举、注解
//@Target(ElementType.FIELD)//字段、枚举的常量
//@Target(ElementType.METHOD)//方法
//@Target(ElementType.PARAMETER)//方法参数
//@Target(ElementType.CONSTRUCTOR)//构造函数
//@Target(ElementType.LOCAL_VARIABLE)//局部变量
//@Target(ElementType.ANNOTATION_TYPE)//注解
//@Target(ElementType.PACKAGE)//包
@Target({ElementType.TYPE})
//@Retention(RetentionPolicy.SOURCE)//注解仅存在于源码中,在class字节码文件中不包含
//@Retention(RetentionPolicy.CLASS)//默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
//@Retention(RetentionPolicy.RUNTIME)//注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
//注解包含在javadoc中:
@Documented
public @interface MYController {
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYService {
    String value() default "";
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYAutowired {
    String value() default "";
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYRequestMapping {
    String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYRequestParam {
    String value() default "";
}

2.建造controller层,service层

package cn.com.bardream.demo.controller;

import cn.com.bardream.demo.service.IDemoService;
import cn.com.bardream.myframework.annotation.MYAutowired;
import cn.com.bardream.myframework.annotation.MYController;
import cn.com.bardream.myframework.annotation.MYRequestMapping;
import cn.com.bardream.myframework.annotation.MYRequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@MYController
@MYRequestMapping("/demo")
public class DemoController {

    @MYAutowired
    private IDemoService demoService;

    @MYRequestMapping("/query")
    public void query(HttpServletRequest req, HttpServletResponse resp,
                      @MYRequestParam("name") String name) {
        String result = "My name is " + name;
        try {
            resp.getWriter().write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @MYRequestMapping("/add")
    public void add(HttpServletRequest req, HttpServletResponse resp,
                    @MYRequestParam("a") Integer a, @MYRequestParam("b") Integer b) {
        try {
            resp.getWriter().write(a + "+" + b + "=" + (a + b));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @MYRequestMapping("/sub")
    public void add(HttpServletRequest req, HttpServletResponse resp,
                    @MYRequestParam("a") Double a, @MYRequestParam("b") Double b) {
        try {
            resp.getWriter().write(a + "-" + b + "=" + (a - b));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @MYRequestMapping("/remove")
    public String remove(@MYRequestParam("id") Integer id) {
        return "" + id;
    }

}
package cn.com.bardream.demo.service;


public interface IDemoService {
	
	String get(String name);
	
}
package cn.com.bardream.demo.service.impl;

import cn.com.bardream.demo.service.IDemoService;
import cn.com.bardream.myframework.annotation.MYService;

@MYService
public class DemoService implements IDemoService {

	public String get(String name) {
		return "My name is " + name;
	}

}

3.准备配置文件:application.properties

scanPackage=cn.com.bardream.demo

4.配置web.xml文件

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>mymvc</servlet-name>
    <servlet-class>cn.com.bardream.myframework.servlet.v2.MYDispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>application.properties</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mymvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

5.新建一MYDispatcherServlet继承HttpServlet,实现init() doGet() doPost() 方法

package cn.com.bardream.myframework.servlet.v2;

import cn.com.bardream.myframework.annotation.MYAutowired;
import cn.com.bardream.myframework.annotation.MYController;
import cn.com.bardream.myframework.annotation.MYRequestMapping;
import cn.com.bardream.myframework.annotation.MYRequestParam;
import cn.com.bardream.myframework.annotation.MYService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
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.io.InputStream;
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.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class MYDispatcherServlet extends HttpServlet {

    // 存储配置文件信息
    Properties configContext = new Properties();

    // 扫描到的类名存储容器
    List<String> classNameContext = new ArrayList<String>();

    // IOC容器 - key:beanName  value:Object
    Map<String, Object> ioc = new HashMap<String, Object>();

    // 采用map存储 HandlerMapping key:uri value:Method
    Map<String, Method> handlerMapping = new HashMap<String, Method>();

    // 先进行初始化
    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、加载配置文件 -> 到configContext
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2、扫描相关的类->从configContext中获取需要扫描的包路径->classNameContext容器中
        doScanner(configContext.getProperty("scanPackage"));
        //3、初始化所有相关的类的实例,并且放入到IOC容器之中
        doInitIOC();
        //4、完成依赖注入
        doAutoWrite();
        //5、初始化HandlerMapping
        doInitHandlerMapping();

        System.out.println("MYframework is init.");
    }

    // 初始化HandlerMapping
    private void doInitHandlerMapping() {
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            if (!entry.getValue().getClass().isAnnotationPresent(MYController.class)) {
                continue;
            }
            MYRequestMapping annotation = entry.getValue().getClass().getAnnotation(MYRequestMapping.class);
            String baseUri = annotation.value();
            for (Method method : entry.getValue().getClass().getMethods()) {
                if (!method.isAnnotationPresent(MYRequestMapping.class)) {
                    continue;
                }
                MYRequestMapping anMapping = method.getAnnotation(MYRequestMapping.class);
                String methodUri = anMapping.value();
                String url = (baseUri + "/" + methodUri).replaceAll("/+", "/");
                handlerMapping.put(url, method);
            }

        }
    }

    // 将定义@AutoWrite的字段,赋值
    private void doAutoWrite() {
        try {
            for (Map.Entry<String, Object> entry : ioc.entrySet()) {
                Field[] fields = entry.getValue().getClass().getDeclaredFields();
                for (Field field : fields) {
                    if (!field.isAnnotationPresent(MYAutowired.class)) {
                        continue;
                    }
                    String beanName = field.getAnnotation(MYAutowired.class).value();
                    if ("".equals(beanName)) {
                        Class<?> type = field.getType();
                        beanName = type.getName();
                    }
                    if (!ioc.containsKey(beanName)) {
                        throw new Exception("the autowrite " + beanName + " is not found!");
                    }
                    Object object = ioc.get(beanName);
                    // 设置私有属性赋值,权限
                    field.setAccessible(true);
                    // 这就是自动注入最后的结果
                    field.set(entry.getValue(), object);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 从类名容器中获取全类名
    // 将进行@MYController @MYService 标记的标记类,通过反射实例化后,存入到ioc中
    private void doInitIOC() {
        try {
            for (String className : classNameContext) {
                Class<?> aClass = Class.forName(className);
                // 判断类上加了@MYController 注解的
                if (aClass.isAnnotationPresent(MYController.class)) {
                    String beanName = toLowFirstName(aClass.getSimpleName());
                    Object obj = aClass.newInstance();
                    ioc.put(beanName, obj);
                    // 判断类上加了@MYService 注解的
                } else if (aClass.isAnnotationPresent(MYService.class)) {
                    String beanName = toLowFirstName(aClass.getSimpleName());
                    MYService annotation = aClass.getAnnotation(MYService.class);
                    if (annotation.value() != "") {
                        beanName = annotation.value();
                    }
                    Object obj = aClass.newInstance();
                    ioc.put(beanName, obj);

                    // 将Service所实现的接口的全类名,也加载进IOC容器中
                    // 这样后面我们使用@MYAotuwrite是,就可以根据接口类型,去获取其实现类的实例
                    for (Class clazz : aClass.getInterfaces()) {
                        if (ioc.containsKey(clazz.getName())) {
                            throw new Exception("the class " + clazz.getName() + " is exist!");
                        }
                        ioc.put(clazz.getName(), obj);
                    }

                } else {
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 将类名首字母转成小写
    private String toLowFirstName(String simpleName) {
        char[] chars = simpleName.toCharArray();
        if ('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

    // 获取配置文件中的需要扫描的包路径
    // 将包路径下所有的类名扫描到类名容器中
    private void doScanner(String scanPackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File filePath = new File(url.getFile());
        File[] files = filePath.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                if (!file.getName().endsWith(".class")) {
                    continue;
                }
                // 全类名
                String className = scanPackage + "." + file.getName().replace(".class", "");
                classNameContext.add(className);
            }
        }
    }

    // 去加载配置文件
    private void doLoadConfig(String contextConfigLocation) {

        InputStream inputStream = null;
        try {
            inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
            configContext.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatcher(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatcher(req, resp);
    }

    // 通过Dispatcher进行请求转发以及匹配
    private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) {
        try {
            String requestURI = req.getRequestURI();
            if (!handlerMapping.containsKey(requestURI)) {
                resp.getWriter().write("404 not found!");
                return;
            }
            Method method = handlerMapping.get(requestURI);
            // 请求参数列表  key:paramName value:paramValue
            Map<String, String[]> parameterMap = req.getParameterMap();
            // 形参列表
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 新建需要传入方法中的参数
            Object[] objs = new Object[parameterTypes.length];
            // 获取方法上的注解--[]参数位置,[]参数上注解数组
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];
                if (parameterType == HttpServletRequest.class) {
                    objs[i] = req;
                } else if (parameterType == HttpServletResponse.class) {
                    objs[i] = resp;
                } else {
                    // 获取参数上的注解数组
                    Annotation[] parameterAnnotation = parameterAnnotations[i];
                    for (Annotation annotation : parameterAnnotation) {
                        if (!(annotation instanceof MYRequestParam)) {
                            continue;
                        }
                        MYRequestParam myRequestParam = (MYRequestParam) annotation;
                        // myRequestParam.value()不想做非空判断了,默认有值了
                        String[] strings = parameterMap.get(myRequestParam.value());
                        objs[i] = conver(parameterType,strings);
                    }
                }
            }
            Object o = ioc.get(toLowFirstName(method.getDeclaringClass().getSimpleName()));
            Object result = method.invoke(o, objs);
            if (result==null||result==Void.class){return;}
            resp.getWriter().write(result.toString());
            return;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Object conver(Class<?> parameterType, String[] strings) {
        if (parameterType==String.class){
            return Arrays.toString(strings).replaceAll("\\[|\\]","").trim().replaceAll("\\s[2]",",");
        }
        if (parameterType==Integer.class){
            return Integer.valueOf(strings[0]);
        }
        if (parameterType==Double.class){
            return Double.valueOf(strings[0]);
        }
        return null;
    }

}

6.测试结果

三.总结-三大阶段-十四步

配置阶段配置web.xmlDispatchServlet
设定init-paramcontextConfigLocation=classpath:application.xml
设定url-pattern/*
配置annotation@Controller@Service@Autowrited@RequesetParam@RequestMapping
初始化阶段调用init()方法加载配置文件
IOC容器初始化Map<String,Object> 
扫描相关类scan-package = "cn.com.bardream.demo"
创建实例化并保存至容器通过反射机制将类实例化放入IOC容器中
进行DI操作扫描IOC容器中的实例,给加了自动装载的属性赋值
初始化HandlerMapping将一个URL和一个Method进行一对一的关联映射Map<String,Method>
运行阶段调用doGet()/doPost()Web容器调用doGet()/doPost()方法.获得request/respond对象
匹配HandlerMapping从request对象中获取用户输入的url,找到对应的Method
反射调用Method.invoke()利用反射调用方法并返回结果
respond.gerWrite().write()将返回结果输出到浏览器

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值