手写Spring框架源码(基础款)(控制反转和依赖注入)

手写Spring框架源码(基础款)(控制反转和依赖注入),第1张

手写Spring框架的目的:为了更了解Spring框架的执行流程和生命周期
Spring框架前提准备:

1 自定义的ApplicationContext

2 自定义的cofig配置类

3 自定义的注解(例如@ComponentScan,@Component,@Scope)

第一步:main()方法为入口(模拟getBean的过程)

public class ApplicationTest {

    public static void main(String[] args) {
        // 自定义的ApplicationContext
        YuWenWenApplicationContext yuWenWenApplicationContext = new YuWenWenApplicationContext(AppConfig.class);
        // 从容器中要找到yuWenWenService的对象
        yuWenWenApplicationContext.getBean("yuWenWenService");

    }

}

第二步:自定义的ApplicationContext(此处的构造方法是要AppCofig配置类中的信息)

public class YuWenWenApplicationContext {

    private Class config;

    public YuWenWenApplicationContext(Class config) {
        this.config = config;
    }

第三步:自定义的注解加上配置类(@ComponentScan是自己写的注解并非spring原生注解)

//扫描包路径
@ComponentScan("com.sbi.service")
public class AppConfig {

}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {

    String value();

}

第四步:自定义ApplicationContext的初始化方法逻辑

其实spring框架做的最重要的一件事,就是将一个类变成一个Bean的过程。

设计思路:
1 扫描ComponentScan的包路径
2 拿到包路径下面所有的带有@Component的注解的类

并且将这些类的名字和类型包装成BeanDefinition对象并且放到BeanDefinitionMap里

(我们视为持有这个注解的类将要被spring管理最终要成为一个bean)

3 将持有@Component注解的类通过反射的方式创建对象并放到一个单例池里

(遍历BeanDefinitionMap根据类描述通过反射创建对象,并且扔到单例池里)

,准备提供给getBean()使用

我们最终的目的其实就是要

通过反射的方式来创建对象

Class aClass = classLoader.loadClass("com.sbi.service.YuWenWenService");
aClass.newInstance();

 这块稍微需要考虑的点就是:
如何通过ComponentScan定义的包路径,拿到包路径下面的所有类的路径并且是以"xxx.xxx.xxx.xxx"的字符串形式传到这个classLoader.loadClass这个方法的参数里

    private void scan(Class config) throws ClassNotFoundException {
        // 此处得到@ComponentScan的扫描路径
        ComponentScan path = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
        // 得到一个类加载器 classpath下面用AppClassLoader
        ClassLoader classLoader = YuWenWenApplicationContext.class.getClassLoader();
        // 得到类的包路径(此处为com.sbi.service格式需要改成com/sbi/service)
        URL resource = classLoader.getResource(path.value().replace(".", "/"));
        // 得到包下的文件
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            // 得到包下所有的文件信息
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                String pathReplaceBefore = absolutePath
                    .substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                String pathreplaceAfter = pathReplaceBefore.replace("\\", ".");
                // 此处最终需要com.sbi.service.YuWenWenService格式
                Class aClass = classLoader.loadClass(pathreplaceAfter);
                // 如果这个类拥有@Component注解,那么我们就要反射
                if (aClass.isAnnotationPresent(Component.class)) {
                    Component declaredAnnotation = aClass.getDeclaredAnnotation(Component.class);
                    // 拿到@Component的value值也就是bean的名字
                    String springbeanName = declaredAnnotation.value();
                    // 描述类创建
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setClazz(aClass);
                    // 判断这个类是否还持有@Scope的其他作用域(spring 一共有四种作用域,默认不指定的情况下就是单例)
                    if (aClass.isAnnotationPresent(Scope.class)) {
                        Scope declaredAnnotation1 = aClass.getDeclaredAnnotation(Scope.class);
                        beanDefinition.setScope(declaredAnnotation1.value());
                    } else {
                        // spring默认不指定的情况下就是单例作用域
                        beanDefinition.setScope("singleton");
                    }
                    // 将类信息放到beanDefinitionMap里
                    beanDefinitionMap.put(springbeanName, beanDefinition);
                }

            }
        }
    }

    // 单例池(key:beanName,value:bean)
    private ConcurrentHashMap singletonObjects = new ConcurrentHashMap<>();
    // BeanDefinitionMap(key:beanName,value:类描述)
    private ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<>();

第五步:getBean()实装

applicationContext根据beanName去单例池里面去拿自己的bean

    public Object getBean(String beanName)
        throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        //去beanDefinitionMap寻找是否有自己的类描述
        if (beanDefinitionMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //如果是单例bean,直接从单例池拿
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = singletonObjects.get(beanName);
                return bean;
                //如果是原型bean就给创建一个新的bean
            } else {
                Object bean = createBean(beanDefinition);
                return bean;
            }
         //没招到的情况下就说明此类并没有加@Competent
        } else {
            throw new NullPointerException("此类不存在");
        }
    }

第六步:构造器初始化逻辑就是将以上的逻辑拼接起来

    public YuWenWenApplicationContext(Class config)
        throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        this.config = config;
        // 扫描bean,并且得到所有beanDefinition放在beanDefinitionMap里
        scan(config);
        // 循环beanDefinitionMap全部
        for (Map.Entry entry : beanDefinitionMap.entrySet()) {
            if (entry.getValue().getScope().equals("singleton")) {
                // 创建bean
                Object bean = createBean(entry.getValue());
                // 将创建好的bean放到单例池等待getBean调用
                singletonObjects.put(entry.getKey(),bean);
            }
        }
    }

测试一下

public class ApplicationTest {

    public static void main(String[] args)
        throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        YuWenWenApplicationContext yuWenWenApplicationContext = new YuWenWenApplicationContext(AppConfig.class);
        System.out.println(yuWenWenApplicationContext.getBean("yuWenWenService"));
        System.out.println(yuWenWenApplicationContext.getBean("yuWenWenService"));
    }
}

结果是两次拿到的都是同一个对象(这里边默认@Scope默认不设置就是单例)

成功的实现了利用简单工厂的一个单例模式。 

com.sbi.service.YuWenWenService@799f7e29
com.sbi.service.YuWenWenService@799f7e29

 第七步:依赖注入的功能实装

到第七步之前,已经可以实现了一个容器创建Bean的过程。

但Spring框架还有一个强大的功能就是依赖注入

自己定义个注解@Autowired

@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {

}

 此刻你仅仅是在属性加上@Autowired注解,是肯定达不到依赖注入的功能的。

@Component("yuWenWenService")
public class YuWenWenService {

    @Autowired
    private MiaoWenWenService miaoWenWenService;

    private void test() {
        // 此刻不做任何逻辑 就是null
        System.out.println(miaoWenWenService);
    }
}

在刚刚的createBean方法中,我们已经成功的创建了一个对象,那么依赖注入的这一步部分逻辑就要写在创建对象后面

思想:拿到这个对象的属性,看看属性有没有持有@Autowired的注解,如果持有,就去单例池里面去拿到这个bean,并给自己的这个属性赋值。

    private Object createBean(BeanDefinition beanDefinition)
        throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 第一步:创建对象
        Object o = beanDefinition.getClazz().getDeclaredConstructor().newInstance();
        // 第二部:依赖注入
        // 先通过反射拿到这个对象的私有属性
        Field[] declaredFields = o.getClass().getDeclaredFields();
        // 遍历私有属性
        for (Field field : declaredFields) {
            // 判断私有属性的是否持有@Autowired
            if (field.isAnnotationPresent(Autowired.class)) {
                // 如果持有@Autowired 就从单例池里拿这个bean并赋值
                Object bean = getBean(field.getName());
                // 默认是不允许设置私有属性的值的 此处必须得改成true
                field.setAccessible(true);
                // 实现依赖注入
                field.set(o,bean);
            }
        }
        return o;
    }

测试一下

public class ApplicationTest {


    public static void main(String[] args)
        throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        YuWenWenApplicationContext yuWenWenApplicationContext = new YuWenWenApplicationContext(AppConfig.class);
        YuWenWenService yuWenWenService = (YuWenWenService) yuWenWenApplicationContext.getBean("yuWenWenService");
        yuWenWenService.test();
    }
}

 结果成功的依赖注入

com.sbi.service.MiaoWenWenService@7aec35a

加入我此刻把 MiaoWenWenService的@Component去除掉,那么属性注入的时候 从单例池里根本找不到MiaoWenWenService这个bean应该就会失效 看看实际效果是不是如此

结果:成功了

Exception in thread "main" java.lang.NullPointerException: 此类不存在

以上便是实现了手写Spring框架最重要的两个功能 控制反转和依赖注入

所有源码如下↓(applicationContext)

package com.sbi.config;

import com.sbi.annotation.Autowired;
import com.sbi.annotation.Component;
import com.sbi.annotation.ComponentScan;
import com.sbi.annotation.Scope;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class YuWenWenApplicationContext {

    private Class config;

    // 单例池(key:beanName,value:bean)
    private ConcurrentHashMap singletonObjects = new ConcurrentHashMap<>();
    // BeanDefinitionMap(key:beanName,value:类描述)
    private ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<>();

    public YuWenWenApplicationContext(Class config)
        throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        this.config = config;
        // 扫描bean,并且得到所有beanDefinition放在beanDefinitionMap里
        scan(config);
        // 循环beanDefinitionMap全部
        for (Map.Entry entry : beanDefinitionMap.entrySet()) {
            if (entry.getValue().getScope().equals("singleton")) {
                // 创建bean
                Object bean = createBean(entry.getValue());
                // 将创建好的bean放到单例池等待getBean调用
                singletonObjects.put(entry.getKey(), bean);
            }
        }
    }

    private Object createBean(BeanDefinition beanDefinition)
        throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 第一步:创建对象
        Object o = beanDefinition.getClazz().getDeclaredConstructor().newInstance();
        // 第二部:依赖注入
        // 先通过反射拿到这个对象的私有属性
        Field[] declaredFields = o.getClass().getDeclaredFields();
        // 遍历私有属性
        for (Field field : declaredFields) {
            // 判断私有属性的是否持有@Autowired
            if (field.isAnnotationPresent(Autowired.class)) {
                // 如果持有@Autowired 就从单例池里拿这个bean并赋值
                Object bean = getBean(field.getName());

                // 默认是不允许设置私有属性的值的 此处必须得改成true
                field.setAccessible(true);
                // 实现依赖注入
                field.set(o, bean);

            }
        }
        return o;
    }

    private void scan(Class config) throws ClassNotFoundException {
        // 此处得到@ComponentScan的扫描路径
        ComponentScan path = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
        // 得到一个类加载器 classpath下面用AppClassLoader
        ClassLoader classLoader = YuWenWenApplicationContext.class.getClassLoader();
        // 得到类的包路径(此处为com.sbi.service格式需要改成com/sbi/service)
        URL resource = classLoader.getResource(path.value().replace(".", "/"));
        // 得到包下的文件
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            // 得到包下所有的文件信息
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                String pathReplaceBefore = absolutePath
                    .substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                String pathreplaceAfter = pathReplaceBefore.replace("\\", ".");
                // 此处最终需要com.sbi.service.YuWenWenService格式
                Class aClass = classLoader.loadClass(pathreplaceAfter);
                // 如果这个类拥有@Component注解,那么我们就要反射
                if (aClass.isAnnotationPresent(Component.class)) {
                    Component declaredAnnotation = aClass.getDeclaredAnnotation(Component.class);
                    // 拿到@Component的value值也就是bean的名字
                    String springbeanName = declaredAnnotation.value();
                    // 描述类创建
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setClazz(aClass);
                    // 判断这个类是否还持有@Scope的其他作用域(spring 一共有四种作用域,默认不指定的情况下就是单例)
                    if (aClass.isAnnotationPresent(Scope.class)) {
                        Scope declaredAnnotation1 = aClass.getDeclaredAnnotation(Scope.class);
                        beanDefinition.setScope(declaredAnnotation1.value());
                    } else {
                        // spring默认不指定的情况下就是单例作用域
                        beanDefinition.setScope("singleton");
                    }
                    // 将类信息放到beanDefinitionMap里
                    beanDefinitionMap.put(springbeanName, beanDefinition);
                }

            }
        }
    }

    public Object getBean(String beanName)
        throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        //去beanDefinitionMap寻找是否有自己的类描述
        if (beanDefinitionMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //如果是单例bean,直接从单例池拿
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = singletonObjects.get(beanName);
                return bean;
                //如果是原型bean就给创建一个新的bean
            } else {
                Object bean = createBean(beanDefinition);
                return bean;
            }
            //没招到的情况下就说明此类并没有加@Competent
        } else {
            throw new NullPointerException("此类不存在");
        }
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/871200.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存