剖析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);  
}
相关推荐
马优晨8 分钟前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
维天说3 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json
Zane19943 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端
杨充4 小时前
11.DDD与战术建模
设计模式·开源·代码规范
杨充4 小时前
12.综合实战图片框架
设计模式·开源·代码规范
噢,我明白了4 小时前
java中Excel的导入和导出(EasyExcel)
java·开发语言·excel
吠品4 小时前
Zabbix Web界面误报Server未运行的排查与解决
java·服务器·数据库
啊湘4 小时前
天气查询API接口 按月Token鉴权 实时天气 物联网可用 文档齐全
java·后端·struts
Java内核笔记5 小时前
告别十亿美元的错误 : Spring Boot 4 空安全 (JSpecify) 实战
java·spring boot·后端
糖果店的幽灵5 小时前
langgraph的 MessagesState 解读
java·开发语言·人工智能·windows·langgraph