`
ftj20003
  • 浏览: 130475 次
  • 性别: Icon_minigender_1
  • 来自: ...
社区版块
存档分类
最新评论

由destory-method引发的IOC容器设计的思考

    博客分类:
  • Java
阅读更多
   第一次读Spring的源码大概是半年前写硕士论文的时候,那时侯只是因为想从中看看如何运用设计模式处理特定的问题,同时也对这个被追捧和广泛使用的框架内部的实现很好奇。期间结合着2.5的reference和CodeLogic分析工具看了其IOC,AOP,事务,三方集成的源码,迷迷糊糊的。这样看下来感觉好像知道点什么,但最直接的感受是盲人摸象,知之甚少,有种时间被浪费的感觉。最近对其destory-method的实现产生了好奇,带着问题出发再次看相关的IOC的设计实现,还是得到了一些感悟。

   对于IOC容器的设计,我想多数人的思路都是大同小异的:
  (1)把bean的属性,依赖,特征信息以编码或者文件的形式描述,毕竟要伸手向IOC容器要对象,总要告诉人家你中意的是什么样的吧,呵呵。
  (2)容器开始工作前,把这些bean的信息加载进来仔细的分析,为每个bean所拥有的信息找个存储方式,这时候我想没太多人还选择文件存储了,不然一个bean的信息对应产生一个小文件?主要是编码的方式实现信息存储,也就是new出相应的信息存储辅助对象,Spring中使用BeanDefinition存储每个bean解析后的配置信息。
  (3)这一步对于设计者来说,要首先考虑到是否在容器初始化执行,至少要把开关选择交给用户决定,或者提供多种实现的选择。也就是bean的初始化。毕竟对于实际的项目来说,如果bean过于繁多,特别是仅仅需要测试其中某个模块涉及到的类的功能的话,需要时再初始化是最基础的要求。在Spring中构造bean的工厂有不同的实现选择。使用相关BeanFactory的话,只有getBean(args)的时候才会实例化相应的bean;而使用相关ApplicationContext的话,其默认有refresh()方法会在注册,验证完成之后实例化singleton的bean。通过这三步的实现,IOC容器的基本功能算是具备了,但是多数的singlton的实例,总不能说我给你了,你看着办吧?所以IOC还应该对这些bean生命周期负责,最起码的:我既然能把你造出来,也能把你毁灭。太暴力了,不和谐了...

   具备以上功能的IOC容器才算是一个理论上能用的容器,但是具体的设计时,可扩展性,灵活性,高性能才是衡量一个IOC优劣的重点。目前IOC容器也不少了,被证明能当重任的,除了Spring赖以成名的IOC容器外,还有Goole的Guice,后者说是使用JDK5以后的注释加反射实现,性能比Spring要好很多。没用过,没看过源码,更没有亲自的测试性能,所以没有根据不做评论(接着我会看看其源码的实现,有那么好?)。当然还有广大的程序员们自己根据项目的需求实现的IOC容器,但是不管怎么说,实现原理应该和上面总结的有大面积交集吧?

   下面看Spring,在为具体bean配置信息时,我们可以定义destory-method用于当bean被销毁时需要执行的方法。对于一些占用资源的bean是需要定义释放资源的实现的。但是这个method的名字我们可以随便的根据业务去命名,IOC容器是怎么去调用的呢?我想第一反应应该是反射吧,最终实现是用的反射,但是其内部如何做到设计的统一才是重点。下面具体的结合相应的实现代码去看看。

   对于一个bean的实例化过程,个人认为Spring是划分的非常细致:
  (1)设置对象的属性,具体的实现挺复杂的
  (2)a.检查是否实现Aware相关的接口,设置具体的依赖对象;b.应用BeanPostProcessor的前置处理;c.初始化方法的处理;d.应用BeanPostProcessor后置处理。这四个小过程在AbstractAutowireCapableBeanFactory的createBean()-->doCreateBean()-->initializeBean()方法中实现。其中的c步骤是在invokeInitMethods()里判断到底是bean实现了InitializingBean接口,调用其afterPropertiesSet()还是直接反射调用init-method方法(如果有...)。
  (3)把bean注册成可销毁的(disposable),通过doCreateBean()-->registerDisposableBeanIfNecessary()方法,这样就算完成了一个bean的实例初始化。
   精细的过程保证了其强大的灵活性和可扩展性,利用BeanPostProcessor扩展可以在最终的bean诞生前改变bean的属性,Spring的IOC提供多个改变的机会。个人认为想自己设计IOC容器的,或者正在设计IOC容器的,把关注点放在是使用xml,properties描述呢,还是json,java等等描述信息的存储上,如果能提高性能得或者满足动态修改得话固然值得。但是更要关注的应该是划分精细,层次分明的实例化的过程和销毁的过程。这直接决定你的IOC容器是否拥有强大的生命力,总不能满足不了改变和扩展而重新架构吧(重构部分代码到无妨,但是重打地基只能说明设计的失败)...

  接着从小见大,初窥Spring的IOC局部设计。回到destory-method上来,都知道使用反射调用方法,但是Spring还有一个侵入性强的方案,直接实现DisposableBean接口,此接口提供destory()方法,所以这就需要把两者统一,最好的办法是当销毁bean的时候统一的循环调用销毁bean的destory()方法。到这里自然的想到把我们的bean适配成DisposableBean注册到容器中,在其destory()中完成自定义销毁方法的调用,所以很自然的使用了适配器模式。而AbstractBeanFactory的实现:
 
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
		if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
			if (mbd.isSingleton()) {
				registerDisposableBean(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors()));
			}
			else {
				// A bean with a custom scope...
				Scope scope = (Scope) this.scopes.get(mbd.getScope());
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope '" + mbd.getScope() + "'");
				}
				scope.registerDestructionCallback(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors()));
			}
		}
	}

这就完成了统一,不管是singleton还是自定义的scope,bean都会有DisposableBeanAdapter实现了DisposableBean接口被注册到容器,当需要销毁bean的时候,总有最后调用DisposableBean的destory()。
  
public void destroy() {
		if (this.beanPostProcessors != null && !this.beanPostProcessors.isEmpty()) {
			for (int i = this.beanPostProcessors.size() - 1; i >= 0; i--) {
				((DestructionAwareBeanPostProcessor) this.beanPostProcessors.get(i)).postProcessBeforeDestruction(
						this.bean, this.beanName);
			}
		}

		boolean isDisposableBean = (this.bean instanceof DisposableBean);
		if (isDisposableBean && this.invokeDisposableBean) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
			}
			try {
				((DisposableBean) this.bean).destroy();
			}
			catch (Throwable ex) {
				String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
				if (logger.isDebugEnabled()) {
					logger.warn(msg, ex);
				}
				else {
					logger.warn(msg + ": " + ex);
				}
			}
		}

		if (this.destroyMethodName != null && !(isDisposableBean && "destroy".equals(this.destroyMethodName))) {
			invokeCustomDestroyMethod();
		}
	}

这个方法完成了销毁动作的统一:1. 已注入的DestructionAwareBeanPostProcessor的postProcessBeforeDestruction()方法,搞点销毁前的小动作;2.如果bean本身就是DisposableBean的实现,调用它的destory()方法,搞点销毁前的小动作;3.如果不是并且有自定义的销毁方法,反射它搞点销毁前的小动作。总而言之,不能轻易的say byebye...

   在Spring中时而统一,时而分离都是根据具体的场景需求选择适合自己的设计解决问题。或许有为了设计而设计的,希望我能找的到,呵呵。总的来说IOC容器本身个人认为没有什么神秘的,但是为了具体的目标而实现的IOC容器在设计上自认远远不能。另外关于开源项目的源码,本人也扫了不少的不少眼,有点看源码的体会:(1)首先源码拿到要看包结构,从包结构设计完全能反映一个项目的好坏,包结构设计凌乱,类的划分界限不明确的要具体对待了。(2)找个切入点,这个不难的。比如你想看tomcat怎么部署app的?Spring中怎么getBean()的?一句话,带着问题进入相应的包,相应的模块。(3)在源码自建一个测试包,写写简单的调用,主要使用IDE的debug看过程。纯代码能看懂功能,不一定能看懂实现,几次断点跟踪一定找到你想看到的逻辑。(4)看到觉得比较闪的实现,试着模拟一下,想想自己会怎么设计等等。写的有点多,哎...
分享到:
评论

相关推荐

    spring配置datasource(dbcp方式)

    一个实例工程,讲解的是怎么在spring中通过dbcp的方式配置datasource

    关于Docker.txt

    lxc-destory:删除处于停机状态的容器; lxc-snapshot:创建和恢复快照; Docker组件: 物理: Client <--> Daemon <--> Registry Server 逻辑: Containers:容器 Images:镜像、映像...

    spring配置实例化后执行的销毁方法比对

    spring配置对象实例化后执行的方法,两种实现比对,通过DisposableBean接口,并实现destory()方法;或者通过applicationContext.xml配置destory-method属性

    myspringlearning:Spring学习项目

    namebeans.xml简单属性的注入UserDAOImpl连接池需要bean的scope属性singleton单例prototype原型官方文档,scope集合注入设置好set,get方法在bean中设置好值自动装配auto-wire生命周期lazy-initinit-method,destory-

    解决Vue keep-alive 调用 $destory() 页面不再被缓存的情况

    刚开始使用的方法是:关闭导航标签,直接调用对应的页面的$destory()方法,OK,可以实现关闭页面(下次再打开该页面,将初始化)的功能,但是遇到个问题: 该页面将不再被缓存,也就是说切换导航tab时,页面将不断的...

    前端项目-overlayscrollbars.zip

    前端项目-overlayscrollbars,一个javascript滚动条插件,它隐藏了本机滚动条,并提供了自定义的可样式覆盖滚动条,但保留了本机功能和感觉。

    terraformize:通过一个简单的REST API端点应用Apply \ Destory Terraform模块

    地形化通过简单的REST API端点应用Apply \ Destory Terraform模块。 Drone.io CI / CD单元测试和自动推送状态: 代码覆盖率:产品特点REST API运行: terraform apply terraform destroy 无需更改代码,支持所有未...

    Web第一个Servlet程序(生命周期、执行流程、tomcat)

    web项目:第一个Servlet程序 Servlet的生命周期:init--service--destroy、执行流程:构造器--init方法--循环[ service方法 ]--destory方法(正常关闭Tomcat)、启动tomcat测试

    servlet運行原理詳解 自我學習整理

    Servlet體系結構是建立在java多線程機制之上的,它的生命週期是由web容器負責管理的。當user從client端向web服務器發送一個http請求時,服務器就會分配一個線程來服務這個http請求,web容器會檢查是否有與此http url...

    istio:istio示例和资源

    Istio istio示例和资源 istioctl 1.8.3二进制文件: : minikube安装: ://minikube....destory minikube minikube删除--purge 获取具有以下地址的入口地址 function istioctl_proxy_address() { minikube_ip=`miniku

    如何检测Unity3D中的Gameobject的destroy事件来触发其他操作?2

    Posted on 2013年02月26日 by U3d / Unity3D脚本/插件/被围观 269 次 假如Unity3D中游戏的魔法有一个持续时间效果,例

    Vue组件生命周期运行原理解析

    Vue实例有一个完整的生命周期,从开始创建、初始化数据、编译模板、挂在DOM、渲染-更新-渲染、卸载等一系列过程,我们成为Vue 实例的生命周期,钩子就是在某个阶段给你一个做某些处理的机会。 ...

    MFC编程基础篇

    MFC编程基础篇,搜集的一点资料MFC编程基础篇

    C++中new与delete、malloc与free应用分析

    一般来说,在C/C++的面试时,对于new/delete和malloc/free这两对的使用和区别经常被考查到,如果这种基础的问题都答不上来,估计很难过面试了。本文即是对new/delete和malloc/free这两对的使用和区别较为简单的分析...

    servletAPI中文版(TXT格式,自己翻译)

    基本类和接口 一、javax.servlet.Servlet接口 servlet抽象集是javax.servlet.Servlet接口,它规定了必须由Servlet类实现由servlet引擎识别和管理的方法集。...void destory() 当servlet将要卸载时由servlet引擎调用

    Fragment生命周期演示用例

    作者:【郭孝星】http://blog.csdn.net/allenwells 微博:【郭孝星的新浪微博】http://weibo.com/allenwells 邮箱:allenwells@163.com 博客:http://blog.csdn.net/allenwells Github:...

    tb-video-player:淘宝视频播放器KISSY组件

    修复一个拼写错误,万万不该,destroy方法在1.4版本以前错误的写成了destory,新版本已经修复并兼容旧版 V1.3 兼容KISSY1.2.0及以下; V1.2 container支持DOM和Node对象; V1.1 增加视频封面配置项; 修复部分bug。 ...

    重建Activity-Recreating an Activity

    有几个情况下 Activity 会由于正常的程序行为而被 Destory,例如当用户点击返回按钮或 者是 Activity 通过调用 finish()发出了停止信号。系统也有可能会在 Activity 处于 stop 状态且长时间不被使用、或在前台 ...

    filter-源代码.rar

    destory():代表是filter销毁方法 当filter对象销毁时执行该方法 Filter对象的生命周期: Filter何时创建:服务器启动时就创建该filter对象 Filter何时销毁:服务器关闭时filter销毁 (2)Filter的AP详解 1)init...

    使用JSR356在Java中构建WebSocket应用

    使用JSR356在Java中构建WebSocket应用,详细介绍,包括1.服务器推送给客户端和2.客户端发送给服务器

Global site tag (gtag.js) - Google Analytics