在《【Spring注解驱动开发】如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!》一文中,咱们讲述了如何使用@Bean注解来指定bean初始化和销毁的方法。具体的用法就是在@Bean注解中使用init-method属性和destroy-method属性来指定初始化方法和销毁方法。除此以外,Spring中是否还提供了其余的方式来对bean实例进行初始化和销毁呢?java
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotationgit
Spring中提供了一个InitializingBean接口,InitializingBean接口为bean提供了属性初始化后的处理方法,它只包括afterPropertiesSet方法,凡是继承该接口的类,在bean的属性初始化后都会执行该方法。InitializingBean接口的源码以下所示。github
package org.springframework.beans.factory; public interface InitializingBean { void afterPropertiesSet() throws Exception; }
根据InitializingBean接口中提供的afterPropertiesSet()方法的名字能够推断出:afterPropertiesSet()方法是在属性赋好值以后调用的。那究竟是不是这样呢?咱们来分析下afterPropertiesSet()方法的调用时机。spring
咱们定位到Spring中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory类下的invokeInitMethods()方法中,来查看Spring加载bean的方法。bash
题外话:不要问我为何会是这个invokeInitMethods()方法,若是你和我同样对Spring的源码很是熟悉的话,你也会知道是这个invokeInitMethods()方法,哈哈哈哈!因此,小伙伴们不要只顾着使用Spring,仍是要多看看Spring的源码啊!Spring框架中使用了大量优秀的设计模型,其代码的编写规范和严谨程度也是业界开源框架中首屈一指的,很是值得阅读。微信
咱们来到AbstractAutowireCapableBeanFactory类下的invokeInitMethods()方法,以下所示。框架
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { //判断该bean是否实现了实现了InitializingBean接口,若是实现了InitializingBean接口,则调用bean的afterPropertiesSet方法 boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { //调用afterPropertiesSet()方法 ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //调用afterPropertiesSet()方法 ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { //经过反射的方式调用init-method invokeCustomInitMethod(beanName, bean, mbd); } } }
分析上述代码后,咱们能够初步得出以下信息:ide
也就是说Spring为bean提供了两种初始化的方式,第一种实现InitializingBean接口,实现afterPropertiesSet方法,第二种配置文件或@Bean注解中经过init-method指定,两种方式能够同时使用,同时使用先调用afterPropertiesSet方法,后执行init-method指定的方法。学习
实现org.springframework.beans.factory.DisposableBean接口的bean在销毁前,Spring将会调用DisposableBean接口的destroy()方法。咱们先来看下DisposableBean接口的源码,以下所示。测试
package org.springframework.beans.factory; public interface DisposableBean { void destroy() throws Exception; }
能够看到,在DisposableBean接口中只定义了一个destroy()方法。
在Bean生命周期结束前调用destory()方法作一些收尾工做,亦能够使用destory-method。前者与Spring耦合高,使用类型强转.方法名(),效率高。后者耦合低,使用反射,效率相对低
多例bean的生命周期不归Spring容器来管理,这里的DisposableBean中的方法是由Spring容器来调用的,因此若是一个多例实现了DisposableBean是没有啥意义的,由于相应的方法根本不会被调用,固然在XML配置文件中指定了destroy方法,也是没有意义的。因此,在多实例bean状况下,Spring不会自动调用bean的销毁方法。
建立一个Animal的类实现InitializingBean和DisposableBean接口,代码以下:
package io.mykit.spring.plugins.register.bean; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; /** * @author binghe * @version 1.0.0 * @description 测试InitializingBean接口和DisposableBean接口 */ public class Animal implements InitializingBean, DisposableBean { public Animal(){ System.out.println("执行了Animal类的无参数构造方法"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("执行了Animal类的初始化方法。。。。。"); } @Override public void destroy() throws Exception { System.out.println("执行了Animal类的销毁方法。。。。。"); } }
接下来,咱们新建一个AnimalConfig类,并将Animal经过@Bean注解的方式注册到Spring容器中,以下所示。
package io.mykit.spring.plugins.register.config; import io.mykit.spring.plugins.register.bean.Animal; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author binghe * @version 1.0.0 * @description AnimalConfig */ @Configuration @ComponentScan("io.mykit.spring.plugins.register.bean") public class AnimalConfig { @Bean public Animal animal(){ return new Animal(); } }
接下来,咱们在BeanLifeCircleTest类中新增testBeanLifeCircle02()方法来进行测试,以下所示。
@Test public void testBeanLifeCircle02(){ //建立IOC容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class); System.out.println("IOC容器建立完成..."); //关闭IOC容器 context.close(); }
运行BeanLifeCircleTest类中的testBeanLifeCircle02()方法,输出的结果信息以下所示。
执行了Animal类的无参数构造方法 执行了Animal类的初始化方法。。。。。 IOC容器建立完成... 执行了Animal类的销毁方法。。。。。
从输出的结果信息能够看出:单实例bean下,IOC容器建立完成后,会自动调用bean的初始化方法;而在容器销毁前,会自动调用bean的销毁方法。
多实例bean的案例代码基本与单实例bean的案例代码相同,只不过在AnimalConfig类中,咱们在animal()方法上添加了@Scope("prototype")注解,以下所示。
package io.mykit.spring.plugins.register.config; import io.mykit.spring.plugins.register.bean.Animal; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; /** * @author binghe * @version 1.0.0 * @description AnimalConfig */ @Configuration @ComponentScan("io.mykit.spring.plugins.register.bean") public class AnimalConfig { @Bean @Scope("prototype") public Animal animal(){ return new Animal(); } }
接下来,咱们在BeanLifeCircleTest类中新增testBeanLifeCircle03()方法来进行测试,以下所示。
@Test public void testBeanLifeCircle03(){ //建立IOC容器 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AnimalConfig.class); System.out.println("IOC容器建立完成..."); System.out.println("-------"); //调用时建立对象 Object bean = ctx.getBean("animal"); System.out.println("-------"); //调用时建立对象 Object bean1 = ctx.getBean("animal"); System.out.println("-------"); //关闭IOC容器 ctx.close(); }
运行BeanLifeCircleTest类中的testBeanLifeCircle03()方法,输出的结果信息以下所示。
IOC容器建立完成... ------- 执行了Animal类的无参数构造方法 执行了Animal类的初始化方法。。。。。 ------- 执行了Animal类的无参数构造方法 执行了Animal类的初始化方法。。。。。 -------
从输出的结果信息中能够看出:在多实例bean状况下,Spring不会自动调用bean的销毁方法。
好了,我们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一块儿学习一块儿进步!!
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation
若是以为文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发再也不迷茫。