关于java:Spring-手撸专栏第-3-章初显身手运用设计模式实现-Bean-的定义注册获取

1次阅读

共计 8595 个字符,预计需要花费 22 分钟才能阅读完成。

作者:小傅哥
博客:https://bugstack.cn

积淀、分享、成长,让本人和别人都能有所播种!😄

一、前言

你是否能预感简单内容的设计问题?

讲道理,无论产品性能是否简单,都有很大一部分程序员会写出一堆 if…else 来实现开发并 顺利 上线。这次要是起因没法预感以后的需要,倒退是否久远、流量是否宏大、迭代是否迅速,所以在被督促上线的状况,不写 if…else 是不可能的!

那你说,既然 if…else 实现的这么快,还思考数据结构、算法逻辑、设计模式、零碎架构吗?当然这根本要看你的我的项目在可预感下能活多久,如果一个我的项目至多存活一年,并且在这一年中又会一直的的迭代。就像;你做了一个营销优惠券零碎,在各种条件下发放各种类型的券,如果在最开始没有思考好零碎设计和架构模式,那么当 流动频发 流量暴增 需要迭代 下、最初你可能会挂在零碎事变上!

咱们在把零碎设计的视角聚焦到具体代码实现上,你会有什么伎俩来实现你想要的设计模式呢?其实编码方式次要依靠于:接口定义、类实现接口、抽象类实现接口、继承类、继承抽象类,而这些操作形式能够很好的隔离开每个类的根底性能、通用性能和业务性能,当类的职责清晰后,你的整个设计也会变得容易扩大和迭代。

接下来在本章节持续欠缺 Spring Bean 容器框架的性能开发,在这个开发过程中会用到较多的接口、类、抽象类,它们之间会有类的实现、类的继承。能够认真参考这部分内容的开发实现,尽管并不会很简单,但这种设计思路是齐全能够复用到咱们本人的业务零碎开发中的。

二、指标

在上一章节《小试牛刀,实现一个简略的 Bean 容器》咱们初步按照 Spring Bean 容器的概念,实现了一个毛糙版本的代码实现。那么本章节咱们须要联合已实现的 Spring Bean 容器进行功能完善,实现 Bean 容器对于 Bean 对象的注册和获取。

这一次咱们把 Bean 的创立交给容器,而不是咱们在调用时候传递一个实例化好的 Bean 对象,另外还须要思考单例对象,在对象的二次获取时是能够从内存中获取对象的。此外不仅要实现性能还须要欠缺根底容器框架的类构造体,否则未来就很难扩容进去其余的性能了。

三、设计

鉴于本章节的案例指标,咱们须要将 Spring Bean 容器欠缺起来,首先十分重要的一点是在 Bean 注册的时候只注册一个类信息,而不会间接把实例化信息注册到 Spring 容器中。那么就须要批改 BeanDefinition 中的属性 Object 为 Class,接下来在须要做的就是在获取 Bean 对象时须要解决 Bean 对象的实例化操作以及判断以后单例对象在容器中是否曾经缓存起来了。整体设计如图 3-1

  • 首先咱们须要定义 BeanFactory 这样一个 Bean 工厂,提供 Bean 的获取办法 getBean(String name),之后这个 Bean 工厂接口由抽象类 AbstractBeanFactory 实现。这样应用模板模式的设计形式,能够对立收口通用外围办法的调用逻辑和规范定义,也就很好的管制了后续的实现者不必关怀调用逻辑,依照对立形式执行。那么类的继承者只须要关怀具体方法的逻辑实现即可。
  • 那么在继承抽象类 AbstractBeanFactory 后的 AbstractAutowireCapableBeanFactory 就能够实现相应的形象办法了,因为 AbstractAutowireCapableBeanFactory 自身也是一个抽象类,所以它只会实现属于本人的形象办法,其余形象办法由继承 AbstractAutowireCapableBeanFactory 的类实现。这里就体现了类实现过程中的各司其职,你只须要关怀属于你的内容,不是你的内容,不要参加。这一部分内容咱们会在代码里有具体的体现
  • 另外这里还有块十分重要的知识点,就是对于单例 SingletonBeanRegistry 的接口定义实现,而 DefaultSingletonBeanRegistry 对接口实现后,会被抽象类 AbstractBeanFactory 继承。当初 AbstractBeanFactory 就是一个十分残缺且弱小的抽象类了,也能十分好的体现出它对模板模式的形象定义。接下来咱们就带着这些设计层面的思考,去看代码的具体实现后果

四、实现

1. 工程构造

small-spring-step-02
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.springframework.beans
    │           ├── factory
    │           │   ├── factory
    │           │   │   ├── BeanDefinition.java
    │           │   │   └── SingletonBeanRegistry.java
    │           │   ├── support
    │           │   │   ├── AbstractAutowireCapableBeanFactory.java
    │           │   │   ├── AbstractBeanFactory.java
    │           │   │   ├── BeanDefinitionRegistry.java
    │           │   │   ├── DefaultListableBeanFactory.java
    │           │   │   └── DefaultSingletonBeanRegistry.java
    │           │   └── BeanFactory.java
    │           └── BeansException.java
    └── test
        └── java
            └── cn.bugstack.springframework.test
                ├── bean
                │   └── UserService.java
                └── ApiTest.java

工程源码 公众号「bugstack 虫洞栈」,回复:Spring 专栏,获取源码

Spring Bean 容器类关系,如图 3-2

尽管这一章节对于 Spring Bean 容器的性能实现与 Spring 源码 中还有不少的差距,但以目前实现后果的类关系图来看,其实曾经具备了肯定的设计复杂性,这些简单的类关系设计在各个接口定义和实现以及在抽象类继承中都有所体现,例如:

  • BeanFactory 的定义由 AbstractBeanFactory 抽象类实现接口的 getBean 办法
  • 而 AbstractBeanFactory 又继承了实现了 SingletonBeanRegistry 的 DefaultSingletonBeanRegistry 类。这样 AbstractBeanFactory 抽象类就具备了单例 Bean 的注册性能。
  • AbstractBeanFactory 中又定义了两个形象办法:getBeanDefinition(String beanName)、createBean(String beanName, BeanDefinition beanDefinition),而这两个形象办法别离由 DefaultListableBeanFactory、AbstractAutowireCapableBeanFactory 实现。
  • 最终 DefaultListableBeanFactory 还会继承抽象类 AbstractAutowireCapableBeanFactory 也就能够调用抽象类中的 createBean 办法了。

综上这一部分的类关系和实现过程还是会有一些简单的,因为所有的实现都以职责划分、共性拆散以及调用关系定义为规范搭建的类关系。这部分内容的学习,可能会丰盛你在简单业务零碎开发中的设计思路。

2. BeanDefinition 定义

cn.bugstack.springframework.beans.factory.config.BeanDefinition

public class BeanDefinition {

    private Class beanClass;

    public BeanDefinition(Class beanClass) {this.beanClass = beanClass;}
        // ...get/set
}
  • 在 Bean 定义类中曾经把上一章节中的 Object bean 替换为 Class,这样就能够把 Bean 的实例化操作放到容器中解决了。如果你有仔细阅读过上一章并做了相应的测试,那么你会发现 Bean 的实例化操作是放在初始化调用阶段传递给 BeanDefinition 构造函数的。

3. 单例注册接口定义和实现

cn.bugstack.springframework.beans.factory.config.SingletonBeanRegistry

public interface SingletonBeanRegistry {Object getSingleton(String beanName);

}
  • 这个类比较简单次要是定义了一个获取单例对象的接口。

cn.bugstack.springframework.beans.factory.config.DefaultSingletonBeanRegistry

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {private Map<String, Object> singletonObjects = new HashMap<>();

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

    protected void addSingleton(String beanName, Object singletonObject) {singletonObjects.put(beanName, singletonObject);
    }

}
  • 在 DefaultSingletonBeanRegistry 中次要实现 getSingleton 办法,同时实现了一个受爱护的 addSingleton 办法,这个办法能够被继承此类的其余类调用。包含:AbstractBeanFactory 以及继承的 DefaultListableBeanFactory 调用。

4. 抽象类定义模板办法(AbstractBeanFactory)

cn.bugstack.springframework.beans.factory.support.AbstractBeanFactory

public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

    @Override
    public Object getBean(String name) throws BeansException {Object bean = getSingleton(name);
        if (bean != null) {return bean;}

        BeanDefinition beanDefinition = getBeanDefinition(name);
        return createBean(name, beanDefinition);
    }

    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;

}
  • AbstractBeanFactory 首先继承了 DefaultSingletonBeanRegistry,也就具备了应用单例注册类办法。
  • 接下来很重要的一点是对于接口 BeanFactory 的实现,在办法 getBean 的实现过程中能够看到,次要是对单例 Bean 对象的获取以及在获取不到时须要拿到 Bean 的定义做相应
    Bean 实例化操作。那么 getBean 并没有本身的去实现这些办法,而是只定义了调用过程以及提供了形象办法,由实现此抽象类的其余类做相应实现。
  • 后续继承抽象类 AbstractBeanFactory 的类有两个,包含:AbstractAutowireCapableBeanFactory、DefaultListableBeanFactory,这两个类别离做了相应的实现解决,接着往下看。

5. 实例化 Bean 类(AbstractAutowireCapableBeanFactory)

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
        Object bean = null;
        try {bean = beanDefinition.getBeanClass().newInstance();} catch (InstantiationException | IllegalAccessException e) {throw new BeansException("Instantiation of bean failed", e);
        }

        addSingleton(beanName, bean);
        return bean;
    }

}
  • 在 AbstractAutowireCapableBeanFactory 类中实现了 Bean 的实例化操作 newInstance,其实这块会埋下一个坑,有构造函数入参的对象怎么解决?能够提前思考
  • 在解决完 Bean 对象的实例化后,间接调用 addSingleton 办法寄存到单例对象的缓存中去。

6. 外围类实现(DefaultSingletonBeanRegistry)

cn.bugstack.springframework.beans.factory.support.DefaultSingletonBeanRegistry

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

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

    @Override
    public BeanDefinition getBeanDefinition(String beanName) throws BeansException {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) throw new BeansException("No bean named'" + beanName + "'is defined");
        return beanDefinition;
    }

}
  • DefaultListableBeanFactory 在 Spring 源码中也是一个十分外围的类,在咱们目前的实现中也是逐渐贴近于源码,与源码类名保持一致。
  • DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory 类,也就具备了接口 BeanFactory 和 AbstractBeanFactory 等一连串的性能实现。所以有时候你会看到一些类的强转,调用某些办法,也是因为你强转的类实现接口或继承了某些类。
  • 除此之外这个类还实现了接口 BeanDefinitionRegistry 中的 registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 办法,当然你还会看到一个 getBeanDefinition 的实现,这个办法咱们文中提到过它是抽象类 AbstractBeanFactory 中定义的形象办法。当初注册 Bean 定义与获取 Bean 定义就能够同时应用了,是不感觉这个套路还蛮深的。接口定义了注册,抽象类定义了获取,都集中在 DefaultListableBeanFactory 中的 beanDefinitionMap 里

五、测试

1. 当时筹备

cn.bugstack.springframework.test.bean.UserService

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

}
  • 这里简略定义了一个 UserService 对象,不便咱们后续对 Spring 容器测试。

2. 测试用例

cn.bugstack.springframework.test.ApiTest

@Test
public void test_BeanFactory(){
    // 1. 初始化 BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    // 2. 注册 bean
    BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
    beanFactory.registerBeanDefinition("userService", beanDefinition);
    // 3. 第一次获取 bean
    UserService userService = (UserService) beanFactory.getBean("userService");
    userService.queryUserInfo();
    // 4. 第二次获取 bean from Singleton
    UserService userService_singleton = (UserService) beanFactory.getBean("userService");
    userService_singleton.queryUserInfo();}
  • 在此次的单元测试中除了包含;Bean 工厂、注册 Bean、获取 Bean,三个步骤,还额定减少了一次对象的获取和调用。这里次要测试验证单例对象的是否正确的寄存到了缓存中。
  • 此外与上一章节测试过程中不同的是,咱们把 UserService.class 传递给了 BeanDefinition 而不是像上一章节那样间接 new UserService() 操作。

3. 测试后果

查问用户信息
查问用户信息

Process finished with exit code 0
  • 这里会有两次测试信息,一次是获取 Bean 时间接创立的对象,另外一次是从缓存中获取的实例化对象。
  • 此外从调试的截图中也能够看到第二次获取单例对象,曾经能够从内存中获取了,如图 3-3
  • 到这本章节的性能实现和测试验证就实现了,对于测试过程中能够再去断点调试下各个阶段类的调用,相熟调用关系。

六、总结

  • 绝对于前一章节对 Spring Bean 容器的简略概念实现,本章节中增强了性能的欠缺。在实现的过程中也能够看到类的关系变得越来越多了,如果没有做过一些略微简单的零碎类零碎,那么即便当初这样 9 个类搭进去的容器工厂也能够给你绕晕。
  • 在 Spring Bean 容器的实现类中要重点关注类之间的职责和关系,简直所有的程序功能设计都离不开接口、抽象类、实现、继承,而这些不同个性类的应用就能够十分好的隔离开类的性能职责和作用范畴。而这样的知识点也是在学习手写 Spring Bean 容器框架过程十分重要的常识。
  • 最初要强调一下对于整个系列内容的学习,可能在学习的过程中会遇到像第二章节那样非常简单的代码实现,但要做一个有成长的程序员要记住代码实现只是最初的落地后果,而那些设计上的思考才是最有价值的中央。就像你是否遇到过,有人让你给一个内容做个形容、文档、阐明,你总感觉太简略了没什么可写的,即便要动笔写了也不晓得要从哪开始!其实这些常识内容都起源你对整体性能的了解,这就不只是代码开发还包含了需要指标、方案设计、技术实现、逻辑验证等等过程性的内容。所以,不要只是被看似简略的内容疏忽了整体全局观,要学会放开视线,凋谢学习视角。

七、系列举荐

  • 《Spring 手撸专栏》开篇介绍,我要带你撸 Spring 啦
  • Spring Bean IOC、AOP 循环依赖解读
  • 90% 的程序员,都没用过多线程和锁,怎么成为架构师?
  • 久等了,小傅哥的《重学 Java 设计模式》终于出版了,彩印 & 纸质
  • 《SpringBoot 中间件设计和开发》| 对,小傅哥的掘金小册上线啦,这次教你造火箭!
正文完
 0