手写Spring-第二章-实现 Bean 的定义、注册、获取

前言

上一章我们实现了一个简化的Bean容器,那么这一章开始,我们就要上一些强度了。这一章主要的目的是,让Spring容器来自动化实现Bean的创建,并且实现单例Bean的复用。在上一章,我们只实现了一个容器,bean的创建,实际上还是我们在注册bean的时候手动new出来的。那么这次,我们就让Spring容器来帮我们做这些工作。

工程结构

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─akitsuki
│  │  │          └─springframework
│  │  │              └─beans
│  │  │                  └─factory
│  │  │                      │  BeanFactory.java
│  │  │                      │  
│  │  │                      ├─config
│  │  │                      │      BeanDefinition.java
│  │  │                      │      DefaultSingletonBeanRegistry.java
│  │  │                      │      SingletonBeanRegistry.java
│  │  │                      │  
│  │  │                      ├─exception
│  │  │                      │      BeanException.java
│  │  │                      │  
│  │  │                      └─support
│  │  │                              AbstractAutowireCapableBeanFactory.java
│  │  │                              AbstractBeanFactory.java
│  │  │                              BeanDefinitionRegistry.java
│  │  │                              DefaultListableBeanFactory.java
│  │  │                    
│  │  └─resources
│  └─test
│      └─java
│          └─com
│              └─akitsuki
│                  └─springframework
│                      └─test
│                          │  ApiTest.java
│                          │  
│                          └─bean
│                                  UserService.java

光是看工程结构就知道,这次强度比起上次来,要暴涨了!(笑)

一切的开始:竟然是异常?

没有异常的程序,是不存在的。只要是程序,就一定会有不按套路出牌的时候。那对于我们的小Spring框架来说,我们也需要一个属于自己的异常类,这样能够帮助我们更好的对异常进行包装。

package com.akitsuki.springframework.beans.factory.exception;

/**
 * Bean相关的异常
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:14
 */
public class BeanException extends RuntimeException {

    public BeanException(String info) {
        super(info);
    }

    public BeanException(String info, Exception e) {
        super(info, e);
    }
}

嗯,一个很简单的运行时异常,但它是属于我们框架自己的异常,所以还是要给予鼓励~

Bean定义的变化

package com.akitsuki.springframework.beans.factory.config;

/**
 * Bean的定义,将Bean的Class类型注册到这里来
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 9:54
 */
public class BeanDefinition {

    private final Class<?> beanClass;

    public BeanDefinition(Class<?> beanClass) {
        this.beanClass = beanClass;
    }

    public Class<?> getBeanClass() {
        return beanClass;
    }
}

我们可以看到,BeanDefinition不再保存实例化完成的bean了,而是保存了bean的class。这也让它更加符合【bean定义】这个名称。这样,我们就可以将bean的实例化,放在容器中进行,而不是在创建bean定义时就进行。

单例bean注册接口

package com.akitsuki.springframework.beans.factory.config;

/**
 * 单例Bean注册接口
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 9:56
 */
public interface SingletonBeanRegistry {

    /**
     * 获取单例Bean
     *
     * @param beanName bean的名称
     * @return 单例bean
     */
    Object getSingleton(String beanName);

    /**
     * 添加一个单例Bean
     *
     * @param beanName bean的名称
     * @param bean     单例bean
     */
    void addSingleton(String beanName, Object bean);
}

接下来是一个新玩意儿了。我们知道,接口一般是用来规定一个类应该具有哪些功能的。那么这个接口:单例bean注册接口,自然就是用来规定一个类如果要提供单例bean注册功能,需要提供哪些功能的。这里,有2个方法:获取单例bean添加单例bean,都很好理解。

我们知道,Spring中的bean,在默认情况下和大多数情况下,都是单例的。所以,我们需要一个功能,用来添加和获取这些单例bean,这就是这个接口存在的意义。

单例bean注册接口的默认实现

package com.akitsuki.springframework.beans.factory.config;

import java.util.HashMap;
import java.util.Map;

/**
 * 默认的单例Bean注册获取实现
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 9:58
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

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

    @Override
    public Object getSingleton(String beanName) {
        return singletonBeans.get(beanName);
    }

    @Override
    public void addSingleton(String beanName, Object singletonBean) {
        singletonBeans.put(beanName, singletonBean);
    }
}

有接口,自然就会有接口对应的实现类。可以看到,这个类用一个map来保存实例化好的单例bean,key是bean的名称。不由得让人想到上次的factory中,用来保存bean定义的那个map。这里也是相同的一种做法。方法的实现本身很浅显易懂,这里就不过多解释了

BeanFactory的变化

在上一次,我们的BeanFactory是一个具体的类。但实际上,我们的BeanFactory应该是各种各样的,不止一种实现。所以,在这一次,我们将BeanFactory变成了一个接口。对于所有的BeanFactory来说,它们都应该拥有的一个功能,自然是获取bean。所以我们在BeanFactory这个接口中,就定义这么一个功能,具体的内容,则由实现了这个接口的子类来完成。

package com.akitsuki.springframework.beans.factory;

import com.akitsuki.springframework.beans.factory.exception.BeanException;

/**
 * bean工厂接口,用来获取bean
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:03
 */
public interface BeanFactory {

    /**
     * 获取Bean
     *
     * @param beanName bean的名称
     * @return bean
     * @throws BeanException exception
     */
    Object getBean(String beanName) throws BeanException;
}

抽象bean工厂!

这一部分是比较重要的一块。我们先上代码

package com.akitsuki.springframework.beans.factory.support;

import com.akitsuki.springframework.beans.factory.BeanFactory;
import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.config.DefaultSingletonBeanRegistry;
import com.akitsuki.springframework.beans.factory.exception.BeanException;

/**
 * 抽象bean工厂,实现了BeanFactory,提供获取Bean的实现
 * 在获取Bean的方法中,调用了两个抽象方法:获取定义、创建Bean
 * 这两个抽象方法由子类来实现,这里只定义过程
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:04
 */
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

    @Override
    public Object getBean(String beanName) throws BeanException {
        //这里先尝试获取单例Bean,如果可以获取到就直接返回
        Object bean = getSingleton(beanName);
        if (null != bean) {
            return bean;
        }
        //这里是上面获取单例Bean失败的情况,需要先调用抽象的获取bean定义的方法,拿到bean的定义,然后再通过这个来新创建一个Bean
        BeanDefinition beanDefinition = getBeanDefinition(beanName);
        return createBean(beanName, beanDefinition);
    }


    /**
     * 获取Bean的定义
     *
     * @param beanName bean的名字
     * @return beanDefinition
     * @throws BeanException exception
     */
    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeanException;

    /**
     * 创建Bean
     *
     * @param beanName       bean的名字
     * @param beanDefinition bean的定义
     * @return bean
     * @throws BeanException exception
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeanException;
}

有点复杂,我们一点点来分析。

首先,我们可以看到,这是一个抽象类,它继承了上面默认的单例bean注册接口实现类 DefaultSingletonBeanRegistry实现BeanFactory接口。那么这个类就具有了以下功能:

  • 由BeanFactory接口提供的获取bean功能
  • 继承自单例bean注册接口默认实现类的添加、获取单例bean功能

既然是抽象类,自然也会有抽象方法,这个抽象bean工厂也提供了2个抽象方法供子类实现:获取bean定义方法 getBeanDefinition、创建bean方法 createBean

框架介绍完了,我们来思考为什么要这么去设计。这里实际上用到了设计模式中的【模板模式】。模板模式是指,一个抽象类中,有一个【主要的方法】,再定义1~n个方法,可以是抽象的,也可以是实际的。主要方法中会调用这些抽象方法,而这些抽象方法则由具体的子类去实现。那么这些抽象方法,其实就相当于【模板】。主要方法是我们最终想要达成的目的,而达成这个目的,需要一些步骤。在抽象类中,我们只在主要方法里去调用这些步骤,而不管这些步骤具体的实现,具体的实现由具体的子类去完成。就好像要让儿子去打酱油,在主方法中,只需要调用【打酱油】这个抽象方法,而具体打酱油要怎么做,走哪条路,去哪家店,则是由儿子这个子类来实现了。

那我们来具体分析这里的主要方法。很显然,这里的主要方法,自然是实现BeanFactory的获取bean方法。这里首先尝试根据名称,直接获取单例的bean,如果获取到了,那就直接返回。那么如果获取不到呢?我们就要去自己创建一个。怎么创建呢?先要获取bean定义,再根据bean定义来创建。所以下面的步骤就是:根据bean名称,获取bean定义,再通过bean定义来创建一个bean,最终返回创建好的bean。而获取bean定义、创建bean这两个方法都是抽象方法,在这里的感觉就是,步骤已经给你列好了,获取bean都需要经过这几步,具体实现我不管,反正最终能给我一个生产好的bean就行。

让抽象变具体:实现createBean方法!

上面我们的抽象类中定义了createBean这个抽象方法,它总要有人来具体实现。下面这个类,就是用来做这个事情的。

package com.akitsuki.springframework.beans.factory.support;

import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.exception.BeanException;

/**
 * 抽象的实例化Bean类,提供创建Bean的能力,创建完成后,放入单例bean map中进行缓存
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:12
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeanException {
        Object bean;
        try {
            //通过反射来创建Bean
            bean = beanDefinition.getBeanClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeanException("创建bean异常", e);
        }
        //放入单例Bean map中,以备调用
        addSingleton(beanName, bean);
        return bean;
    }
}

我们可以看到,它自己本身也是一个抽象类,继承了上面的抽象bean工厂,并且实现了createBean方法。这里槽点有些多,我们一个一个来解释。

  1. 这个类的名字好奇怪>_<!
    确实很奇怪,但这是为了和Spring保持一致而命名的。在Spring中,的确是由这个类来继承AbstractBeanFactory的,但这个类在现阶段过于简化了,没有体现出本身的功能。在这里可以简单的理解为为了实现createBean方法的一个子类(虽然名字有点怪)
  2. 为什么它也是一个抽象类,搞不懂诶>_<!
    我们可以看到,它只实现了父类的一个抽象方法。如果它是一个普通的类,那就要把两个方法全部实现了。
  3. 为什么不把父类的两个抽象方法都实现了啊,是太懒了吗>_<!
    这里体现出的是一种分层实现的方式,也是单一职责的一种体现。父类提供的2个抽象方法涉及到2个方面:beanbean定义。这个类只应该负责bean这一块,而bean定义,则交给更适合的人去负责,所以它只实现了创建bean的方法。

这里创建bean的实现,是简单的使用反射来实现的,从bean定义中拿到了class,调用了newInstance方法。这里其实无法处理有参构造方法。但这里为了简化,我们先不考虑这种情况,后面再继续完善。

关于bean定义,要做的事:注册接口

上面我们定义了一个接口单例bean注册接口,这个接口规定了注册一个单例bean,需要拥有哪些功能。那么,对于bean定义,是不是也需要这么一个注册接口呢?

package com.akitsuki.springframework.beans.factory.support;

import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.exception.BeanException;

/**
 * 注册Bean定义的接口
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:21
 */
public interface BeanDefinitionRegistry {

    /**
     * 获取bean定义
     *
     * @param beanName bean的名称
     * @return bean定义
     * @throws BeanException exception
     */
    BeanDefinition getBeanDefinition(String beanName) throws BeanException;

    /**
     * 注册Bean定义
     *
     * @param beanName       bean的名称
     * @param beanDefinition bean的定义
     */
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
}

由于和单例bean注册接口类似,这里就不过多解释了。bean定义总是需要先注册,才能使用。这个我们在上一次完成小demo的时候,就已经明确了。所以这里我们用接口来规定bean定义注册时需要的功能:获取、注册。

最终的实现类!

上面我们定义了各种接口,也有各种抽象类或者普通类通过分层实现和继承,各自实现了许多功能。在最后,这些功能都需要落实在一个具体的实现类上。可以说,这个类是一个集大成的类,它通过逐级继承,拥有了前面所有描述的功能!

package com.akitsuki.springframework.beans.factory.support;

import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.exception.BeanException;

import java.util.HashMap;
import java.util.Map;

/**
 * 最终集大成的核心类,通过逐级继承获取到了其他的各种bean操作方法
 * 同时也实现了BeanDefinitionRegistry的注册bean定义、获取bean定义的方法
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:20
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {

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

    @Override
    public BeanDefinition getBeanDefinition(String beanName) throws BeanException {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (null == beanDefinition) {
            throw new BeanException("没有名称为" + beanName + "的Bean定义");
        }
        return beanDefinition;
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }
}

终于到了大boss了。但是这个类看起来意外的很简单。因为它所拥有的功能,都被各个父类进行分担了。而对于它自己来说,则是实现bean定义注册接口里面的两个方法即可。可能这就是继承的力量吧(笑)。

而这里的beanDefinition操作,则与上次练习中的beanFactory类似,也比较好理解,就不多解释了。

最后我们来分析一下这个类最终所拥有的功能,再次感受一下继承的力量吧~

  1. beanDefinition的操作能力:自身实现 BeanDefinitionRegistry接口所拥有
  2. 创建bean:继承自 AbstractAutowireCapableBeanFactory
  3. 获取bean:继承自 AbstractBeanFactory
  4. 注册、获取单例bean:继承自 DefaultSingletonBeanRegistry

嗯嗯,真是了不起。有了这么个集大成的类,我们这次的demo也就算完成了。下面准备进行测试。顺带一提,这个类的名字也很奇怪,但这也是为了和Spring保持一致,原因参照上面的解释吧~

终于,要开始测试了!

经过了上面这些兜兜转转、逐级继承,最终我们有了DefaultListableBeanFactory这么个集万千宠爱于一身的类。还记得这一章我们的目标是什么吗?对,实现Bean的定义、注册、获取,并且让Spring容器来帮我们进行bean的实例化!

首先,来把我们的bean稍微改造一下吧~

package com.akitsuki.springframework.test.bean;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:30
 */
public class UserService {

    public UserService() {
        System.out.println("userService初始化");
    }

    public void queryUserInfo() {
        System.out.println("查询用户信息");
    }
}

其实也没有什么改造,只不过加了个无参的构造方法而已,这样做,是为了更好的看出Spring帮助我们创建bean的过程。

接下来是正式的测试方法!

package com.akitsuki.springframework.test;

import com.akitsuki.springframework.beans.factory.config.BeanDefinition;
import com.akitsuki.springframework.beans.factory.exception.BeanException;
import com.akitsuki.springframework.beans.factory.support.DefaultListableBeanFactory;
import com.akitsuki.springframework.test.bean.UserService;
import org.junit.Test;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/7 10:31
 */
public class ApiTest {

    @Test
    public void testBeanFactory() throws BeanException {
        //初始化BeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //注册Bean
        BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
        beanFactory.registerBeanDefinition("userService", beanDefinition);
        //获取Bean
        UserService userService = (UserService) beanFactory.getBean("userService");
        userService.queryUserInfo();
        //第二次获取Bean
        userService = (UserService) beanFactory.getBean("userService");
        userService.queryUserInfo();
    }
}

测试结果:

userService初始化
查询用户信息
查询用户信息

Process finished with exit code 0

可以看到,在这次的测试中,我们首先初始化了我们那个集大成的bean工厂:DefaultListableBeanFactory。然后,将bean的class作为参数,创建了bean的定义,并注册到了Spring中。接下来,我们只需要简单地对工厂说:我需要一个名为"userService"的bean。就可以等着工厂帮我们生产了。这里从工厂获取了2次,但结果中只初始化了1次。可以看出,在第一次获取bean的时候,工厂会进行新建操作。而第二次进行获取的时候,工厂直接从缓存中将第一次创建的bean进行了返回,这就是单例bean的思想。到这里,我们的第二章手写Spring也要告一段落了。整个工程看起来也要比第一次更加像样了。

相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring,这里对应的代码是mini-spring-02

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值