查看原文
其他

Spring源码(七)-IOC中的那些设计模式我们来一起捋捋

孙亮亮 SpringForAll社区 2021-05-26


前言

接上一篇讲下spring-ioc中的设计模式。Spring作为一款及其优秀的框架,其代码的编写非常优秀,里面采用了大量的设计模式。下面我们一点点分析。
先简单说下常见的设计模式

  • 1、工厂模式

  • 2、单例模式

  • 3、策略模式

  • 4、装饰器模式

参考:设计模式学习

1、工厂模式

【参考】:工厂模式的区别

1.1、定义

工厂模式可将Java对象的调用者从被调用者的实现逻辑中分离出来,调用者只需关心被调用者必须满足的规则(接口),而不必关心实例的具体实现过程。工厂模式由抽象产品(接口)、具体产品(实现类)、生产者(工厂类)三种角色组成。

1.2、SPRING中工厂模式的应用

Spring中在各种BeanFactory以及ApplicationContext创建中都用到了典型的工厂方法模式。ApplicationContext的设计图如下,SpringBean的体系结构比较复杂,顶级接口是BeanFactory;BeanFactory共有三个子接口:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory,还有一个SimpleJndiBeanFactory实现类。这三个子接口集成了顶级接口并对BeanFactory的功能进行了增强,称为二级接口;ConfigurableBeanFactory对二级接口HierarchicalBeanFactory进行了再次增强,它还继承了另一个外来的接口SingletonBeanRegistry,可以被称为三级接口;ConfigurableListableBeanFactory是一个更强大的接口,继承了上述的所有接口,称为四级接口。其余的为抽象类,实现了Spring Bean四级接口所定义的所有功能。

img

2、单例模式

【参考】: 单例

2.1、定义

单例模式具有以下特点:

  • 1、单例类只能有一个实例。

  • 2、单例类必须自己创建自己的唯一实例。

  • 3、单例类必须给所有其他对象提供这一实例

2.2 SPRING中单例模式的使用

在Spring中,所有的bean默认都是单例创建的。在创建bean的代码中我们经常看到Singleton这个单词。下面我们通过代码看看单例是怎么实现的。

1【AbstractBeanFactory】
2protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
3            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException 
{
4·············            
5        //实例化依赖的bean之后可以实例化mbd本身了
6        //单例模式的创建
7        if (mbd.isSingleton()) {
8            sharedInstance = getSingleton(beanName, () -> {
9                try {
10                    /**
11                     *核心创建bean
12                     */

13                    return createBean(beanName, mbd, args);
14                }
15                catch (BeansException ex) {
16                    // Explicitly remove instance from singleton cache: It might have been put there
17                    // eagerly by the creation process, to allow for circular reference resolution.
18                    // Also remove any beans that received a temporary reference to the bean.
19                    destroySingleton(beanName);
20                    throw ex;
21                }
22            });
23            //真正的bean初始化处理
24            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
25        }
26···········                
27}

通过将工厂函数传入getSingleton函数中,就可以获得一个Bean单例。单例的生成是通过修改createBean函数的参数实现的,其中mbd是一个RootBeanDefinition类,它存储了生成Bean实例所需要的信息。在createBean之中的代码里,程序调用实例化Bean的函数initializeBean

3、策略模式

【参考】:策略模式

3.1 定义

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。策略模式实际就是一堆算法族的封装。

3.2 SPRING中策略模式的应用

当bean需要访问资源配置文件时,Spring有两种方式

  • 代码中获取Rescource实例

  • 依赖注入

    第一种方式需要获取rescource资源的位置,代码中耦合性太高,而今我们一直使用注解,依赖注入的方式去获取。这样的话就无需修改程序,只改配置文件即可。

    1<beans> 
    2 <bean id="test" class="com.example.Test"> 
    3 <!-- 注入资源 --> 
    4    <property name="tmp" value="classpath:book.xml"/>
    5 </bean> 
    6</beans>

在依赖注入的过程中,Spring会调用ApplicationContext 来获取Resource的实例。然而,Resource 接口封装了各种可能的资源类型,包括了:UrlResource,ClassPathResource,FileSystemResource等,Spring需要针对不同的资源采取不同的访问策略。在这里,Spring让ApplicationContext成为了资源访问策略的“决策者”。在资源访问策略的选择上,Spring采用了策略模式。当 Spring 应用需要进行资源访问时,它并不需要直接使用 Resource 实现类,而是调用 ApplicationContext 实例的 getResource() 方法来获得资源,ApplicationContext 将会负责选择 Resource 的实现类,也就是确定具体的资源访问策略,从而将应用程序和具体的资源访问策略分离开来。

1ApplicationContext ctx = new Class PathXmlApplicationContext("bean.xml");
2Resource res = ctx.getResource("book.xml");

上面的代码中,Spring 将采用和 ApplicationContext 相同的策略来访问资源。即:ApplicationContext 是 ClassPathXmlApplicationContext,则res 就是 ClassPathResource 实例。若将代码改为:

1ApplicationContext ctx = new Class FileSystemXmlApplicationContext("bean.xml");

则再次调用ctx.getResource时,res 就是 ClassPathResource 实例。

4、装饰器模式

【参考】:装饰器模式

4.1、定义

通过使用修饰模式,可以在运行时扩充一个类的功能。原理是:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数。装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法。修饰类必须和原来的类有相同的接口。

4.2、SPRING中装饰器模式的使用

Spring中类中带有Wrapper的都是包装类,如下创建bean就是典型的装饰器模式

1BeanWrapper instanceWrapper = null;
2if (mbd.isSingleton()) {
3    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
4}
5if (instanceWrapper == null) {
6    //根据指定的bean使用对应的侧脸创建新的实例,如工厂方法,构造函数自动注入,简单初始化
7    instanceWrapper = createBeanInstance(beanName, mbd, args);
8}
9final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
10Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
11mbd.resolvedTargetType = beanType;
12if (beanType != null) {
13    // Allow post-processors to modify the merged bean definition.
14    synchronized (mbd.postProcessingLock) {
15        if (!mbd.postProcessed) {
16            try {
17                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
18            }
19            catch (Throwable ex) {
20                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
21                        "Post-processing of merged bean definition failed", ex);
22            }
23            mbd.postProcessed = true;
24        }
25    }
26}

写在最后

当然Spring-IOC中还有很多的设计模式,比如代理,代理模式会放到AOP源码分析那里去讲解,那里才是代理模式的大量使用。



限时扫码关注领取600页+Spring官方教程小书


  1. 限时3周免费下载

  2. 长按识别二维码关注SpringForAll社区公众号

  3. 在公众号回复“Spring”或者"spring"即可获取



点击原文阅读更多


推荐:人人都是 API 设计者:我对 RESTful API、GraphQL、RPC API 的思考

上一篇:给大家推荐8个SpringBoot精选项目


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存