Spring源码之:实现一个简单的Spring容器

注意,本文不涉及对Spring源码的解析

在本文中,我们将通过代码来实现一个简单的Spring容器

通过不断完善自定义的Spring容器,我们将逐步认识到Spring的三级缓存各自的作用

1. 无任何依赖的Bean

先来个最简单的,假设Spring容器管理的都是无任何依赖的Bean:

java 复制代码
class A { }
class B { }

Spring容器的实现如下:

java 复制代码
public class Spring {

    /**
     * beanMap用于存放所有已经创建的Bean;其中,key为beanName,value为Bean本身
     */
    private Map<String, Object> beanMap = new HashMap<>();

    /**
     * beanDefinitionMap用于模拟xml文件的解析结果;其中key为beanName,value为Bean的类型
     */
    private Map<String, Class> beanDefinitionMap = new HashMap<>();
    {
        beanDefinitionMap.put("a", A.class);
        beanDefinitionMap.put("b", B.class);
    }

    /**
     * 初始化Spring容器,根据beanDefinitionMap来生成相应的Bean
     */
    private Spring() throws Exception {

        // 遍历所有的BeanDefinition,并获取相应的Bean
        // 这里调用getBean()方法并不是为了获取Bean,而是为了触发Bean的创建
        for (Map.Entry<String, Class> entry : beanDefinitionMap.entrySet()) {
            getBean(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 创建Bean的核心方法
     */
    private Object getBean(String beanName, Class beanClass) throws Exception {

        // 如果已经创建过了,则直接返回已创建的
        Object bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }

        // 直接通过反射创建Bean实例
        bean = beanClass.newInstance();
        System.out.println(bean + " created!");

        // 将该Bean放到beanMap中并返回
        beanMap.put(beanName, bean);
        return bean;
    }

    /**
     * 测试程序,打印出容器中所有的Bean
     */
    public static void main(String[] args) throws Exception {
        new Spring().beanMap.forEach((beanName, bean) -> System.out.println(beanName + ": " + bean));
    }
}

结果如下:

text 复制代码
A@4f2410ac created!
B@722c41f4 created!
a: A@4f2410ac
b: B@722c41f4

2. 无循环依赖的Bean

复杂一点,假设A类中有个B b;字段:

java 复制代码
class A { B b; }
class B { }

显然,此时需要对a对象进行依赖注入,实现如下:

java 复制代码
public class Spring {
    private Map<String, Object> beanMap = new HashMap<>();
    private Map<String, Class> beanDefinitionMap = new HashMap<>();
    {
        beanDefinitionMap.put("a", A.class);
        beanDefinitionMap.put("b", B.class);
    }

    private Spring() throws Exception {
        for (Map.Entry<String, Class> entry : beanDefinitionMap.entrySet()) {
            getBean(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 对原来的getBean()方法进行改进
     */
    private Object getBean(String beanName, Class beanClass) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }

        bean = beanClass.newInstance();
        System.out.println(bean + " created!");

        // 依赖注入;为了方便,我们直接将字段名称当作beanName来查找对应的Bean
        for (Field field : beanClass.getDeclaredFields()) {
            String fieldName = field.getName();
            Object dependency = getBean(fieldName, beanDefinitionMap.get(fieldName));
            System.out.println(bean + ": " + fieldName + "=" + dependency);
            field.set(bean, dependency);
        }

        beanMap.put(beanName, bean);
        return bean;
    }

    public static void main(String[] args) throws Exception {
        new Spring().beanMap.forEach((beanName, bean) -> System.out.println(beanName + ": " + bean));
    }
}

结果如下:

text 复制代码
A@4f2410ac created!
B@5b80350b created!
A@4f2410ac: b=B@5b80350b
a: A@4f2410ac
b: B@5b80350b

3. 循环依赖的Bean

再复杂一点,假设B类中也有个A a;字段,此时出现循环依赖:

java 复制代码
class A { B b; }
class B { A a; }

这时候,上述实现的Spring容器就会不断地创建a和b对象,最终栈溢出,原因:

  1. 首先,创建a对象,并对其进行依赖注入(注意此时a对象并未存放到beanMap中)
  2. 对a进行依赖注入时,发现b对象不存在,因此创建b对象,并对其进行依赖注入
  3. 对b进行依赖注入时,发现a对象不存在(因为之前创建的a对象没保存到beanMap中),因此容器又去创建a对象,导致死循环

解决方法是,在创建对象之后、进行依赖注入之前就把对象存到beanMap

java 复制代码
public class Spring {
    private Map<String, Object> beanMap = new HashMap<>();
    private Map<String, Class> beanDefinitionMap = new HashMap<>();
    {
        beanDefinitionMap.put("a", A.class);
        beanDefinitionMap.put("b", B.class);
    }

    private Spring() throws Exception {
        for (Map.Entry<String, Class> entry : beanDefinitionMap.entrySet()) {
            getBean(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 对原来的getBean()方法进行改进
     */
    private Object getBean(String beanName, Class beanClass) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }

        bean = beanClass.newInstance();
        System.out.println(bean + " created!");

        // 立刻将Bean放入beanMap中
        beanMap.put(beanName, bean);
        
        // 对该Bean进行依赖注入,然后返回该Bean
        for (Field field : beanClass.getDeclaredFields()) {
            String fieldName = field.getName();
            Object dependency = getBean(fieldName, beanDefinitionMap.get(fieldName));
            System.out.println(bean + ": " + fieldName + "=" + dependency);
            field.set(bean, dependency);
        }
        return bean;
    }

    public static void main(String[] args) throws Exception {
        new Spring().beanMap.forEach((beanName, bean) -> System.out.println(beanName + ": " + bean));
    }
}

结果如下:

text 复制代码
A@4f2410ac created!
B@5b80350b created!
B@5b80350b: a=A@4f2410ac
A@4f2410ac: b=B@5b80350b
a: A@4f2410ac
b: B@5b80350b

4. 循环依赖 + AOP

我们只使用了一级缓存(即beanMap)就解决了循环依赖的问题;但这还不够,因为我们没有考虑到AOP的情况

假设我们需要对a进行AOP增强,那么b中注入的a实际上应该是a的代理而不是a本身,这样的话上面的实现就有问题了

java 复制代码
/**
 * 为了方便,我们规定实现了本接口的类就代表该类需要进行AOP增强
 */
interface Proxyable { }

class A implements Proxyable { B b; }
class B { Proxyable a; }

上述的实现中,我们是直接将a本身放到了一级缓存中,导致注入b中的就是a本身,这就不符合要求了

因此,在实例化对象之后,需要判断该对象是否需要AOP;如果需要,则应创建它的代理对象并将代理对象放入beanMap中:

java 复制代码
public class Spring {
    private Map<String, Object> beanMap = new HashMap<>();
    private Map<String, Class> beanDefinitionMap = new HashMap<>();
    {
        beanDefinitionMap.put("a", A.class);
        beanDefinitionMap.put("b", B.class);
    }

    private Spring() throws Exception {
        for (Map.Entry<String, Class> entry : beanDefinitionMap.entrySet()) {
            getBean(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 对原来的getBean()方法进行改进
     */
    private Object getBean(String beanName, Class beanClass) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }

        // 创建原始对象
        Object rowBean = beanClass.newInstance();
        System.out.println(rowBean + " created!");

        // 如果该对象有AOP增强,则创建该对象的代理对象
        // 注意proxyOrSelf才是真正需要暴露出去的对象,因此将其存到beanMap中
        Object proxyOrSelf = proxyIfNecessary(rowBean);
        beanMap.put(beanName, proxyOrSelf);

        // 对原始对象进行依赖注入
        for (Field field : beanClass.getDeclaredFields()) {
            String fieldName = field.getName();
            Object dependency = getBean(fieldName, beanDefinitionMap.get(fieldName));
            System.out.println(rowBean + ": " + fieldName + "=" + dependency);
            field.set(rowBean, dependency);
        }

        // 注意proxyOrSelf才是真正要返回(暴露)的对象
        return proxyOrSelf;
    }

    /**
     * 模拟AOP增强
     */
    private Object proxyIfNecessary(Object bean) {
        if (!(bean instanceof Proxyable)) {
            return bean;
        }
        Object beanProxy = Proxy.newProxyInstance(
                bean.getClass().getClassLoader(), 
                bean.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    if (method.getName().equals("toString")) {
                        return "Proxy(delegate=" + bean.toString() + ")";
                    }
                    return method.invoke(bean, args);
                }
        );
        System.out.println(beanProxy + " created!");
        return beanProxy;
    }

    public static void main(String[] args) throws Exception {
        new Spring().beanMap.forEach((beanName, bean) -> System.out.println(beanName + ": " + bean));
    }
}

结果如下:

text 复制代码
A@4f2410ac created!
Proxy(delegate=A@4f2410ac) created!
B@16b3fc9e created!
B@16b3fc9e: a=Proxy(delegate=A@4f2410ac)
A@4f2410ac: b=B@16b3fc9e
a: Proxy(delegate=A@4f2410ac)
b: B@16b3fc9e

5. 三级缓存

上述的代码还存在一个问题,原因在于:

  1. Spring的AOP是通过BeanPostProcessor来实现的,而BeanPostProcessor在正常情况下应该等到Bean初始化时再执行
  2. 我们的代码为了解决AOP和循环依赖的问题,在Bean刚刚实例化后就立刻生成其代理对象,也就是提前执行了BeanPostProcessor
  3. 因此,我们的做法是破坏了Spring的规范的

想象这样一种情形:a需要进行AOP增强,但是a依赖的Bean并没有反过来依赖a

  1. 那么a的代理对象完全可以等到a初始化完成后再创建
  2. 但是,我们的代码中,一上来就创建了a的代理对象,这显然是不对的

我们希望尽量让代理对象在原始对象初始化完成后再创建,只有在万不得已的情况下才提前创建代理对象,因此引入ProxyFactory

java 复制代码
/**
 * ProxyFactory主要用于获取Bean的代理对象
 */
interface ProxyFactory {

    /**
     * 封装代理对象的创建逻辑;注意:
     * 1. 在原始对象没有AOP增强的情况下,该方法返回的是原始对象本身
     * 2. 代理对象只有在调用get()方法时才会创建
     * 
     * 假设a需要进行AOP增强,并且a依赖了b:
     * 1. 那么,在实例化a后,我们可以将a的代理对象的创建逻辑封装到ProxyFactory中,并将该ProxyFactory缓存起来
     * 2. 如果b依赖了a,即出现了循环依赖,那么在对b进行依赖注入时,可以调用a的ProxyFactory的get()方法提前获取到a代理对象
     * 3. 如果b没有依赖a,那么a的ProxyFactory的get()方法没有被调用,这时候就可以在a初始化完成后再创建a的代理对象了
     */
    Object get();
}

具体的实现逻辑:

java 复制代码
public class Spring {
    private Map<String, Object> beanMap = new HashMap<>();
    private Map<String, Class> beanDefinitionMap = new HashMap<>();
    {
        beanDefinitionMap.put("a", A.class);
        beanDefinitionMap.put("b", B.class);
    }

    /**
     * beanFactoryMap中,key为beanName,value为Bean的代理对象工厂
     */
    private Map<String, ProxyFactory> proxyFactoryMap = new HashMap<>();

    private Spring() throws Exception {
        for (Map.Entry<String, Class> entry : beanDefinitionMap.entrySet()) {
            getBean(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 对原来的getBean()方法进行改进
     */
    private Object getBean(String beanName, Class beanClass) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }

        // 判断是否有代理对象工厂,如果有,说明该Bean之前已经创建过了,只是没有初始化完成
        // 因此,如果有代理对象工厂,说明出现了循环依赖,此时需要通过工厂提前获取代理对象并返回
        // 注意,工厂使用过后要删掉,避免重复创建代理对象从而破坏单例;同时还要把得到的代理对象放到一级缓存中
        ProxyFactory proxyFactory = proxyFactoryMap.remove(beanName);
        if (proxyFactory != null) {
            Object proxy = proxyFactory.get();
            beanMap.put(beanName, proxy);
            return proxy;
        }

        // 创建原始对象
        Object rowBean = beanClass.newInstance();
        System.out.println(rowBean + " created!");

        // 将代理对象的创建逻辑封装到ProxyFactory中
        // 这样的话,如果该Bean有AOP增强,且其它Bean依赖了该Bean,那么其它Bean可以通过该工厂获得该Bean的代理
        proxyFactoryMap.put(beanName, () -> proxyIfNecessary(rowBean));

        // 对原始对象进行依赖注入
        for (Field field : beanClass.getDeclaredFields()) {
            String fieldName = field.getName();
            Object dependency = getBean(fieldName, beanDefinitionMap.get(fieldName));
            System.out.println(rowBean + ": " + fieldName + "=" + dependency);
            field.set(rowBean, dependency);
        }

        // 判断该Bean对应的代理对象工厂是否存在(如果存在,则顺便删除掉)
        // 如果存在,说明代理对象没有提前创建,即没有出现循环依赖,此时创建该Bean的代理对象(如果有必要的话)并返回
        // 如果不存在,说明其它Bean通过代理工厂提前创建了本Bean的代理对象;此时需要返回代理对象(该代理对象肯定在一级缓存中)
        proxyFactory = proxyFactoryMap.remove(beanName);

        // 如果没有循环依赖,则创建代理对象,并将代理对象放入一级缓存,然后返回该代理对象
        if (proxyFactory != null) {
            Object proxyOrSelf = proxyFactory.get();
            beanMap.put(beanName, proxyOrSelf);
            return proxyOrSelf;
        }

        // 否则,该Bean一定已经在一级缓存中了,因此返回一级缓存中的Bean
        return beanMap.get(beanName);
    }

    private Object proxyIfNecessary(Object bean) {
        if (!(bean instanceof Proxyable)) {
            return bean;
        }
        Object beanProxy = Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    if (method.getName().equals("toString")) {
                        return "Proxy(delegate=" + bean.toString() + ")";
                    }
                    return method.invoke(bean, args);
                }
        );
        System.out.println(beanProxy + " created!");
        return beanProxy;
    }

    public static void main(String[] args) throws Exception {
        new Spring().beanMap.forEach((beanName, bean) -> System.out.println(beanName + ": " + bean));
    }
}

结果如下:

text 复制代码
class A implements Proxyable { B b; }
class B { Proxyable a; }

A@4f2410ac created!
B@782830e created!
Proxy(delegate=A@4f2410ac) created!
B@782830e: a=Proxy(delegate=A@4f2410ac)
A@4f2410ac: b=B@782830e
a: Proxy(delegate=A@4f2410ac)
b: B@782830e

--------------------------------------------------

class A implements Proxyable { B b; }
class B { }

A@4f2410ac created!
B@782830e created!
A@4f2410ac: b=B@782830e
Proxy(delegate=A@4f2410ac) created!
a: Proxy(delegate=A@4f2410ac)
b: B@782830e

6. 二级缓存

我们只通过一级缓存和三级缓存就解决了循环依赖和AOP的问题,但这么做还是存在一些问题:

  1. 问题在于我们直接将成品和半成品都存放到了一级缓存中
  2. 也就是说,如果用户从一级缓存中获取Bean,有可能获取到的是半成品,就可能引起空指针异常
  3. 因此,还需要再加一个缓存,专门用来存放半成品,而一级缓存则只存放成品
java 复制代码
public class Spring {
    private Map<String, Object> beanMap = new HashMap<>();
    private Map<String, Class> beanDefinitionMap = new HashMap<>();
    {
        beanDefinitionMap.put("a", A.class);
        beanDefinitionMap.put("b", B.class);
    }
    private Map<String, ProxyFactory> proxyFactoryMap = new HashMap<>();

    /**
     * proxyMap中,key为beanName,value为Bean的代理对象(当然也有可能是原始的Bean对象),并且该对象未初始化完成
     */
    private Map<String, Object> proxyMap = new HashMap<>();

    private Spring() throws Exception {
        for (Map.Entry<String, Class> entry : beanDefinitionMap.entrySet()) {
            getBean(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 对原来的getBean()方法进行改进
     */
    private Object getBean(String beanName, Class beanClass) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }

        // 如果二级缓存中有代理对象,说明该Bean的代理对象已经提前创建了,此时将代理对象返回
        // 注意,此时不需要将代理对象从二级缓存移到一级缓存,因为此时的代理对象还未初始化完成
        Object proxy = proxyMap.get(beanName);
        if (proxy != null) {
            return proxy;
        }

        // 如果有代理对象工厂,则提前生成代理对象,将代理对象存放到二级缓存中并返回
        ProxyFactory proxyFactory = proxyFactoryMap.remove(beanName);
        if (proxyFactory != null) {
            proxyMap.put(beanName, proxy = proxyFactory.get());
            return proxy;
        }

        // 创建原始对象
        Object rowBean = beanClass.newInstance();
        System.out.println(rowBean + " created!");

        // 将代理对象的创建逻辑封装到ProxyFactory中
        // 这样的话,如果该Bean有AOP增强,且其它Bean依赖了该Bean,那么其它Bean可以通过该工厂获得该Bean的代理
        proxyFactoryMap.put(beanName, () -> proxyIfNecessary(rowBean));

        // 对原始对象进行依赖注入
        for (Field field : beanClass.getDeclaredFields()) {
            String fieldName = field.getName();
            Object dependency = getBean(fieldName, beanDefinitionMap.get(fieldName));
            System.out.println(rowBean + ": " + fieldName + "=" + dependency);
            field.set(rowBean, dependency);
        }

        // 判断该Bean对应的代理对象工厂是否存在(如果存在,则顺便删除掉)
        // 如果存在,说明代理对象没有提前创建,即没有出现循环依赖,此时创建该Bean的代理对象(如果有必要的话)并返回
        // 如果不存在,说明其它Bean通过代理工厂提前创建了本Bean的代理对象;此时需要返回代理对象(该代理对象肯定在二级缓存中)
        proxyFactory = proxyFactoryMap.remove(beanName);

        // 如果没有循环依赖,则创建代理对象,并将代理对象放入一级缓存,然后返回该代理对象
        if (proxyFactory != null) {
            beanMap.put(beanName, proxy = proxyFactory.get());
            return proxy;
        }

        // 否则,将代理对象从二级缓存转移到一级缓存并返回
        beanMap.put(beanName, proxy = proxyMap.remove(beanName));
        return proxy;
    }

    private Object proxyIfNecessary(Object bean) {
        if (!(bean instanceof Proxyable)) {
            return bean;
        }
        Object beanProxy = Proxy.newProxyInstance(
                bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                (proxy, method, args) -> {
                    if (method.getName().equals("toString")) {
                        return "Proxy(delegate=" + bean.toString() + ")";
                    }
                    return method.invoke(bean, args);
                }
        );
        System.out.println(beanProxy + " created!");
        return beanProxy;
    }

    public static void main(String[] args) throws Exception {
        new Spring().beanMap.forEach((beanName, bean) -> System.out.println(beanName + ": " + bean));
    }
}

结果如下:

text 复制代码
class A implements Proxyable { B b; }
class B { Proxyable a; }

A@4f2410ac created!
B@782830e created!
Proxy(delegate=A@4f2410ac) created!
B@782830e: a=Proxy(delegate=A@4f2410ac)
A@4f2410ac: b=B@782830e
a: Proxy(delegate=A@4f2410ac)
b: B@782830e

--------------------------------------------------

class A implements Proxyable { B b; }
class B { }

A@4f2410ac created!
B@782830e created!
A@4f2410ac: b=B@782830e
Proxy(delegate=A@4f2410ac) created!
a: Proxy(delegate=A@4f2410ac)
b: B@782830e

7. 最终测试

我们用一个更加复杂的例子来测试上述代码:

java 复制代码
class A implements Proxyable { B b; Proxyable d; }
class B { Proxyable a; }
class C implements Proxyable { Proxyable a; B b; Proxyable d; }
class D implements Proxyable { Proxyable d; }
java 复制代码
public class Spring  {
    private Map<String, Class> beanDefinitionMap = new HashMap<>();
    {
        beanDefinitionMap.put("a", A.class);
        beanDefinitionMap.put("b", B.class);
        beanDefinitionMap.put("c", C.class);
        beanDefinitionMap.put("d", D.class);
    }
    
    // 省略其它代码
}

执行结果:

text 复制代码
A@4f2410ac created!                         // 创建a对象
B@782830e created!                          // 对a对象进行依赖注入时,发现需要b对象,因此创建b对象
Proxy(delegate=A@4f2410ac) created!         // 对b对象进行依赖注入时,发现需要a对象,因此提前暴露a的代理对象
B@782830e: a=Proxy(delegate=A@4f2410ac)     // b对象依赖注入成功
A@4f2410ac: b=B@782830e                     // a对象的b字段注入成功
D@3b22cdd0 created!                         // 对a对象进行依赖注入时,发现需要d对象,因此创建d对象
Proxy(delegate=D@3b22cdd0) created!         // 对d对象进行依赖注入时,发现需要d对象,因此提前暴露d的代理对象
D@3b22cdd0: d=Proxy(delegate=D@3b22cdd0)    // d对象底层注入的是d的代理对象
A@4f2410ac: d=Proxy(delegate=D@3b22cdd0)    // a对象的d字段注入成功
C@4d591d15 created!                         // 按照顺序,这里应该创建b对象,但b对象之前已经创建了,因此轮到c对象了
C@4d591d15: a=Proxy(delegate=A@4f2410ac)    // 由于一级缓存中已经有a对象了,因此直接注入给c对象
C@4d591d15: b=B@782830e                     // 由于一级缓存中已经有b对象了,因此直接注入给c对象
C@4d591d15: d=Proxy(delegate=D@3b22cdd0)    // 由于一级缓存中已经有d对象了,因此直接注入给c对象
Proxy(delegate=C@4d591d15) created!         // c对象依赖注入完成,创建其代理对象
a: Proxy(delegate=A@4f2410ac)
b: B@782830e
c: Proxy(delegate=C@4d591d15)
d: Proxy(delegate=D@3b22cdd0)
相关推荐
Amarantine、沐风倩✨28 分钟前
设计一个监控摄像头物联网IOT(webRTC、音视频、文件存储)
java·物联网·音视频·webrtc·html5·视频编解码·七牛云存储
路在脚下@1 小时前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
啦啦右一1 小时前
Spring Boot | (一)Spring开发环境构建
spring boot·后端·spring
森屿Serien2 小时前
Spring Boot常用注解
java·spring boot·后端
苹果醋33 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader3 小时前
深入解析 Apache APISIX
java·apache
菠萝蚊鸭3 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪4 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0074 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生4 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl