Spring源码------手写体验IOC与DI

Spring源码------手写体验IOC与DI

目录

Spring源码------手写体验IOC与DI

1、前言

2、Spring IOC和DI原理流程

3、核心代码

4、总结


1、前言

本篇博客并非是对Spring源码的深入研究。而是对上一篇博客《Spring源码------手写体验MVC》结构的优化。过程中主要是体验Spring IOC和DI的初始化过程。那么这篇博客涉及到的知识点大致有以下几点:

  • 如何自定义注解,如何通过反射机制去赋予注解强大的功能(说白了,就是体验在反射机制下,注解功能是多么的强大)
  • Spring Ioc容器的实现原理
  • Spring DI 注解注入
  • Java反射机制
  • Java I/O流(加载配置文件,读取配置文件信息) 

2、Spring IOC和DI原理流程

Spring IOC的基本流程:

  • 读取配置文件
  • 解析配置文件,并封装成BeanDefinition
  • 把BeanDefinition对应的实例放到容器进行缓存

Spring DI的基本流程:

  • 循环读取BeanDefinition的缓存信息
  • 调用getBean()方法创建对象实例
  • 将创建好的对象实例包装为BeanWrapper对象
  • 将BeanWrapper对象缓存到IOC容器中
  • 循环IOC容器执行来进行注入

大致流图:

image.png

3、核心代码

代码结构图:

核心上下文applicationContext 代码:

**
 * @description: 自定义上下文
 * @author: zps
 * @create: 2020-05-08 15:06
 **/
public class ZPSApplicationContext {

    private ZPSBeanDefinitionReader reader ;

    //保存BeanDefinition信息
    private Map<String , ZPSBeanDefinition> beanDefinitionMap = new HashMap<String , ZPSBeanDefinition>();

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

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

    public ZPSApplicationContext(String... contextConfigLocation) {

        //加载配置文件
        reader = new ZPSBeanDefinitionReader(contextConfigLocation);

        //解析配置文件,封装成BeanDefinition
        List<ZPSBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

        //把BeanDefinition缓存起来
        try {
            doRegistBeanDefinition(beanDefinitions);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //依赖注入,这里默认没有延迟加载
        doAutowired();

    }

    private void doAutowired() {
        //调用getBean()
        //这一步,所有的Bean并没有真正的实例化,还只是配置阶段
        for(Map.Entry<String , ZPSBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()){
            //根据类名进行初始化
            getBean(beanDefinitionEntry.getKey());
        }

    }

    //把BeanDefinition缓存起来
    private void doRegistBeanDefinition(List<ZPSBeanDefinition> beanDefinitions) throws Exception {
        for (ZPSBeanDefinition 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配置信息
        ZPSBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        //2、反射实例化newInstance();
        Object instance = instantiateBean(beanName,beanDefinition);
        //3、封装成一个叫做BeanWrapper
        ZPSBeanWrapper beanWrapper = new ZPSBeanWrapper(instance);
        //4、保存到IoC容器
        factoryBeanInstanceCache.put(beanName,beanWrapper);
        //5、执行依赖注入
        populateBean(beanName,beanDefinition,beanWrapper);

        return beanWrapper.getWrapperInstance();
    }

    //执行依赖注入
    private void populateBean(String beanName, ZPSBeanDefinition beanDefinition, ZPSBeanWrapper zpsBeanWrapper) {
        

        Object instance = zpsBeanWrapper.getWrapperInstance();

        Class<?> clazz = zpsBeanWrapper.getWrappedClass();

        //在Spring中@Component
        if(!(clazz.isAnnotationPresent(ZPSController.class) || clazz.isAnnotationPresent(ZPSService.class))){
            return;
        }

        //把所有的包括private/protected/default/public 修饰字段都取出来
        for (Field field : clazz.getDeclaredFields()) {
            if(!field.isAnnotationPresent(ZPSAutowired.class)){ continue; }

            ZPSAutowired autowired = field.getAnnotation(ZPSAutowired.class);

            //如果用户没有自定义的beanName,就默认根据类型注入
            String autowiredBeanName = autowired.value().trim();
            if("".equals(autowiredBeanName)){
                //field.getType().getName() 获取字段的类型
                autowiredBeanName = field.getType().getName();
            }

            //暴力访问
            field.setAccessible(true);

            try {
                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){
                    continue;
                }
                //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    private Object instantiateBean(String beanName, ZPSBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        Object instance = null;
        try {
            if(this.factoryBeanObjectCache.containsKey(beanName)){
                instance = this.factoryBeanObjectCache.get(beanName);
            }else {
                Class<?> clazz = Class.forName(className);
                //2、默认的类名首字母小写
                instance = clazz.newInstance();
                this.factoryBeanObjectCache.put(beanName, instance);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return instance;
    }

    public Object getBean(Class<?> clazz){
        return getBean(clazz.getName());
    }

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

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

对应上图的BeanDefinition:

**
 * @description: 读取配置文件工具类
 * @author: zps
 * @create: 2020-05-08 15:18
 **/
public class ZPSBeanDefinitionReader {

    Properties properties = new Properties();

    //保存扫描的bean的类名信息
    private List<String> regitryBeanClasses = new ArrayList<String>();

    public ZPSBeanDefinitionReader(String... configLocations){

        //读取配置文件信息
        doConfig(configLocations[0]);

        //对文件进行扫描,筛选出需要的类文件
        doScanner(properties.getProperty("scanPackage"));

    }

    //对文件进行扫描
    private void doScanner(String scanPackage) {
        //jar 、 war 、zip 、rar
        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 {
                if(!file.getName().endsWith(".class")){continue;}
                //全类名 = 包名.类名
                String className = (scanPackage + "." + file.getName().replace(".class", ""));
                //Class.forName(className);
                regitryBeanClasses.add(className);
            }
        }
    }

    //加载配置文件
    private void doConfig(String configLocation) {
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation.replaceAll("classpath:",""));
        try {
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null != is){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public List<ZPSBeanDefinition> loadBeanDefinitions(){
        List<ZPSBeanDefinition> result = new ArrayList<ZPSBeanDefinition>();

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

                //保存类对应的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 ZPSBeanDefinition doCreateBeanDefinition(String beanName, String beanClassName) {
        ZPSBeanDefinition beanDefinition = new ZPSBeanDefinition();
        beanDefinition.setFactoryBeanName(beanName);
        beanDefinition.setBeanClassName(beanClassName);
        return beanDefinition;
    }

    private String toLowerFirstCase(String simpleName) {
        char [] chars = simpleName.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

}

对应上图的BeanDefinition(该类主要是封装从配置文件中读取的类信息):

**
 * @description: 封装类的信息
 * @author: zps
 * @create: 2020-05-08 15:23
 **/
public class ZPSBeanDefinition {

    private String factoryBeanName;  //简单类名
    private String beanClassName;    //全类名

    public String getFactoryBeanName() {
        return factoryBeanName;
    }

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

    public String getBeanClassName() {
        return beanClassName;
    }

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

对应上图的BeanWrapper :

/**
 * @description: bean的包装类
 * @author: zps
 * @create: 2020-05-08 17:39
 **/
public class ZPSBeanWrapper {
    private Object wrapperInstance;
    private Class<?> wrappedClass;

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

    public Object getWrapperInstance() {
        return wrapperInstance;
    }

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

运行截图:

4、总结

对Spring Ioc 和DI 大致工作流程的描述:

Spring IOC的基本流程:

  • 读取配置文件
  • 解析配置文件,并封装成BeanDefinition
  • 把BeanDefinition对应的实例放到容器进行缓存

Spring DI的基本流程:

  • 循环读取BeanDefinition的缓存信息
  • 调用getBean()方法创建对象实例
  • 将创建好的对象实例包装为BeanWrapper对象
  • 将BeanWrapper对象缓存到IOC容器中
  • 循环IOC容器执行来进行注入
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值