欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

bean工厂的功能拓展
(last modified Apr 22, 2023, 8:46 PM )
by
侧边栏壁纸
  • 累计撰写 192 篇文章
  • 累计创建 66 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

bean工厂的功能拓展

橙序员
2023-04-22 / 0 评论 / 2 点赞 / 465 阅读 / 2,084 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

诸如前文https://shiker.tech/archives/96所述,spring通过bean工厂替我们管理了我们平时开发中对象创建和属性填充等工作,但是我们实际在开发中,并没有直接使用XmlBeanFactory进行bean的获取或其流程的操作,而是通过创建应用容器(ClassPathXml

诸如前文bean获取&bean工厂职责梳理所述,spring通过bean工厂替我们管理了我们平时开发中对象创建和属性填充等工作,但是我们实际在开发中,并没有直接使用XmlBeanFactory进行bean的获取或其流程的操作,而是通过创建应用容器(ClassPathXmlApplicationContext beanFactory = new ClassPathXmlApplicationContext("spring/application-context.xml");)的方式获取bean。
那么这个ClassPathXmlApplicationContext与bean工厂又什么不同,我们又可以通过它做到哪些bean工厂无法实现的功能呢?

初识应用容器ApplicationContext

首先我们看下应用容器的层级关系:

image-20230422190128812

可以发现应用容器接口在实现了我们所熟知的bean工厂之外,还实现了以下功能接口:

  1. 资源加载器-获取相应的类加载器和资源文件
  2. 环境变量-拓展功能,帮助我们在程序启动时获取对应的环境变量
  3. 国际化消息-拓展功能,帮助我们在做国际化时,快捷的获取不同语言的展示文本
  4. 事件发布器-拓展功能,帮助我们在容器中进行事件发布和监听
  5. 生命周期-拓展功能,容器有了对应的生命周期,我们便可以对指定的容器进行销毁和启动

ApplicationContext的刷新

当我们创建应用容器时,spring会调用应用刷下文的刷新refresh方法,执行如下流程

  • 容器初始化-进行环境变量验证
    • 设置容器的启动时间和容器状态
    • 加载定义环境变量的验证规则
    • 调用自定义验证规则验证环境变量
  • bean工厂初始化-创建bean工厂
    • 这里主要就是解析bean定义,创建bean工厂
  • bean工厂拓展
    • 添加了对xml文件中SPEL语言的解析支持
    • 添加了对属性编辑器,支持属性类型转换
    • 添加了AOP的支持
  • 自定义容器-容器bean后置处理器的添加
  • 调用容器bean后置处理器
    • 执行bean定义注册后处理器
    • 执行容器级bean后置处理器
  • 注册bean后置处理器
  • 注册国际化消息
  • 注册事件多播器
  • 自定义容器-自定义刷新操作
  • 注册事件监听器
  • bean初始化
    • 添加类型转换器
    • 添加嵌入值解析器
    • 初始化AOP bean和单例bean
  • 容器启动完成
    • 加载自定义生命周期,并调用启动方法
    • 发布容器完成事件

代码执行的流程如下所示:

image-20230422201135118

总结以下,通过应用容器我们可以实现的拓展功能有:

  1. 环境变量验证
  2. bean配置支持SPEL
  3. 属性编辑器
  4. 容器级bean后置处理器
  5. bean后置处理器
  6. 国际化消息
  7. 事件的发布与监听
  8. 生命周期监控

ApplicationContext的生命周期

容器的启动,暂停,刷新和关闭我们可以调用容器对应的startstoprefreshclose方法完成。

容器的暂停和恢复很简单,只调用了自定义生命周期的暂停和恢复方法,并发布相应的事件

容器的暂停代码:

	@Override
	public void stop() {
		getLifecycleProcessor().stop();
		publishEvent(new ContextStoppedEvent(this));
	}

容器的恢复代码:

	@Override
	public void start() {
		getLifecycleProcessor().start();
		publishEvent(new ContextStartedEvent(this));
	}

而容器的关闭则需要做如下操作

  1. 发布关闭事件
  2. 关闭所有自定义生命周期
  3. 销毁bean对象
  4. 关闭bean工厂
  5. 自定义容器的操作
  6. 容器状态更新

对应代码则为:

	@Override
	public void close() {
		synchronized (this.startupShutdownMonitor) {
			doClose();
			// 如果我们注册了一个 JVM 关闭挂钩,我们现在就不再需要它了:我们已经明确地关闭了上下文。
			if (this.shutdownHook != null) {
				try {
					Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
				}
				catch (IllegalStateException ex) {
					//忽略 - 虚拟机已经关闭
				}
			}
		}
	}
	
	protected void doClose() {
		if (this.active.get() && this.closed.compareAndSet(false, true)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Closing " + this);
			}

			LiveBeansView.unregisterApplicationContext(this);

			try {
				// 发布关机事件。
				publishEvent(new ContextClosedEvent(this));
			}
			catch (Throwable ex) {
				logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
			}

			// 停止所有 Lifecycle bean,以避免在单个销毁过程中出现延迟。
			if (this.lifecycleProcessor != null) {
				try {
					this.lifecycleProcessor.onClose();
				}
				catch (Throwable ex) {
					logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
				}
			}

			// 销毁上下文的 BeanFactory 中所有缓存的单例。
			destroyBeans();

			// bean工厂置为空
			closeBeanFactory();

			// 自定义容器的其他关闭后操作
			onClose();
			
            //上下文激活状态为关闭
			this.active.set(false);
		}
	}

监听ApplicationContext状态

我们可以通过监听容器在各个阶段发出的事件来完成对容器状态的监听,当然也可以创建自定义容器来在启动和关闭时执行其他操作。例如:

我们先创建自定义容器实现ClassPathXmlApplicationContext, 并重写onRefreshonClose方法:

public class MyApplicationContext extends ClassPathXmlApplicationContext {

    public MyApplicationContext(String configLocation) throws BeansException {
        super(configLocation);
    }

    @Override
    protected void onRefresh() throws BeansException {
        super.onRefresh();
        System.out.println("容器刷新时,其他处理");
    }

    @Override
    protected void onClose() {
        super.onClose();
        System.out.println("容器关闭后,其他处理");
    }
}

然后创建相应的监听器:

public class MyStartListener implements ApplicationListener<ContextStartedEvent> {
    @Override
    public void onApplicationEvent(ContextStartedEvent event) {
        System.out.println("接收事件"+ event);
    }
}

public class MyStopListener implements ApplicationListener<ContextStoppedEvent> {
    @Override
    public void onApplicationEvent(ContextStoppedEvent event) {
        System.out.println("接受事件"+event);
    }
}

public class MyRefreshListener implements ApplicationListener<ContextStartedEvent> {
    @Override
    public void onApplicationEvent(ContextStartedEvent event) {
        System.out.println("接受事件"+event);
    }
}

public class MyClosedListener implements ApplicationListener<ContextClosedEvent> {

    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        System.out.println("接收事件" + event);
    }
}

我们的xml配置为:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 自定义一个bean,用于验证容器各个阶段是否可以正常获取到 -->
	<bean id="a" class="org.jdbc.demo.A">
		<property name="c" value="awqe"/>
		<property name="b">
			<bean class="org.jdbc.demo.A.B">
				<property name="s" value="sds"/>
			</bean>
		</property>
		<property name="date" value="2023-03-24 00:00:00"/>
	</bean>
	<bean id="myListener" class="org.jdbc.demo.MyListener"/>
	<bean id="myClosedListener" class="org.jdbc.demo.MyClosedListener"/>
	<bean id="myRefreshListener" class="org.jdbc.demo.MyRefreshListener"/>
	<bean id="myStartListener" class="org.jdbc.demo.MyStartListener"/>
	<bean id="myStopListener" class="org.jdbc.demo.MyStopListener"/>
</beans>

编写测试类:

public class DemoTest {
	public static void main(String[] args) {
		MyApplicationContext beanFactory = new MyApplicationContext("spring/application-context.xml");
		A a = (A) beanFactory.getBean("a");
		System.out.println(a);
		beanFactory.stop();
		a = (A) beanFactory.getBean("a");
		System.out.println(a);
		beanFactory.start();
		a = (A) beanFactory.getBean("a");
		System.out.println(a);
		beanFactory.close();
		try {
			a = (A) beanFactory.getBean("a");
			System.out.println(a);
		}catch (Exception e){
			System.out.println("获取bean异常");
		}
		beanFactory.refresh();
		a = (A) beanFactory.getBean("a");
		System.out.println(a);
	}
}

//----控制台输出----
//容器刷新时,其他处理
//----当前容器已启动----
//A(c=awqe, b=org.jdbc.demo.A$B@5bcea91b, date=Fri Mar 24 00:00:00 CST 2023)
//----当前容器已关闭----
//接受事件org.springframework.context.event.ContextStoppedEvent[source=org.jdbc.demo.MyApplicationContext@6193b845, started on Sat Apr 22 16:54:04 CST 2023]
//A(c=awqe, b=org.jdbc.demo.A$B@5bcea91b, date=Fri Mar 24 00:00:00 CST 2023)
//----当前容器已启动----
//接受事件org.springframework.context.event.ContextStartedEvent[source=org.jdbc.demo.MyApplicationContext@6193b845, started on Sat Apr 22 16:54:04 CST 2023]
//接收事件org.springframework.context.event.ContextStartedEvent[source=org.jdbc.demo.MyApplicationContext@6193b845, started on Sat Apr 22 16:54:04 CST 2023]
//A(c=awqe, b=org.jdbc.demo.A$B@5bcea91b, date=Fri Mar 24 00:00:00 CST 2023)
//接收事件org.springframework.context.event.ContextClosedEvent[source=org.jdbc.demo.MyApplicationContext@6193b845, started on Sat Apr 22 16:54:04 CST 2023]
//----当前容器已关闭----
//容器关闭后,其他处理
//获取bean异常
//当前加载bean名称为:son
//当前加载bean名称为:son
//容器刷新时,其他处理
//----当前容器已启动----
//A(c=awqe, b=org.jdbc.demo.A$B@7a30d1e6, date=Fri Mar 24 00:00:00 CST 2023)
//
//进程已结束,退出代码0

由此我们可以看到,容器的暂停和恢复,并不影响bean的获取,只会影响我们自定义周期的调用

2

评论区