Spring5 - 30个类手写实战 - 打卡第二天(IOC与DI)

相关文章:
Spring5 - 30个类手写实战 - 打卡第一天(V1版本)

Spring5 - 30个类手写实战 - 打卡第三天(MVC)

Spring5 - 30个类手写实战 - 打卡第四天(AOP)

Spring5 - 30个类手写实战 - 打卡第五天(手绘IOC时序图)

1.IOC中三个最重要的类

在这里插入图片描述

  • BeanDefinition : 所有配置文件(properties/xml/yml/…),在内存中的体现形式
  • BeanWrapper : 保存了原生对象对未来要创建的各种对象之间的关联关系
  • ApplicationContext : 简单的理解为它就是工厂类,它有一个getBean()的方法,从IOC容器中去获取一个实例。Spring中默认是单例,并且是延时加载(Lazy)的。Spring中发生DI由getBean()触发。调用getBean()创建对象。
  • BeanDefinitionReader : 读取配置文件,最终将配置文件变成BeanDefinition

今天主要完成以下5个步骤:

  1. 调用serverlet init() 方法,初始化ApplicationContext
  2. 读取配置文件 (properties/xml/yml)
  3. 扫描相关的类 ,配置文件保存到内存中 BeanDefinition
  4. 初始化IOC容器并且实例化对象 BeanWrapper
  5. 完成DI注入

项目结构:

在这里插入图片描述

2 代码

LBeanDefinition

package com.liulin.spring.framework.beans.config;

/**
 * Create by DbL on 2020/5/1 0001
 */
public class LBeanDefinition {
    private String beanClassName;
    private String factoryBeanName;

    public String getBeanClassName() {
        return beanClassName;
    }

    public void setBeanClassName(String beanClassName) {
        this.beanClassName = beanClassName;
    }

    public String getFactoryBeanName() {
        return factoryBeanName;
    }

    public void setFactoryBeanName(String factoryBeanName) {
        this.factoryBeanName = factoryBeanName;
    }
}

LBeanWrapper

package com.liulin.spring.framework.beans;

/**
 * Create by DbL on 2020/5/1 0001
 */
public class LBeanWrapper {
    private Object wrapperInstance;
    private Class<?> wrappedClass;

    public LBeanWrapper(Object instance) {
        this.wrapperInstance = instance;
        this.wrappedClass = instance.getClass();
    }

    public Object getWrapperInstance() {
        return wrapperInstance;
    }

    public Class<?> getWrappedClass() {
        return wrappedClass;
    }
}

LBeanDefinitionReader

package com.liulin.spring.framework.support;

import com.liulin.spring.framework.beans.config.LBeanDefinition;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * Create by DbL on 2020/5/1 0001
 */
public class LBeanDefinitionReader {
    // 保存扫描的结果
    private List<String> registryBeanClasses = new ArrayList<String>();
    private Properties contextConfig = new Properties();

    public LBeanDefinitionReader(String... configLocations) {
        // 现在只有一个 写死取第一个
        doLoadConfig(configLocations[0]);
        // 扫描配置文件中配置的相关的类
        doScanner(contextConfig.getProperty("scanPackage"));
    }

    public List<LBeanDefinition> loadBeanDefinitions() {
        List<LBeanDefinition> result = new ArrayList<LBeanDefinition>();
        for (String className : registryBeanClasses) {
            try {
                Class<?> beanClass = Class.forName(className);
                if(beanClass.isInterface()){
                    continue;
                }
                // 保存类对应的ClassName(全类名) 还有beanName
                // 1. 默认类名首字母小写
                result.add(doCreateBeandefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));
                // 2. 自定义
                // 3. 接口注入
                for(Class<?> i : beanClass.getInterfaces()){
                    result.add(doCreateBeandefinition(i.getName(),beanClass.getName()));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    private LBeanDefinition doCreateBeandefinition(String beanName, String beanClassName) {
        LBeanDefinition beanDefinition = new LBeanDefinition();
        beanDefinition.setBeanClassName(beanClassName);
        beanDefinition.setFactoryBeanName(beanName);
        return beanDefinition;
    }

    private void doLoadConfig(String contextConfigLocation) {
        // Spring中使用策略模式读取,这里直接将classpath: 替换成空
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:", ""));
        try {
            contextConfig.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != resourceAsStream) {
                try {
                    resourceAsStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doScanner(String scanPackage) {
        // com.liulin.demo 需要转换成文件夹的路径形式便于扫描路径下的文件
        URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
        File classPath = new File(url.getFile());
        // 当成是一个classPath文件夹
        for (File file : classPath.listFiles()) {
            if (file.isDirectory()) {
                // 递归遍历子文件夹
                doScanner(scanPackage + "." + file.getName());
            } else {
                // 非class文件跳过
                if (!file.getName().endsWith(".class")) continue;
                String className = file.getName().replace(".class", "");
                // Class.forName(className)
                // 防止名字重复,这里使用包名加上类名
                registryBeanClasses.add(scanPackage + "." + className);
            }
        }
    }

    /**
     * 首字母小写
     * @param simpleName
     * @return
     */
    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}

LApplicationContext

package com.liulin.spring.framework.context;

import com.liulin.spring.framework.annotation.LAutowired;
import com.liulin.spring.framework.annotation.LController;
import com.liulin.spring.framework.annotation.LService;
import com.liulin.spring.framework.beans.LBeanWrapper;
import com.liulin.spring.framework.beans.config.LBeanDefinition;
import com.liulin.spring.framework.support.LBeanDefinitionReader;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 职责:完成Bean的创建和DI
 * Create by DbL on 2020/5/1 0001
 */
public class LApplicationContext {

    private LBeanDefinitionReader reader;

    private Map<String, LBeanDefinition> beanDefinitionMap = new HashMap<String, LBeanDefinition>();

    private Map<String, LBeanWrapper> factoryBeanInstanceCache = new HashMap<String, LBeanWrapper>();

    private Map<String, Object> factoryBeanObjectCache = new HashMap<String, Object>();

    public LApplicationContext(String... configLocations) {
        // 1.加载配置文件
        reader = new LBeanDefinitionReader(configLocations);
        try {
            // 2.解析配置文件,封装成BeanDefinition
            List<LBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
            // 3.把Beandefinition缓存起来
            // beanDefinitionMap
            doRegistBeanDefinition(beanDefinitions);
            doAutoWrited();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doAutoWrited() {
        // 调用getBean() 仅非延时加载的类
        for (Map.Entry<String, LBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }

    private void doRegistBeanDefinition(List<LBeanDefinition> beanDefinitions) throws Exception {
        for (LBeanDefinition beanDefinition : beanDefinitions) {
            if (this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The " + beanDefinition.getFactoryBeanName() + " is Exists");
            }
            beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
            beanDefinitionMap.put(beanDefinition.getBeanClassName(), beanDefinition);
        }
    }

    // Bean的实例化,DI是从这个方法开始的
    public Object getBean(String beanName) {
        // 1. 先拿到BeanDefinition配置信息
        LBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        // 2.反射实例化 newInstance()
        Object instance = instantiateBean(beanName, beanDefinition);
        // 3.封装成beanWrapper
        LBeanWrapper beanWrapper = new LBeanWrapper(instance);
        // 4.保存到IOC容器
        factoryBeanInstanceCache.put(beanName, beanWrapper);
        // 5.执行依赖注入
        populateBean(beanName, beanDefinition, beanWrapper);
        return beanWrapper.getWrapperInstance();
    }

    private void populateBean(String beanName, LBeanDefinition beanDefinition, LBeanWrapper beanWrapper) {
        // 可能会涉及到循环依赖?  这里不做处理
        // 用两个缓存
        // 1.把第一次读取结果为空的BeanDefinition存到第一个缓存
        // 2.等第一次循环之后,第二次循环再检查第一次的缓存。再进行赋值
        Object instance = beanWrapper.getWrapperInstance();
        Class<?> clazz = beanWrapper.getWrappedClass();
        // Spring中为component ,为其他注解的父类,这里只针对自定义的LController与LService两个注解
        if(!(clazz.isAnnotationPresent(LController.class) || clazz.isAnnotationPresent(LService.class)) ){
            return;
        }
        for (Field field : clazz.getDeclaredFields()) {
            if (!field.isAnnotationPresent(LAutowired.class)) {
                return;
            }
            LAutowired autowired = field.getAnnotation(LAutowired.class);
            // 如果用户没有自定义的beanName,就默认根据类型注入
            String autoWiredName = autowired.value().trim();
            if ("".equals(autoWiredName)) {
                // 接口全名
                autoWiredName = field.getType().getName();
            }
            // 对于类中的private属性的成员进行暴力访问
            field.setAccessible(true);
            try {
                if(this.factoryBeanInstanceCache.get(autoWiredName) == null){
                    continue;
                }
                // ioc.get(beanName) 相当于通过接口的全名从IOC中拿到接口的实现的实例
                field.set(instance, this.factoryBeanInstanceCache.get(autoWiredName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }

    // 创建真正的实例对象
    private Object instantiateBean(String beanName, LBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            if(this.factoryBeanObjectCache.containsKey(beanName)){
                instance =  factoryBeanObjectCache.get(beanName);
            }else {
                Class<?> aClass = Class.forName(className);
                instance = aClass.newInstance();
                this.factoryBeanObjectCache.put(beanName, instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    public Object getBean(Class beanClass) {
        return getBean(beanClass.getName());
    }

    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[getBeanDefinitionCount()]);
    }
}

LDispatcherServlet

package com.liulin.spring.framework.beans.webmvc.servlet;

import com.liulin.spring.framework.annotation.*;
import com.liulin.spring.framework.context.LApplicationContext;

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

/**
 * Create by DbL on 2020/4/29 0029
 */
public class LDispatcherServlet extends HttpServlet {
    private LApplicationContext applicationContext;

    // IOC容器,key默认是类名首字母小写,value是对应的实例对象
    // private Map<String, Object> ioc = new HashMap<String, Object>();

    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 {
        // 6.委派,根据URL去找到一个对应的Method并通过response返回
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception , 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;
        }

        Map<String, String[]> params = req.getParameterMap();
        Method method = this.handlerMapping.get(url);
        // 获取形参列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        Object[] paramValues = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            Class paramterType = parameterTypes[i];
            if (paramterType == HttpServletRequest.class) {
                paramValues[i] = req;
            } else if (paramterType == HttpServletResponse.class) {
                paramValues[i] = resp;
            } else if (paramterType == String.class) {
                // 通过运行时的状态去拿到注解的值
                Annotation[][] pa = method.getParameterAnnotations();
                for (int j = 0; j < pa.length; j++) {
                    for (Annotation a : pa[j]) {
                        if (a instanceof LRequestParam) {
                            String paramName = ((LRequestParam) a).value();
                            if (!"".equals(paramName.trim())) {
                                String value = Arrays.toString(params.get(paramName))
                                        .replaceAll("\\[|\\]", "")
                                        .replaceAll("\\s+", "");
                                paramValues[i] = value;
                            }
                        }
                    }
                }
            }
        }

        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
        method.invoke(applicationContext.getBean(beanName), paramValues);

    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 初始化Spring的核心IO容器
        applicationContext = new LApplicationContext(config.getInitParameter("contextConfigLocation"));

        //================MVC部分==============//
        // 5.初始化HandlerMapping
        doInitHandlerMapping();
        System.out.println("L Spring framework init success ");
    }

    private void doInitHandlerMapping() {
        if (this.applicationContext.getBeanDefinitionCount() == 0) {
            return;
        }
        for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
            Class<?> clazz = applicationContext.getBean(beanName).getClass();
            // 类没有加注解的跳过
            if (!clazz.isAnnotationPresent(LController.class)) {
                continue;
            }
            // 如果类上定义了路径,方法上的路径需要拼接上此路径
            String baseUrl = "";
            if (clazz.isAnnotationPresent(LRequestMapping.class)) {
                LRequestMapping Mapping = clazz.getAnnotation(LRequestMapping.class);
                baseUrl = Mapping.value();
            }
            // 只获取public的方法
            for (Method method : clazz.getMethods()) {
                // 方法没有加注解的跳过
                if (!method.isAnnotationPresent(LRequestMapping.class)) {
                    continue;
                }
                LRequestMapping requestMapping = method.getAnnotation(LRequestMapping.class);
                // 对于配置了“/” 和没有配置“/”的通过正则表达式统一处理
                String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
                handlerMapping.put(url, method);
                System.out.println("mapped : " + url + " , " + method);
            }
        }
    }


    /**
     * 首字母小写
     * @param simpleName
     * @return
     */
    private String toLowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }
}

3 测试

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值