Spring 源码分析之AbstractApplicationContext源码分析
首先我觉得分析ApplicationContext必须从它的实现类开始进行分析,AbstractApplicationContext我觉得是一个不错的选择,那我们就从这里开始逐一分析吧,首先我自己手画了一张图,作为索引吧,其中蓝色的为类,紫色的为接口,箭头 指向的方向是父类或者父接口。
因为里面接口和方法过多,所以不做展示,下面具体来进行代码分析。首先我们来看看这句话,MESSAGE_SOURCE_BEAN_NAME。
1 | public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource" ; |
它这句话翻译成中文就是消息资源的bean的一个name,我们暂时把它看成一个普通的beanName,我们来看看有哪些地方引用到了这个属性,首先在initMessageSource方法里面有引用到,我把这些地方标红显示了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | protected void initMessageSource() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { this .messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource. class ); // Make MessageSource aware of parent MessageSource. if ( this .parent != null && this .messageSource instanceof HierarchicalMessageSource) { HierarchicalMessageSource hms = (HierarchicalMessageSource) this .messageSource; if (hms.getParentMessageSource() == null ) { // Only set parent context as parent MessageSource if no parent MessageSource // registered already. hms.setParentMessageSource(getInternalParentMessageSource()); } } if (logger.isTraceEnabled()) { logger.trace( "Using MessageSource [" + this .messageSource + "]" ); } } else { // Use empty MessageSource to be able to accept getMessage calls. DelegatingMessageSource dms = new DelegatingMessageSource(); dms.setParentMessageSource(getInternalParentMessageSource()); this .messageSource = dms; beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this .messageSource); if (logger.isTraceEnabled()) { logger.trace( "No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this .messageSource + "]" ); } } } |
还有一个显示的地方就是在StaticApplicationContext类中的构造器当中有使用到。下面是StaticApplicationContext的类结构图:
1 2 3 4 5 6 7 | public StaticApplicationContext( @Nullable ApplicationContext parent) throws BeansException { super (parent); // Initialize and register a StaticMessageSource. this .staticMessageSource = new StaticMessageSource(); getBeanFactory().registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this .staticMessageSource); } |
我们下面再来看一下AbstractApplicationContext这个类的一些Fields,并且来理清一下对象和对象之间的依赖关系,首先是parent -ApplicationContext,我们找到是一个叫做getParent()的方法对这个私有的属性进行了调用,然后又发现了getParentBeanFactory方法也对其进行了间接调用,因为BeanFactory是ApplicationContext的父接口,如下图:
1 | private ApplicationContext parent; public ApplicationContext getParent() {<br> return this .parent;<br>} public BeanFactory getParentBeanFactory() {<br> return getParent();<br>} |
在这个类中还有一个属性是environment,这个environment在这里也有getter和setter方法,唯一需要注意的是如果调用getEnvironment()方法在environment为空的情况下会创建一个StandardEnvironment对象。
1 | private ConfigurableEnvironment environment; public ConfigurableEnvironment getEnvironment() {<br> if ( this .environment == null ) {<br> this .environment = createEnvironment();<br> }<br> return this .environment;<br>} |
StandardEnvironment类继承了抽象的AbstractEnvironment,它的类结构图如下所示:
还有一个比较重要的属性就是beanFactoryPostProcessors,这事一个ArrayList的数组,Doc当中给出的解释是当onRefresh的时候有用到。我找到了3个方法引用到了这个属性,下面都已标红。
1 | private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>(); public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) { Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null" ); this .beanFactoryPostProcessors.add(postProcessor);} public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() { return this .beanFactoryPostProcessors;} protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); }} |
往下面看还有一个属性active,它是线程安全的,用到了CAS技术。它常常和closed这个属性一起使用,我们发现,在如下3个地方有组合引用,我已用相应的颜色标识出来。
- prepareRefresh
- doClose
- assertBeanFactoryActive
1 | private final AtomicBoolean active = new AtomicBoolean(); private final AtomicBoolean closed = new AtomicBoolean(); protected void prepareRefresh() { // Switch to active. this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); .................... ....................}protected void doClose() { // Check whether an actual close attempt is necessary... if (this.active.get() && this.closed.compareAndSet(false, true)) { if (logger.isDebugEnabled()) { logger.debug("Closing " + this); } ........................ ........................ //Switch to inactive. this.active.set(false);}protected void assertBeanFactoryActive() { if (!this.active.get()) { if (this.closed.get()) { throw new IllegalStateException(getDisplayName() + " has been closed already"); } else { throw new IllegalStateException(getDisplayName() + " has not been refreshed yet"); } }} |
我们继续往下看,有一个startupShutdownMonitor的属性,字面意思上面理解就是启动关闭监视器,属性在这个类当中的命名表示了它所发挥的作用,我们来看一下有哪些方法引用到了这个属性。大家不知道发现没有,还有一个“很像”的属性在这里就是shutdownHook,这个和startupShutdownMonitor是配合在一起使用的。shudownHook在这里是一个线程类型的属性。
1 | private final Object startupShutdownMonitor = new Object(); private Thread shutdownHook; public void refresh() throws BeansException, IllegalStateException { synchronized ( this .startupShutdownMonitor) {......}......} public void registerShutdownHook() { if ( this .shutdownHook == null ) { // No shutdown hook registered yet. this.shutdownHook = new Thread() { @Override public void run() { synchronized (startupShutdownMonitor) { doClose(); } } }; Runtime.getRuntime().addShutdownHook(this.shutdownHook); }}public void close() { synchronized (this.startupShutdownMonitor) { doClose(); // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } }} |
既然shutdownHook和startupShutdownMonitor一起使用,那么它们之间的关系我们得分析一下,hook顾名思义钩子,说简单点这个就是一个钩子,也算是一个扩展点。我们来仔细分析一下它的几个方法,首先是registerShutdownHook方法:这个方法有一句话特别重要,就是Runtime.getRuntime().addShutdownHook(this.shutdownHook);它实际上在系统层面上把钩子线程添加到了JVM虚拟机。在钩子运行的时候,就会执行doClose方法关闭并销毁applicationContext。需要注意的一点是明白registerShutdownHook方法和close方法的不同点,在close方法中如果发现已经调用registerShutdownHook在JVM层面上注册了钩子,那么就调用Runtime.getRuntime().removeShutdownHook(this.shutdownHook)移除此钩子,另外这个close的实现来自于closable接口的父接口AutoClosable接口方法,而registerShutdownHook则在PropertyResolver当中被定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public void <strong>registerShutdownHook</strong>() { if ( this .shutdownHook == null ) { // No shutdown hook registered yet. this .shutdownHook = new Thread() { @Override public void run() { synchronized (startupShutdownMonitor) { doClose(); } } }; Runtime.getRuntime().addShutdownHook( this .shutdownHook); } }<br><br> public void <strong>close</strong>() {<br> synchronized ( this .startupShutdownMonitor) {<br> doClose();<br> // If we registered a JVM shutdown hook, we don't need it anymore now:<br> // We've already explicitly closed the context.<br> if (this.shutdownHook != null) {<br> try {<br> Runtime.getRuntime().removeShutdownHook(this.shutdownHook);<br> }<br> catch (IllegalStateException ex) {<br> // ignore - VM is already shutting down<br> }<br> }<br> }<br>} |
时间不早了,今天就写到这里吧。
__EOF__
作 者:ღKawaii
出 处:https://www.cnblogs.com/kmsfan/p/10805845.html
关于博主:一个普通的小码农,为了梦想奋斗
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!

出处:http://www.cnblogs.com/kmsfan
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
欢迎大家加入KMSFan之家,以及访问我的优酷空间!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?