a5.手写实现简单版springMVC

思路

在这里插入图片描述

说明

* git地址: 文章目录中置顶的git文章中;
* springMVC简易版,  只是为了通过简单实现更加了解Spring体系, 功能不完备, 存在细节bug.

配置阶段

* 创建DispatcherServlet类:
	* 所有请求都会访问该serlvet;
	* 继承HttpServlet;

* 配置DispatcherServlet:
	* web.xml中配置;
	* 拦截所有请求;
	* 添加变量: 配置文件地址;
	* 随容器初始化;

* 配置文件:
	* 配置bean目录;
	* 初始化IOC容器时, 使用该目录获取所有bean;

* 编写annotation:
	* @Controller, @Service, @Autowired, @RequestMapping, @RequestParams;

初始化阶段

  • 读取bean目录
* 装载配置文件;
* 获取需要初始化bean的目录;
  • 创建容器
* class容器:
	* Map格式;
	* key = class.getName;  value = null;
	* 这里应该使用list, 由开始设计时没考虑周全, 所以这里我使用了map
* url容器
	* Map格式;
	* key: request请求的url;
	* value: 对应执行的Method;
* bean容器
	* key: 接口路径 / class路径 / @Service自定义的名称
	* value: Object
  • 初始化class容器
* 遍历bean目录;
* 装载目录中所有java类全名;
  • 初始化Bean容器
* 遍历class容器
* 实例化所有@Controller和@Service注解的类:
* 装载:
	* 装载: 类名-Object
	* 如果该类实现了接口,  另外装载: 接口名-Object;
	* 如果该类使用了@Service注解并存在注解值, 另外装载: 注解值-Object
  • 初始化url容器
* 遍历bean容器;
* 当类为@Controller注解修时:
	* 获取所有方法;
	* 当方法被@RequestMapping修饰时, 添加到url容器中;
	* key: @Contruller中@RequestMapping + 方法上@RequestMapping;
	* value: Method
  • 装配bean
* 当bean中有@Autowired修饰的变量时:
	* 获取@Autowired的值A;
	* 从bean容器中, 通过值A获取到对象;
	* 为bean中变量赋值;

运行阶段

  • 重写doPost方法
* 获取request请求的url;
* 从url容器中,获取到对应Method;
* 为Method添加参数:
	* 从request参数中, 找到@RequestParam修饰的对应参数;
* 执行Method;
	* Object : bean容器中的Object;
	* Method : url容器中的mehtod;

代码结构:

在这里插入图片描述

配置阶段

创建Dispatcher类型

public class BZDispatcherServlet extends HttpServlet {

}

配置DispatcherServlet

* web.xml

  <servlet>
    <servlet-name>BZDispatcherServlet</servlet-name>
    <servlet-class>mvcFramework.dispatcherServlet.BZDispatcherServlet</servlet-class>
    <init-param>
      <!-- 配置文件地址 -->
      <param-name>applicationContext</param-name>
      <param-value>application.properties</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>BZDispatcherServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

配置bean目录

* 在properties中, 配置bean目录;

scanPackage=mvc

编写annotation

  • @Controller
import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZController {
    String value() default "";
}
  • @Service
import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZService {
    String value() default "";
}
  • @Autowired
import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZAutowired {
    String value() default "";
}
  • @RequestMapping
import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BZRequestMapping {
    String value() default "";
}
  • @RequestParam
import java.lang.annotation.*;

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

初始化阶段

覆盖init()方法


  • 定义容器
    Map<String, Object> BeanMap = new HashMap();
    Map<String, Object> URLMap = new HashMap();
    Map<String, Object> classMap = new HashMap();
  • 重写init()方法;
    @Override
    public void init(ServletConfig config) {
        System.out.println("bz dispatcher start!");

        try {
            //获取bean目录;
            String scanPackage = getScanPackage(config);
            System.out.println("获取到bean目录: " + scanPackage);

            //装配IOC容器 及 url 映射
            initIOC(scanPackage);

            //装配bean
            fitBean();

            System.out.println("bz dispatcher init finish!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

获取bean目录

    private String getScanPackage(ServletConfig config) throws Exception {
        Properties properties = new Properties();
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("applicationContext"));
        if (is == null) {
            throw new RuntimeException("Bean 目录不存在!");
        }
        properties.load(is);
        String scanPackage = properties.getProperty("scanPackage");
        if (scanPackage == null || scanPackage.equals("")) {
            throw new RuntimeException("scanPackage 配置信息不存在!");
        }
        return scanPackage;
    }

装配IOC容器

    private void initIOC(String scanPackage) throws Exception 	  {
        //初始化classMap
        initClassMap(scanPackage);

        //初始化IOC容器
        initIOCMap();
    }
  • 初始化classMap容器
    private void initClassMap(String scanPackage) {
        //获取目录
        scanPackage = scanPackage.replaceAll("\\.", "/");
        URL url = this.getClass().getClassLoader().getResource(scanPackage);
        if (url == null) {
            System.out.println("bean 目录中无文件!");
            return;
        }
        File rootFile = new File(url.getFile());
        for (File file : rootFile.listFiles()) {
            if (file.isDirectory()) { //递归目录
                initClassMap(scanPackage + "/" +file.getName());
            }
            if (!file.getName().endsWith(".class")) {
                continue;
            }

            String className = scanPackage + "/" +file.getName().replace(".class", "");
            classMap.put(className.replaceAll("/","."), null);
        }
    }
  • 初始化IOC容器
    private void initIOCMap() throws Exception {
        for (String className : classMap.keySet()) {

            Class<?> clazz = Class.forName(className);
            if (clazz.isAnnotationPresent(BZController.class)) {
                // 添加url映射
                initURLMap(clazz);

                //把controller添加到bean
                Constructor constructor = clazz.getDeclaredConstructor();
                constructor.setAccessible(true);
                BeanMap.put(clazz.getName(),constructor.newInstance());
            } else if (clazz.isAnnotationPresent(BZService.class)) {
                initBeanMap(clazz);
            }
        }
    }
	
    private void initURLMap(Class<?> clazz) {
        String controllerURL = "";

        if (clazz.isAnnotationPresent(BZRequestMapping.class)) {
            controllerURL = clazz.getAnnotation(BZRequestMapping.class).value();
        }


        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if (!method.isAnnotationPresent(BZRequestMapping.class)) {
                continue;
            }
            BZRequestMapping bzRequestMapping = method.getAnnotation(BZRequestMapping.class);
            String url = controllerURL + "/" +bzRequestMapping.value();
            url = url.replaceAll("/+", "/");
            if(URLMap.containsKey(url)){
                throw new RuntimeException("URLMap重复!!" +  url);
            }
            URLMap.put(url, method);
        }
    }	

    private void initBeanMap(Class<?> clazz) throws Exception {

        BZService bzService = clazz.getAnnotation(BZService.class);
        String value = bzService.value();

        //创建对象
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object obj = constructor.newInstance();

        if (value != null && !value.equals("")) {
            if(BeanMap.containsKey(value)){
                throw new RuntimeException("BeanMap重复! " + value);
            }
            BeanMap.put(value, obj);
        }
        BeanMap.put(clazz.getName(), obj);

        //获取接口
        Class<?>[] clazzs = clazz.getInterfaces();
        //BeanMap.put(initClassNameToBeanName(clazz.getName()), obj);
        if (clazzs.length == 1) {
            if(BeanMap.containsKey(clazzs[0].getName())){
                throw new RuntimeException("BeanMap重复! " + clazzs[0].getName());
            }
            BeanMap.put(clazzs[0].getName(), obj);
        } else if (clazzs.length > 1) {
            throw new RuntimeException("Service 最多继承一个接口!");
        }
    }

    private String initClassNameToBeanName(String name) {
        String  [] str = name.split("\\.");
        return str[str.length-1];

    }

装配Bean

    private void fitBean() throws Exception {
        for (String clazzName : classMap.keySet()) {
            Class clazz = Class.forName(clazzName);
            Field[] fields = clazz.getDeclaredFields();


            for (Field field : fields) {
                if (!field.isAnnotationPresent(BZAutowired.class)) {
                    continue;
                }

                Constructor targetCon = clazz.getDeclaredConstructor();
                targetCon.setAccessible(true);
                Object targetObject = targetCon.newInstance();

                BZAutowired bzAutowired = field.getAnnotation(BZAutowired.class);
                String value = bzAutowired.value();
                field.setAccessible(true);

                if (value != null && !value.equals("")) {
                    Object obj = BeanMap.get(value);
                    if(obj == null){
                        throw new RuntimeException("Bean 不存在: " + value);
                    }
                    field.set(targetObject, obj);
                } else {
                    Type type = field.getGenericType();
                    Object obj = BeanMap.get(type.getTypeName());
                    if(obj == null){
                        throw new RuntimeException("Bean 不存在" + type.getTypeName());
                    }
                    field.set(targetObject, obj);
                }
            }

        }
    }

重写doPost方法

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

        try {
            doDispatcher(req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //获取url
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        if(!URLMap.containsKey(url)){
            resp.getWriter().write("404 Not Found!!");return;
        }


        Method method = (Method) URLMap.get(url);
        Map<String,String[]> params = req.getParameterMap();



        //获取方法的形参列表
        Class<?> [] parameterTypes = method.getParameterTypes();
        Object [] paramValues = new Object[parameterTypes.length];
        Parameter[] methodParams = method.getParameters();

        for (int i = 0; i < parameterTypes.length; i ++) {
            Class parameterType = parameterTypes[i];
            //不能用instanceof,parameterType它不是实参,而是形参
            if(parameterType == HttpServletRequest.class){
                paramValues[i] = req;
                continue;
            }else if(parameterType == HttpServletResponse.class){
                paramValues[i] = resp;
                continue;
            }else if(parameterType == String.class){
                BZRequestParam bzRequestParam = methodParams[i].getAnnotation(BZRequestParam.class);
                if(params.containsKey(bzRequestParam.value())){
                    for (Map.Entry<String,String[]> param : params.entrySet()){
                        if(!param.getKey().equals(bzRequestParam.value())){
                            continue;
                        }
                        String value = Arrays.toString(param.getValue())
                                .replaceAll("\\[|\\]","")
                                .replaceAll("\\s",",");
                        paramValues[i] = value;
                    }
                }
            }
        }

        method.invoke(BeanMap.get(method.getDeclaringClass().getName()),paramValues);
    }

编写service和controller

congroller

@BZController
@BZRequestMapping("/buzzered")
public class DemoController {

    @BZAutowired
    DemoService1 ds1;

    @BZAutowired
    IService is;

    @BZRequestMapping("mvcDemo")
    public void demo(HttpServletRequest req, HttpServletResponse resp, @BZRequestParam("name")String name, @BZRequestParam("age")String age) throws Exception {
        resp.getWriter().write("My name is " + name  + " age: " + age);
    }

}

service

  • 接口形式注入
public interface IService {

    public void demo();
}

@BZService
public class IServiceImpl implements IService {
    @Override
    public void demo() {
        System.out.println("demo demo demo");
    }
}

  • 自定义名称形式注入
@BZService("DemoService")
public class DemoService {

    public void demo(){
        System.out.println("demo demo demo");
    }
}

  • 类名形式注入
@BZService
public class DemoService1 {

    @BZAutowired("DemoService")
    DemoService ds;

    public void demo(){
        ds.demo();
    }
}

部署并访问

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_28682977/article/details/88930502