剖析Spring中的设计模式(一) | 工厂&观察者

目录

Spring中工厂模式的应用

Spring设计理念

Spring中Bean组件定义相关

BeanFactory源码&使用情景

Spring中的FactoryBean

为什么需要FactoryBean?

FactoryBean的使用特点?

使用场景

BeanFactory与FactoryBean区别

BeanFactory和ApplicationContext的区别

Spring中观察者模式应用

Spring中观察者模式

事件机制工作流程

监听器什么时候注册到IOC容器

Spring如何发布的事件并通知监听者


Spring设计模式内容,读者如果阅读过 自己实现Spring简易版解析 或许有不一样的感受哦

Spring中工厂模式的应用

Spring设计理念

  • Spring是面向Bean的编程(BOP:Bean Oriented Programming),Bean在Spring中才是真正的主角。Bean在Spring中作用就像Object对OOP的意义一样,没有对象的概念就像没有面向对象编程,Spring中没有Bean也就没有Spring存在的意义。Spring提供了IoC 容器通过配置文件或者注解的方式来管理对象之间的依赖关系。

  • 控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫"依赖查找"(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。

Spring中Bean组件定义相关

  • Bean的定义 解析Bean的配置信息,封装到BeanDefinition中

  • Bean的创建 使用反射创建bean对象 id

  • Bean的解析 赋值、初始化等操作。

Spring Bean的创建是典型的工厂模式,它的顶级接口是BeanFactory。

职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。目的是为了区分Spring内部对象处理和转化的数据限制

但从图中可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。

Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象

BeanFactory,以Factory结尾,表示它是一个工厂(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是工厂的顶层接口,也是IOC容器的核心接口,因此BeanFactory中定义了管理Bean的通用方法 ,如 getBeancontainsBean 等.

BeanFactory只是个接口,并不是IOC容器的具体实现,所以Spring容器给出了很多种实现,如 DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。

BeanFactory源码&使用情景

  • 从IOC容器中获取Bean(Name or Type)

  • 检索IOC容器中是否包含了指定的对象

  • 判断Bean是否为单例

java 复制代码
public interface BeanFactory {
    
    /**
        对FactoryBean的转移定义,因为如果使用bean的名字来检索FactoryBean得到的是对象是工厂生成的对象,
        如果想得到工厂本身就需要转移
    */
    String FACTORY_BEAN_PREFIX = "&";

    //根据Bean的名字 获取IOC容器中对应的实例
    Object getBean(String var1) throws BeansException;
    
    //根据Bean的名字和class类型得到bean实例,增加了类型安全验证机制
    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);

    
   //查看Bean容器中是否存在对应的实例,存在返回true 否则返回false
    boolean containsBean(String var1);

    //根据Bean的名字 判断这个bean是不是单例
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    //得到bean实例的class类型
    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;    
    //得到bean的别名
    String[] getAliases(String var1);
}

Spring中的FactoryBean

FactoryBean是一个Bean,但又不仅仅是一个Bean,这样听起来矛盾,但为啥又这样说呢?其实在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个FactoryBean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂方法模式和装饰器模式类似

java 复制代码
public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

    /**
    getObject()方法: 会返回该FactoryBean生产的对象实例,我们需要实现该方法,以给出自己的对象实例化逻辑
    这个方法也是FactoryBean的核心.
    */
    @Nullable
    T getObject() throws Exception;

    /**
    getObjectType()方法: 仅返回getObject() 方法所返回的对象类型,如果预先无法确定,返回NULL,
    这个方法返回类型是在IOC容器中getBean所匹配的类型
    */
    @Nullable
    Class<?> getObjectType();

    //该方法的结果用于表明 工厂方法getObject() 所生产的 对象是否要以单例形式存储在容器中如果以单例存在就返回true,否则返回false
    default boolean isSingleton() {
        return true;
    }
}

为什么需要FactoryBean?

  • 在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。

  • 由于第三方库不能直接注册到spring容器,于是可以实现org.springframework.bean.factory.FactoryBean接口,然后给出自己对象的实例化代码即可。

FactoryBean的使用特点?

  • 当用户使用容器本身时,可以使用转义字符"&"来得到FactoryBean本身,以区别通过FactoryBean产生的实例对象和FactoryBean对象本身。

FactoryBean表现的是一个工厂的职责,如果一个BeanA 是实现FactoryBean接口,那么A就是变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()方法返回的对象,而不是对象本身,如果想获取工厂对象本身,需要在名称前面加上 '&'符号

  • getObject('name') 返回的是工厂中工厂方法生产的实例

  • getObject('&name') 返回的是工厂本身实例

使用场景

  • FactoryBean的最为经典的使用场景,就是用来创建AOP代理对象,这个对象在Spring中就是 ProxyFactoryBean

BeanFactory与FactoryBean区别

  • 他们两个都是工厂,但是FactoryBean本质还是一个Bean,也归BeanFactory管理

  • BeanFactory是Spring容器的顶层接口,FactoryBean更类似于用户自定义的工厂接口

BeanFactory和ApplicationContext的区别

  • BeanFactory是Spring容器的顶层接口,而ApplicationContext应用上下文类 他是BeanFactory的子类,他是Spring中更高级的容器,提供了更多的功能

    • 国际化

    • 访问资源

    • 载入多个上下文

    • 消息发送 响应机制

  • 两者的装载bean的时机不同

    • BeanFactory: 在系统启动的时候不会去实例化bean,只有从容器中拿bean的时候才会去实例化(懒加载)

      • 优点: 应用启动的时候占用的资源比较少,对资源的使用要求比较高的应用 ,比较有优势
    • ApplicationContext:在启动的时候就把所有的Bean全部实例化。

      • lazy-init= true 可以使bean延时实例化

      • 优点: 所有的Bean在启动的时候就加载,系统运行的速度快,还可以及时的发现系统中配置的问题。

Spring中观察者模式应用

观察者模式(发布-订阅(Publish/Subscribe)模式)它是用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应的作出反应。

在观察者模式中发生改变的对象称为观察目标 ,而被通知的对象称为观察者,一个观察目标可以应对多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。

观察者和被观察者,是松耦合的关系;发布者和订阅者,则完全不存在耦合。

  • 在设计模式结构上,发布订阅模式继承自观察者模式,是观察者模式的一种实现的变体。

  • 在设计模式意图上,两者关注点不同,一个关心数据源,一个关心的是事件消息。

    • 观察者模式:数据源直接通知订阅者发生改变。

    • 发布订阅模式:数据源告诉第三方(事件通道)发生了改变,第三方再通知订阅者发生了改变。

Spring中观察者模式

Spring 基于观察者模式,实现了自身的事件机制也就是事件驱动模型,事件驱动模型通常也被理解成观察者或者发布/订阅模型。

Spring事件模型提供如下几个角色

  • 【事件】ApplicationEvent:是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都需要继承 ApplicationEvent, 并且通过 source 得到事件源。

    • ContextRefreshEvent,当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件。

    • ContextStartedEvent,当ApplicationContext启动的时候发布事件.

    • ContextStoppedEvent,当ApplicationContext容器停止的时候发布事件.

    • RequestHandledEvent,只能用于DispatcherServlet的web应用,Spring处理用户请求结束后,系统会触发该事件。

  • 【事件监听】ApplicationListener:

    • ApplicationListener(应用程序事件监听器) 继承自jdk的EventListener,所有的监听器都要实现这个接口,这个接口只有一个onApplicationEvent()方法,该方法接受一个ApplicationEvent或其子类对象作为参数

    • 在方法体中,可以通过不同对Event类的判断来进行相应的处理.当事件触发时所有的监听器都会收到消息,如果你需要对监听器的接收顺序有要求,可是实现该接口的一个实现SmartApplicationListener,通过这个接口可以指定监听器接收事件的顺序。

    • 实现了ApplicationListener接口之后,需要实现方法onApplicationEvent(),在容器将所有的Bean都初始化完成之后,就会执行该方法。

  • 【事件源】ApplicationEventPublisher:事件的发布者,封装了事件发布功能方法接口,是Applicationcontext接口的超类

    • 事件机制的实现需要三个部分,事件源,事件,事件监听器,ApplicationEvent就相当于事件,ApplicationListener相当于事件监听器,ApplicationEventPublisher相当于时间源。

    • 我们常用的ApplicationContext都继承了AbstractApplicationContext,像我们平时常见ClassPathXmlApplicationContext、XmlWebApplicationContex也都是继承了它,AbstractApplicationcontext是ApplicationContext接口的抽象实现类,在该类中实现了publishEvent方法

  • 【事件管理】ApplicationEventMulticaster:用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。

    java 复制代码
    public interface ApplicationEventMulticaster {
        
        //添加事件监听器
        void addApplicationListener(ApplicationListener<?> var1);
    
        //添加事件监听器,使用容器中的bean
        void addApplicationListenerBean(String var1);
    
        //移除事件监听器
        void removeApplicationListener(ApplicationListener<?> var1);
    
        void removeApplicationListenerBean(String var1);
    
        //移除所有
        void removeAllListeners();
    
        //发布事件
        void multicastEvent(ApplicationEvent var1);
    
        void multicastEvent(ApplicationEvent var1, @Nullable ResolvableType var2);
    }
    // 在AbstractApplicationcontext中有一个applicationEventMulticaster
    // 的成员变量,
    // 提供了监听器Listener的注册方法
    public abstract class AbstractApplicationContext extends DefaultResourceLoader
            implements ConfigurableApplicationContext, DisposableBean {
    
      private ApplicationEventMulticaster applicationEventMulticaster;
        
      protected void registerListeners() {
            // Register statically specified listeners first.
            for (ApplicationListener<?> listener : getApplicationListeners()) {
                getApplicationEventMulticaster().addApplicationListener(listener);
            }
            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let post-processors apply to them!
            String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
            for (String lisName : listenerBeanNames) {
                getApplicationEventMulticaster().addApplicationListenerBean(lisName);
            }
        }
    }

事件机制工作流程

监听器什么时候注册到IOC容器

注册的开始逻辑是在AbstractApplicationContext类的refresh方法,该方法包含了整个IOC容器初始化所有方法。其中有一个registerListeners()方法就是注册系统监听者(Spring自带的)和自定义监听器的。

java 复制代码
public void refresh() throws BeansException, IllegalStateException {
    // BeanFactory准备工作完成后进行的后置处理工作
    this.postProcessBeanFactory(beanFactory);

    // 执行BeanFactoryPostProcessor的方法;
    this.invokeBeanFactoryPostProcessors(beanFactory);

    // 注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
    this.registerBeanPostProcessors(beanFactory);

    // 初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
    this.initMessageSource();

    // 初始化事件派发器
    this.initApplicationEventMulticaster();

    // 子类重写这个方法,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
    this.onRefresh();

    // 注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean,这些监听器是注册到ApplicationEventMulticaster中的
    this.registerListeners();

    // 初始化所有剩下的非懒加载的单例bean
    this.finishBeanFactoryInitialization(beanFactory);

    // 完成context的刷新
    this.finishRefresh();
    }

看registerListeners的关键方法体,其中的两个方法addApplicationListener和addApplicationListenerBean,从方法可以看出是添加监听者。

java 复制代码
protected void registerListeners() {
    Iterator var1 = this.getApplicationListeners().iterator();

    while(var1.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var1.next();
        this.getApplicationEventMulticaster().addApplicationListener(listener);
    }

    String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
    String[] var7 = listenerBeanNames;
    int var3 = listenerBeanNames.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        String listenerBeanName = var7[var4];
        this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }
}

该接口主要两个职责,维护ApplicationListener相关类和发布事件。实现在默认实现类AbstractApplicationEventMulticaster,最后将Listener放到了内部类ListenerRetriever两个set集合中

java 复制代码
private class ListenerRetriever {
        public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
        public final Set<String> applicationListenerBeans = new LinkedHashSet();
}

ListenerRetriever被称为监听器注册表。

Spring如何发布的事件并通知监听者

这个注意的有两个方法

publishEvent方法

  • AbstractApplicationContext实现了ApplicationEventPublisher 接口的publishEvent方法
复制代码
java 复制代码
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    Assert.notNull(event, "Event must not be null");
    Object applicationEvent;
    
    //尝试转换为ApplicationEvent或者PayloadApplicationEvent,如果是PayloadApplicationEvent则获取eventType
    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent)event;
    } else {
        applicationEvent = new PayloadApplicationEvent(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
        }
    }

   
    if (this.earlyApplicationEvents != null) {
         //判断earlyApplicationEvents是否为空(也就是早期事件还没有被发布-说明广播器还没有实例化好),如果不为空则将当前事件放入集合
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        //否则获取ApplicationEventMulticaster调用其multicastEvent将事件广播出去。本文这里获取到的广播器实例是SimpleApplicationEventMulticaster。
        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
    }
        
    //将事件交给父类处理
    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event);
        }
    }

}

multicastEvent方法 继续进入到multicastEvent方法,该方法有两种方式调用invokeListener,通过线程池和直接调用,进一步说就是通过异步和同步两种方式调用.

java 复制代码
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    
    //解析事件类型
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    
    //获取执行器
    Executor executor = this.getTaskExecutor();
    
    // 获取合适的ApplicationListener,循环调用监听器的onApplicationEvent方法
    Iterator var5 = this.getApplicationListeners(event, type).iterator();

    while(var5.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var5.next();
        if (executor != null) {
            //如果executor不为null,则交给executor去调用监听器
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            //否则,使用当前主线程直接调用监听器;
            this.invokeListener(listener, event);
        }
    }

}

invokeListener方法

java 复制代码
// 该方法增加了错误处理逻辑,然后调用doInvokeListener
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    ErrorHandler errorHandler = this.getErrorHandler();
    if (errorHandler != null) {
        try {
            this.doInvokeListener(listener, event);
        } catch (Throwable var5) {
            errorHandler.handleError(var5);
        }
    } else {
        this.doInvokeListener(listener, event);
    }

}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    //直接调用了listener接口的onApplicationEvent方法
    listener.onApplicationEvent(event);  
}
相关推荐
小猪乔治爱打球11 分钟前
[Golang修仙之路]单例模式
设计模式
我爱布朗熊11 分钟前
5.Elasticsearch - Spring Data 框架
spring·elasticsearch·jenkins
LCY13320 分钟前
k8s 部署spring项目+动态启动pod
spring·容器·kubernetes
XW-ABAP24 分钟前
c语言练习4
java·c语言·前端
异常君27 分钟前
【爆肝整理】Java 泛型深度解析:从类型擦除到通配符,一文搞懂 PECS 原则与实战避坑指南
java·后端
胚芽鞘68136 分钟前
idea+vue3+mybatis+springBoot3前后端分离实现对一张表的增删改查(完整代码版)
java·ide·intellij-idea
Charlie__ZS1 小时前
Spring Cloud-负载均衡
spring·spring cloud·负载均衡
猫咪-95271 小时前
数据库原理及应用mysql版陈业斌实验三
java·数据库·sql
异常君1 小时前
深入剖析 Java ReentrantLock:解锁显式锁的高级特性与实战应用
java·后端
写bug写bug1 小时前
彻底搞懂如何通过 ZooKeeper 实现注册中心
java·后端·zookeeper