`
ftj20003
  • 浏览: 132372 次
  • 性别: 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)看到觉得比较闪的实现,试着模拟一下,想想自己会怎么设计等等。写的有点多,哎...
分享到:
评论

相关推荐

    基于springioc bean 的几个属性介绍

    destory-method 属性用于指定在 Spring 容器关闭或销毁时调用的方法。这只适用于单例模式,用于释放资源、关闭连接等操作。 这些基于 Spring IOC Bean 的几个属性可以帮助开发者更好地控制 Bean 对象的创建、生命...

    关于Docker.txt

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

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

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

    spring启动加载程序的几种方法介绍

    init-method="initMethod" destory-method="destroyMethod"/> ``` 对应的Java类可以像这样定义: ```java public class DataInitializer { public void initMethod() throws Exception { System.out.println(...

    myspringlearning:Spring学习项目

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

    Spring Bean的初始化和销毁实例详解

    System.out.println("jsr250-destory-method"); } ``` 在上面的代码中,我们使用@PreDestroy注解来指定Bean的销毁方法为destroy。 三、配置和使用 在Spring框架中,我们可以使用@Configuration注解来配置Bean的...

    Spring中Bean的生命周期使用解析

    13. 自定义的destory-method指定的方法:如果在配置文件中使用destory-method指定了销毁方法,那么在Bean实例销毁之前会调用该指定的方法完成一些销毁之前的处理工作。 在这个阶段,如果配置文件中指定了销毁方法,...

    spring配置文档[定义].pdf

    例如,`personService2`在创建后会调用`init`方法,在容器关闭时调用`destory`方法。 8. **属性注入**: - `<property>`元素用于注入bean的属性,`name`对应属性名,`ref`属性用于引用其他bean,如`personService4...

    Spring框架初始化解析

    2. 通过在xml中定义init-method 和 destory-method方法。 3. 通过Bean实现InitializingBean和 DisposableBean接口。 4. 写一个类,实现BeanPostProcessor接口,这个接口有两个方法:postProcessBeforeInitialization...

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

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

    利用注解配置Spring容器的方法

    @Bean(name = "user", initMethod = "init", destroyMethod = "destory") @Scope("prototype") public User getUser() { return new User("tom", 20); } ``` 在上面的例子中,我们使用@Bean注解注册了一个名为...

    一元多项式加减乘运算-数据结构-课程设计

    一元多项式是数学中的基本概念,通常由不同幂次的x的系数组成,如2x^3 - 4x^2 + 5x - 1。在这个课程设计中,我们将使用链表数据结构来表示一元多项式,并实现加、减、乘运算。 首先,定义一个名为`Polyn.h`的头文件...

    JavaWeb开发技术-Servlet的生命周期.pptx

    在销毁阶段,Servlet容器会调用destory()方法来销毁Servlet实例对象。destory()方法是Servlet生命周期中的最后一个方法,它用于释放Servlet对象占用的资源。 在Servlet的生命周期中,还有一些其他的方法,例如...

    第九章 菜单设计的应用.docx

    第九章主要讨论的是菜单设计在Visual Foxpro (VFP)中的应用。VFP支持两种类型的菜单,即条形菜单和弹出式菜单。菜单结构包括内部名字和一系列菜单选项,每个菜单项都有名称和选项序号,用于在代码中进行引用。菜单项...

    操作系统课程设计 用C语言实现进程调度

    操作系统课程设计用C语言实现进程调度 本文主要讲述了使用C语言实现进程调度的操作系统课程设计。进程调度是操作系统中最重要的部分之一,它负责将CPU资源分配给多个进程,以提高系统的效率和性能。在本设计中,...

    Servlet实验报告.pdf

    本实验报告的主要目的是掌握 Servlet 应用,熟悉 web 开发的 MVC 模式,了解 servlet 的工作流程和作用,并掌握 servlet 的方法 init()、destory()、doGet()、doPost()。 一、实验目的 1. 掌握 Servlet 应用,熟悉...

    前端项目-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...

Global site tag (gtag.js) - Google Analytics