从一个简单的例子看spring ApplicationContext上下文隔离

前言



  某天,浏览博客园的时候,对首页上面的一篇文章,标题为:<<一个普通类就能干趴你的springboot,你信吗?>>,文章链接:https://www.cnblogs.com/rongdi/p/11780204.html#4414216 很是感兴趣。点进去之后,大致看一下。该篇博文主要说的是在使用spring boot环境下想创建一个名为Environment的bean,结果发现创建不了,于是不断调试终于找到了“真理”。

  说真的。这篇博文的内容非常长,主要也是记录调试过程的“流水账”。我也只看到了看头,就迅速拉到文章结尾看一下。比较让我感到震惊的是,博主提到为了写这篇文章花费了很长的时间,从该博主的这篇文章中摘录了一句话:周五晚上从下班回家一边一步步断点一遍写这篇博客。可以看出该博主很是用心,调试程序是一件很费心,耗时的事。于是评论区送上一个大大的赞。

  无论是调试还是阅读Spring源码,真的是一件很枯燥的事,非常考验人的耐心,由其是spring发展了这么多年,已经形成了生态圈。其代码也是高度抽象。曾经对spring进行过一番折腾,也是为了给自己所在小团队提供一个基于spring封装的迷你型的小框架。

  由于有折腾过spring的经历,我一眼就看出了问题的所在。根本原因就在于spring框架自身也有一个Environment类,在应用程序启动时也会向spring ApplicationContext中注入名为environment的bean,这样就会跟博主命名Environment类注入名为environment的bean产生冲突,因为这两个bean的名称一样。

  像BAT这样级别的公司往往内部或多或少都会有自己的框架,这些框架往往都是由一个类似于基础架构部的团队来负责提供的,这样应用开发小组会基于这个框架快速的开发应用。应用开发者一般只会关心如何使用框架,一般都由专门的人来折腾框架。虽然我没有在这样体量的公司里面工作过,鉴于之前折腾框架经验来看。框架无论多么的高大上,有一点是可以肯定的时,框架所使用的资源跟应用所使用的资源肯定会进行隔离。

  为什么会这样说呢,打个比方。封装框架的过程中肯定会引入一些第三方的jar,应用在开发的过程也会引用第三方jar,假设框架和应用同时引用一个jar但是二者的版本不同?那可能会导致程序在运行的过程中搞不好就会出现 java.lang.NoSuchMethodException异常,包冲突了。这时该怎么办?如果要框架jar跟应用的jar保持一致,那就不得了,这么多应用都使用框架进行开发,牵一发而动全身,风险即大。如果应用使用跟框架一样的jar,但是这个jar又没有相应的方法,使用不了。可以想象一样,如果框架和应用没有分别定义自身的类加载器来加载各自的所引用的jar,遇到这样的场景,解决起来将会非常棘手。

  所以在封装框架的过程中,都会对框架所引用的资源跟应用所使用的资源都要进行相应的隔离,如果不隔离的话,框架三天两天就要改动,对应用开发者来说就会认为框架非常的不稳定。由其在BAT这样大体量的公司,开发人员如此众多,对框架提供技术支撑的人不会很多,框架如果不够稳定的话,搞不好那么那些提供技术支撑人员的电话,每天都会响个不停,会到处去解决问题,疲于奔命。就像那篇文章提到定义Envrionment bean时候,应用就会跑起不来,打电话给技术支撑的人,人家一过来捣鼓一番对像你说,老兄,对不住啊,命名冲突了,换这个名字吧。也许重新命个名字了事,但是有些场景这个类的名字改不了,别人的代码已经固定了要使用这个名字来调用你的bean,改名字别人就调不了。重新命名也许可以解决问题,但内心深处,你会对这个框架失去信心了。什么框架,还限制别人bean的名字。

  像spring框架就提供了对ApplicationContext进行隔离的功能,可以轻松解决这个问题。在spring官网的文档中我也没有看到有提到,不允许应用程序注入一个命名为Environment的Bean。

程序出错



我已经将复现同样错误的示例程序代码上传到了gitee上面。大家可以把代码拉下来,跑起来会出现跟那篇文章中提所到的一模一样错误。 出现这样错误的原因就是由于两个同名的类注入到同一个ApplicationContext中导致的。

示例代码链接:https://gitee.com/fiercetiger/laboratory/tree/master/applicationcontext-test

导致程序出错,Bean的源码如下所示:

1 @Component
2 public class Environment {
3 }

ApplicationContext隔离



  spring ApplicationContext是可以设置成上下级关系的,查找bean的时候如果在当前的ApplicationContext中没有找到的话,就会到自己的父级的ApplicationContext中去查找,一直向上回溯,如果找到就会返回。这样一来的话,我们可以这样处理。让应用的ApplicationContext作为spring框架的ApplicationContext的父级。示例程序,我也提交到了gitee上面,可以把代码拉下来,跑一下就会发现没有报错。

示例代码链接:https://gitee.com/fiercetiger/laboratory/tree/master/applicationcontext-test2

  关键代码如下所示,定义一个类继承spring boot的SpringApplication类,覆盖其createApplicationContext方法,在方法中首先创建应用的ApplicationContext,并注入应用所定义的Environment Bean,随后将其设置为spring boot ApplicationContext的父级。为了更好的演示向上回溯查找Bean的效果,特意定义了一个MyService Bean,这个Bean注入到spring boot ApplicationContext中,并且在MyService Bean中自动注入对应用所定义的Environment Bean的依赖。当应用程序启动之后,没有报错。说明了MyService Bean成功注入了父级的application context中所定义的Environment Bean

 1 import org.springframework.boot.SpringApplication;
 2 import org.springframework.context.ConfigurableApplicationContext;
 3 import org.springframework.context.support.StaticApplicationContext;
 4
 5 public class MySpringApplication extends SpringApplication {
 6
 7     public MySpringApplication(Class<?>[] classes){
 8         super(classes);
 9     }
10
11     @Override
12     protected ConfigurableApplicationContext createApplicationContext(){
13
14         StaticApplicationContext parent=new StaticApplicationContext();
15         parent.registerBean(Environment.class);
16         parent.refresh();
17
18         ConfigurableApplicationContext child=super.createApplicationContext();
19         child.setParent(parent);
20
21         return child;
22     }
23 }

结尾



  写这篇文章的旨在分享有关spring ApplicationContext 一个小小的知识点,Spring所涉及到的知识点非常庞杂。那篇文章的博主为了弄清楚问题的真相,花费大量的程序来调试程序,还花了大篇幅的文章记录下来,可以看到出该博主是一个对技术有着执着追求的人。这篇文章也完整呈现了我在那篇文章评论区中所提到,可以采用对applicationcontext进行分层来解决这一问题。

原文地址:https://www.cnblogs.com/yql1986/p/11805772.html

时间: 2024-10-10 08:14:02

从一个简单的例子看spring ApplicationContext上下文隔离的相关文章

一个简单的例子看明白如何利用window.location.hash实现ajax操作时浏览器的前进/后退功能

我们知道JavaScript中很早就提供了window.history对象,利用history对象的forward().go().back()方法能够方便实现不同页面之间的前进.后退等这种导航功能.但是AJAX操作,是不能用浏览器的前进和后退按钮进行导航的,因为浏览器并不会将AJAX操作加入到历史记录中.但是借助location.hash,我们能够自己实现AJAX操作的前进和后退.关于window.location.hash的详细介绍和使用方式,可以参考下面这2篇文章. location.has

Android Handler的一个简单使用例子

在前面 开启一个线程Thread并用进度条显示进度 这篇文章里,我们用线程实现了这么一个简单的功能,就是点击按钮,加载进度条.但是有没有发现,点击一次之后,再次点击就会没效.我们可是需要每次点击都要显示下一张图片的.永盈会娱乐城 这里就需要引入 Android 的消息机制了,简单来说,就是 Handler.Looper 还有 Message Queue的使用.这里我们用一个简单的例子来说明 Handler 的使用,就是每次点击按钮,给消息队列发送一个数字 5.还是在 PaintingActivi

Linux内核中的信号机制--一个简单的例子【转】

本文转载自:http://blog.csdn.net/ce123_zhouwei/article/details/8562958 Linux内核中的信号机制--一个简单的例子 Author:ce123(http://blog.csdn.NET/ce123) 信号机制是类UNIX系统中的一种重要的进程间通信手段之一.我们经常使用信号来向一个进程发送一个简短的消息.例如:假设我们启动一个进程通过socket读取远程主机发送过来的网络数据包,此时由于网络因素当前主机还没有收到相应的数据,当前进程被设置

【Python】一个简单的例子

问题描述: Python基础篇 参考资料: (1)http://www.cnblogs.com/octobershiner/archive/2012/12/04/2801670.html (2)http://www.cnblogs.com/itech/archive/2010/06/20/1760345.html 例子: 求解Fibonacci glb_var.py gl_count=1 path.py # coding:utf-8 ''' Created on 2014-4-28 @autho

从一个简单的约束看规范性的SQL脚本对数据库运维的影响

原文:从一个简单的约束看规范性的SQL脚本对数据库运维的影响 之前提到了约束的一些特点,看起来也没什么大不了的问题,http://www.cnblogs.com/wy123/p/7350265.html以下以实际生产运维中遇到的一个问题来说明规范的重要性. 如下是一个简单的建表脚本,表面上看起来并没有什么问题.其中创建了3个约束,一个主键约束,一个唯一约束,一个默认值约束,该脚本执行起来没有任何问题. USE Test GO if exists(select 1 from sys.tables

关于apriori算法的一个简单的例子

apriori算法是关联规则挖掘中很基础也很经典的一个算法,我认为很多教程出现大堆的公式不是很适合一个初学者理解.因此,本文列举一个简单的例子来演示下apriori算法的整个步骤. 下面这个表格是代表一个事务数据库D,其中最小支持度为50%,最小置信度为70%,求事务数据库中的频繁关联规则. Tid 项目集 1  面包,牛奶,啤酒,尿布 2  面包,牛奶,啤酒 3  啤酒,尿布 4  面包,牛奶,花生 apriori算法的步骤如下所示: (1)生成候选频繁1-项目集C1={{面包},{牛奶},{

一个简单的例子让你了解React-Redux

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 一个简单的例子让你了解React-Redux - 小平果的欢乐谷 - 博客频道 - CSDN.NET 小平果的欢乐谷 你的到来会让我很意外,谢谢光临! 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &nbsp [5月书讯]流畅的Python,终于等

duilib DirectUI库里面的一个简单的例子RichListDemo

http://blog.csdn.net/zengraoli/article/details/9993153 2013-08-16 00:08 3289人阅读 评论(2) 收藏 举报 目录(?)[+] 1.首先来看这里的CRichListWnd 已经不再是从CWindowWnd继承了 classCRichListWnd:publicWindowImplBase 从WindowImplBase中,可以看到有三个抽象函数: virtualCDuiStringGetSkinFolder()=0; vi

一个简单的例子搞懂ES6之Promise

ES5中实现异步的常见方式不外乎以下几种: 1. 回调函数 2. 事件驱动 2. 自定义事件(根本上原理同事件驱动相同) 而ES6中的Promise的出现就使得异步变得非常简单.promise中的异步是这样的: * 每当我需要执行一次异步操作的时候,我都需要new一个promise对象 * 每一个异步操作的Promise对象都需要设定一个成功执行的条件和成功的回调.一个失败的条件和失败的回调 * Promise对象可通过执行then()方法获得成功的回调信息 * Promise对象可通过执行ca