[02][01][02] 手写代码提炼Spring的核心原理

1. 看源码找不到入口,每次都晕车

  • 如何下手,从哪里开始看,不要一开始就去调试

1.1 mini版本Spring实现的基本思路

[外链图片转存失败(img-4bBLSphb-1566873582828)(evernotecid://DE37D598-33A4-4708-800A-6D4CF841DDE2/wwwevernotecom/149352153/ENResource/p286)]

2. 手写spring源码V1版本

2.1 配置阶段

  • 配置web.xml
  • 设定init-param
  • 设定url-pattern
  • 配置Annotation

2.1.1 配置web.xml,设定init-param,设定url-pattern

<?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/j2ee" xmlns:javaee="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/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">
	<display-name>My Web Application</display-name>
	<servlet>
		<!-- 拦截器名称 -->
		<servlet-name>mymvc</servlet-name>
		<!-- 拦截器对应的类路径 -->
		<servlet-class>com.zhunongyun.toalibaba.myspring1.mvcframework.v2.servlet.GPDispatcherServlet</servlet-class>
		<!-- init初始化参数 -->
		<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>

2.1.2 配置Annotation

package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;

import java.lang.annotation.*;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
    String value() default "";
}
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
    String value() default "";
}
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
    String value() default "";
}
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;

import java.lang.annotation.*;

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
    String value() default "";
}
package com.zhunongyun.toalibaba.myspring1.mvcframework.annotation;

import java.lang.annotation.*;

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

2.2 初始化阶段

  • 调用init()方法
  • IOC容器初始化
  • 扫描相关的类
  • 创建实例化并保存至容器
  • 进行DI操作
  • 初始化HandlerMapping

2.2.1 调用init()方法

@Override
public void init(ServletConfig config) throws ServletException {
    //模板模式
    //1、加载配置文件
    this.doLoadConfig(config.getInitParameter("contextConfigLocation"));
    //2、扫描相关的类
    this.doScanner(contextConfig.getProperty("scanPackage"));
    //3、初始化所有相关的类的实例,并且放入到IOC容器之中
    this.doInstance();
    //4、完成依赖注入
    this.doAutowired();
    //5、初始化HandlerMapping
    this.initHandlerMapping();

    System.out.println("My Spring framework is init.");
}

2.2.2 IOC容器初始化

/**
 * 加载配置文件
 *
 * @param contextConfigLocation
 */
private void doLoadConfig(String contextConfigLocation) {
    try (InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);) {
        //1、读取配置文件
        contextConfig.load(fis);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2.2.3 扫描相关的类

/**
 * 扫描相关的类
 * @param scanPackage
 */
private void doScanner(String scanPackage) {
    //包传过来包下面的所有的类全部扫描进来的
    URL url = this.getClass().getClassLoader()
            .getResource("/" + scanPackage.replaceAll("\\.", "/"));
    File classPath = new File(url.getFile());

    for (File file : classPath.listFiles()) {
        if (file.isDirectory()) {
            doScanner(scanPackage + "." + file.getName());
        } else {
            if (!file.getName().endsWith(".class")) {
                continue;
            }
            String className = (scanPackage + "." + file.getName()).replace(".class", "");
            classNames.add(className);
        }
    }
}

2.2.4 创建实例化并保存至容器

/**
 * 初始化所有相关的类的实例,并且放入到IOC容器之中
 */
private void doInstance() {
    //控制反转过程
    //工厂模式来实现的

    if (classNames.isEmpty()) {
        return;
    }

    try {
        for (String className : classNames) {
            Class<?> clazz = Class.forName(className);

            if (clazz.isAnnotationPresent(MyController.class)) {
                Object instance = clazz.newInstance();
                String beanName = toLowerFirstCase(clazz.getSimpleName());
                ioc.put(beanName, instance);
            } else if (clazz.isAnnotationPresent(MyService.class)) {
                //1、默认的类名首字母小写

                String beanName = toLowerFirstCase(clazz.getSimpleName());
                //2、自定义命名
                MyService service = clazz.getAnnotation(MyService.class);
                if (!"".equals(service.value())) {
                    beanName = service.value();
                }
                Object instance = clazz.newInstance();
                ioc.put(beanName, instance);

                //3、根据类型注入实现类,投机取巧的方式
                //clazz.getInterfaces()能获取当前类实现的接口类的类名
                for (Class<?> i : clazz.getInterfaces()) {
                    if (ioc.containsKey(i.getName())) {
                        throw new Exception("The beanName is exists!!");
                    }
                    ioc.put(i.getName(), instance);
                }
            } else {
                continue;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2.2.5 进行DI操作

/**
 * 完成依赖注入
 */
private void doAutowired() {
    if (ioc.isEmpty()) {
        return;
    }
    for (Map.Entry<String, Object> entry : ioc.entrySet()) {
        //拿到实例对象中的所有属性
        Field[] fields = entry.getValue().getClass().getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(MyAutowired.class)) {
                continue;
            }
            MyAutowired autowired = field.getAnnotation(MyAutowired.class);
            String beanName = autowired.value().trim();
            if ("".equals(beanName)) {
                beanName = field.getType().getName();
            }
            //不管你愿不愿意,强吻
            field.setAccessible(true); //设置私有属性的访问权限
            try {
                //执行注入动作
                field.set(entry.getValue(), ioc.get(beanName));
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }
}

2.2.6 初始化HandlerMapping

/**
 * 初始化HandlerMapping
 */
private void initHandlerMapping() {
    if (ioc.isEmpty()) {
        return;
    }

    for (Map.Entry<String, Object> entry : ioc.entrySet()) {
        Class<?> clazz = entry.getValue().getClass();
        if (!clazz.isAnnotationPresent(MyController.class)) {
            continue;
        }

        String baseUrl = "";
        //获取Controller的url配置
        if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
            MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
            baseUrl = requestMapping.value();
        }

        //获取Method的url配置
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {

            //没有加RequestMapping注解的直接忽略
            if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                continue;
            }

            //映射URL
            MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
            //  /demo/query
            //  (//demo//query)
            String url = ("/" + baseUrl + "/" + requestMapping.value())
                    .replaceAll("/+", "/");
            handlerMapping.put(url, method);
            System.out.println("Mapped " + url + "," + method);
        }
    }
}

2.3 运行阶段

  • 调用doPost()/doGet()方法
  • 匹配HandlerMapping
  • 反射调用method.invoker()
  • response.getWrite().write()

2.3.1 调用doPost()/doGet()方法,response.getWrite().write()

@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 {
    //派遣,分发任务
    try {
        //委派模式
        doDispatch(req, resp);
    } catch (Exception e) {
        e.printStackTrace();
        resp.getWriter().write("500 Excetion Detail:" + Arrays.toString(e.getStackTrace()));
    }
}

2.3.2 匹配HandlerMapping,反射调用method.invoker(),

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

    Method method = this.handlerMapping.get(url);
    //第一个参数:方法所在的实例
    //第二个参数:调用时所需要的实参
    //获取方法的形参列表
    Class<?>[] parameterTypes = method.getParameterTypes();
    //保存请求的url参数列表
    Map<String, String[]> parameterMap = req.getParameterMap();
    //保存赋值参数的位置
    Object[] paramValues = new Object[parameterTypes.length];

    Annotation[][] pa = method.getParameterAnnotations();

    //按根据参数位置动态赋值
    for (int i = 0; i < parameterTypes.length; i++) {
        Class parameterType = parameterTypes[i];
        if (parameterType == HttpServletRequest.class) {
            paramValues[i] = req;
            continue;
        } else if (parameterType == HttpServletResponse.class) {
            paramValues[i] = resp;
            continue;
        } else {
            //提取方法中加了注解的参数
            for (Annotation a : pa[i]) {
                if (a instanceof MyRequestParam) {
                    String paramName = ((MyRequestParam) a).value();
                    if (!"".equals(paramName.trim())) {
                        String value = Arrays.toString(parameterMap.get(paramName))
                                .replaceAll("\\[|\\]", "");
                        paramValues[i] = value;
                    }
                    break;
                }
            }
        }
    }
    //投机取巧的方式

    //通过反射拿到method所在class,拿到class之后还是拿到class的名称
    //再调用toLowerFirstCase获得beanName
    String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
    method.invoke(ioc.get(beanName), paramValues);
}

2.4 拦截器MyDispatcherServlet类

package com.zhunongyun.toalibaba.myspring1.mvcframework.v2.servlet;

import com.zhunongyun.toalibaba.myspring1.mvcframework.annotation.*;

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.*;

/**
 * 拦截器
 */
public class MyDispatcherServlet extends HttpServlet {

    //存储aplication.properties的配置内容
    private Properties contextConfig = new Properties();
    //存储所有扫描到的类
    private List<String> classNames = new ArrayList<String>();
    //IOC容器,保存所有实例化对象
    //注册式单例模式
    private Map<String, Object> ioc = new HashMap<String, Object>();
    //保存Contrller中所有Mapping的对应关系

    private Map<String, Method> handlerMapping = new HashMap<String, Method>();

    @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 {
        //派遣,分发任务
        try {
            //委派模式
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Excetion Detail:" + Arrays.toString(e.getStackTrace()));
        }
    }
    
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
        if (!this.handlerMapping.containsKey(url)) {
            resp.getWriter().write("404 Not Found!!");
            return;
        }

        Method method = this.handlerMapping.get(url);
        //第一个参数:方法所在的实例
        //第二个参数:调用时所需要的实参
        //获取方法的形参列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        //保存请求的url参数列表
        Map<String, String[]> parameterMap = req.getParameterMap();
        //保存赋值参数的位置
        Object[] paramValues = new Object[parameterTypes.length];

        Annotation[][] pa = method.getParameterAnnotations();

        //按根据参数位置动态赋值
        for (int i = 0; i < parameterTypes.length; i++) {
            Class parameterType = parameterTypes[i];
            if (parameterType == HttpServletRequest.class) {
                paramValues[i] = req;
                continue;
            } else if (parameterType == HttpServletResponse.class) {
                paramValues[i] = resp;
                continue;
            } else {
                //提取方法中加了注解的参数
                for (Annotation a : pa[i]) {
                    if (a instanceof MyRequestParam) {
                        String paramName = ((MyRequestParam) a).value();
                        if (!"".equals(paramName.trim())) {
                            String value = Arrays.toString(parameterMap.get(paramName))
                                    .replaceAll("\\[|\\]", "");
                            paramValues[i] = value;
                        }
                        break;
                    }
                }
            }
        }
        //投机取巧的方式

        //通过反射拿到method所在class,拿到class之后还是拿到class的名称
        //再调用toLowerFirstCase获得beanName
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(ioc.get(beanName), paramValues);
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //模板模式
        //1、加载配置文件
        this.doLoadConfig(config.getInitParameter("contextConfigLocation"));
        //2、扫描相关的类
        this.doScanner(contextConfig.getProperty("scanPackage"));
        //3、初始化所有相关的类的实例,并且放入到IOC容器之中
        this.doInstance();
        //4、完成依赖注入
        this.doAutowired();
        //5、初始化HandlerMapping
        this.initHandlerMapping();

        System.out.println("My Spring framework is init.");
    }

    /**
     * 加载配置文件
     *
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) {
        try (InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);) {
            //1、读取配置文件
            contextConfig.load(fis);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描相关的类
     * @param scanPackage
     */
    private void doScanner(String scanPackage) {
        //包传过来包下面的所有的类全部扫描进来的
        URL url = this.getClass().getClassLoader()
                .getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File classPath = new File(url.getFile());

        for (File file : classPath.listFiles()) {
            if (file.isDirectory()) {
                doScanner(scanPackage + "." + file.getName());
            } else {
                if (!file.getName().endsWith(".class")) {
                    continue;
                }
                String className = (scanPackage + "." + file.getName()).replace(".class", "");
                classNames.add(className);
            }
        }
    }

    /**
     * 初始化所有相关的类的实例,并且放入到IOC容器之中
     */
    private void doInstance() {
        //控制反转过程
        //工厂模式来实现的

        if (classNames.isEmpty()) {
            return;
        }

        try {
            for (String className : classNames) {
                Class<?> clazz = Class.forName(className);

                if (clazz.isAnnotationPresent(MyController.class)) {
                    Object instance = clazz.newInstance();
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    ioc.put(beanName, instance);
                } else if (clazz.isAnnotationPresent(MyService.class)) {
                    //1、默认的类名首字母小写

                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    //2、自定义命名
                    MyService service = clazz.getAnnotation(MyService.class);
                    if (!"".equals(service.value())) {
                        beanName = service.value();
                    }
                    Object instance = clazz.newInstance();
                    ioc.put(beanName, instance);

                    //3、根据类型注入实现类,投机取巧的方式
                    //clazz.getInterfaces()能获取当前类实现的接口类的类名
                    for (Class<?> i : clazz.getInterfaces()) {
                        if (ioc.containsKey(i.getName())) {
                            throw new Exception("The beanName is exists!!");
                        }
                        ioc.put(i.getName(), instance);
                    }
                } else {
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 完成依赖注入
     */
    private void doAutowired() {
        if (ioc.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            //拿到实例对象中的所有属性
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(MyAutowired.class)) {
                    continue;
                }
                MyAutowired autowired = field.getAnnotation(MyAutowired.class);
                String beanName = autowired.value().trim();
                if ("".equals(beanName)) {
                    beanName = field.getType().getName();
                }
                //不管你愿不愿意,强吻
                field.setAccessible(true); //设置私有属性的访问权限
                try {
                    //执行注入动作
                    field.set(entry.getValue(), ioc.get(beanName));
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    /**
     * 初始化HandlerMapping
     */
    private void initHandlerMapping() {
        if (ioc.isEmpty()) {
            return;
        }

        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            if (!clazz.isAnnotationPresent(MyController.class)) {
                continue;
            }

            String baseUrl = "";
            //获取Controller的url配置
            if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
                baseUrl = requestMapping.value();
            }

            //获取Method的url配置
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {

                //没有加RequestMapping注解的直接忽略
                if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }

                //映射URL
                MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
                //  /demo/query
                //  (//demo//query)
                String url = ("/" + baseUrl + "/" + requestMapping.value())
                        .replaceAll("/+", "/");
                handlerMapping.put(url, method);
                System.out.println("Mapped " + url + "," + method);
            }
        }
    }

    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}
发布了29 篇原创文章 · 获赞 10 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/csharpqiuqiu/article/details/100095158