1、容器中的bean的生命周期

spring容器可以管理singleton作用域的bean的生命周期,包括bean何时被创建、何时初始化完成、何时被销毁。客户端代码不能控制该类型bean的销毁。spring容器可以管理该类型bean在实例化结束之后和销毁之前的行为。

prototype作用域类型的bean则完全交由客户端代码管理,spring容器仅仅是负责创建bean。spring容器无法管理该类型的bean。

管理bean的生命周期行为的主要时机有以下两个:

1》注入依赖关系之后

2》即将销毁bean之前

2、依赖关系注入之后的行为

spring提供两种方式在bean全部属性设置成功后执行特定的行为:

1》使用init-method属性,指定某方法在在bean全部属性(即包括依赖)设置成功后执行特定行为。

2》实现InitializingBean接口,并实现该接口的void afterPropertiesSet() throws Exception方法。

举个例子:

Chinese.java

package com.lfy.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; public class Chinese implements InitializingBean,BeanNameAware,ApplicationContextAware { private ApplicationContext ctx;
private String beanName;
private String personName; public Chinese() { System.out.println("=========调用无参构造器Chinese()=========");
} @Override
public void setBeanName(String beanName) { this.beanName=beanName;
System.out.println("=========setBeanName()=========");
} @Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.ctx=arg0;
System.out.println("=========setApplicationContext()=========");
} /**
* 生命周期方法
*/
@Override
public void afterPropertiesSet() throws Exception { System.out.println("=========初始化bean方法afterPropertiesSet()=========");
} /**
* 将被设为生命周期方法
*/
public void init() { System.out.println("=========初始化bean方法init()=========");
} public void setPersonName(String name) { this.personName=name;
System.out.println("=========setPersonName()执行setter方法注入personName========="+personName);
} public void info() { System.out.println("Chinese实现类"+",部署该bean时指定的id为:"+beanName);
} }

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- spring配置文件的根元素,使用spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="chinese" class="com.lfy.bean.Chinese" init-method="init">
<property name="personName" value="至尊宝"/>
</bean>
</beans>

SpringTest.java

package com.lfy.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lfy.bean.Chinese; /**
* 获得bean本身的id
* @author lfy
*
*/
public class SpringTest { public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); Chinese chin=ctx.getBean("chinese", Chinese.class);
} }

运行结果:

总结:上面的例子,两种方式都同时实现了,注意它们执行的顺序。afterPropertiesSet()方法先于init()方法得到执行。

3、bean销毁之前的行为

spring提供两种方式在bean实例销毁之前执行特定的行为:

1》使用destroy-method属性,指定某方法在在bean实例销毁之前执行特定行为。

2》实现DisposableBean接口,并实现该接口的void destroy() throws Exception方法。

举个例子:对前面的例子做一些修改

Chinese.java

package com.lfy.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; public class Chinese implements InitializingBean,DisposableBean,BeanNameAware,ApplicationContextAware { private ApplicationContext ctx;
private String beanName;
private String personName; public Chinese() { System.out.println("=========调用无参构造器Chinese()=========");
} @Override
public void setBeanName(String beanName) { this.beanName=beanName;
System.out.println("=========setBeanName()=========");
} @Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException { this.ctx=arg0;
System.out.println("=========setApplicationContext()=========");
} /**
* 生命周期方法
*/
@Override
public void afterPropertiesSet() throws Exception { System.out.println("=========初始化bean方法afterPropertiesSet()=========");
} /**
* 将被设为生命周期方法
*/
public void init() { System.out.println("=========初始化bean方法init()=========");
} /**
* 生命周期方法
*/
@Override
public void destroy() throws Exception { System.out.println("=========结束bean前执行方法destroy()=========");
} /**
* 将被设为生命周期方法
*/
public void close() { System.out.println("=========结束bean前执行方法close()=========");
} public void setPersonName(String name) { this.personName=name;
System.out.println("=========setPersonName()执行setter方法注入personName========="+personName);
} public void info() { System.out.println("Chinese实现类"+",部署该bean时指定的id为:"+beanName);
} }

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- spring配置文件的根元素,使用spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- 遇到了个问题,偶尔出现destory-method="close"不能出现在<bean.../>,暂未明白原因 -->
<bean id="chinese" class="com.lfy.bean.Chinese" init-method="init" destory-method="close">
<property name="personName" value="至尊宝"/>
</bean>
</beans>

SpringTest.java

package com.lfy.main;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lfy.bean.Chinese; /**
* 获得bean本身的id
* @author lfy
*
*/
public class SpringTest { public static void main(String[] args) { AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); Chinese chin=ctx.getBean("chinese", Chinese.class); ctx.registerShutdownHook();
} }

运行结果:

总结:由于xml解析发生了些问题未能找到原因,一开始destory-method方法没能演示效果。后来突然又好了,可能是eclipse的问题。在非Web应用上,使用registerShutdownHook()能够正确的关闭spring容器。并且注意destory()方法先于close()方法执行。

4、此外协调作用域不同步的bean:

singleton作用域的bean依赖于prototype作用域的bean,但singleton bean只有一次初始化的机会,在初始化单例bean的时候,会先创建prototype作用域的bean,然后再初始化单例bean。这将导致以后无论什么时候通过单例bean访问prototype bean都会是同一个bean,相当于单例bean变相的将prototype bean单例化了。

为了解决这种生命周期与预期不同步的问题,有两种解决思路:

1》 放弃依赖注入:单例bean每次需要prototype bean时,主动向容器请求新的bean实例。

2》利用方法注入:通常使用lookup方法注入。使用lookup方法注入可以让spring容器重写容器中bean的抽象或者具体方法,返回查找容器中其他bean的结果。spring通过使用JDK动态代理或cglib库修改客户端的二进制代码,从而实现上述需求。

使用lookup方法注入,大致的步骤有:

1》将调用者bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖的bean。

2》在<bean.../>元素中添加<lookup-method.../>子元素让spring为调用者bean的实现类实现指定的抽象方法。

3》使用<lookup-method.../>元素需要指定如下两个元素:

name:指定需要让spring实现的方法。

bean:指定spring实现该方法的返回值。

举个例子:

Dog.java

package com.lfy.bean;

public interface Dog {

    public String run();
}

GunDog.java

package com.lfy.impl;

import com.lfy.bean.Dog;

public class GunDog implements Dog {

    private String name;

    public void setName(String name) {

        this.name=name;
} @Override
public String run() { return "我是一只叫"+this.name+"的猎犬,奔跑迅速...";
} }

Chinese.java

package com.lfy.bean;

public abstract class Chinese {

    //private Dog dog;留作动态注入一个Dog的实例

    // 定义抽象方法,该方法用于获取被依赖的bean
public abstract Dog getDog(); public void hunt() { System.out.println("我带着:"+getDog()+"出去打猎");
System.out.println(getDog().run());
}
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- spring配置文件的根元素,使用spring-beans-4.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <!-- chinese依赖于gunDog,并且为单例bean依赖prototype实例。
spring检测到lookup-method元素,会自动为该元素的name属性
指定的方法提供实现体
-->
<bean id="chinese" class="com.lfy.bean.Chinese">
<lookup-method name="getDog" bean="gunDog"/>
</bean> <!-- 指定bean的作用域为prototype,希望每次都是新的bean -->
<bean id="gunDog" class="com.lfy.impl.GunDog" scope="prototype">
<property name="name" value="旺财"/>
</bean>
</beans>

SpringTest.java

package com.lfy.main;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import com.lfy.bean.Chinese; /**
* 获得bean本身的id
* @author lfy
*
*/
public class SpringTest { public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml"); Chinese p1=ctx.getBean("chinese", Chinese.class);
Chinese p2=ctx.getBean("chinese", Chinese.class);
System.out.println("是否还是同一个Chinese猎人:"+(p1 == p2));
p1.hunt();
p2.hunt();
} }

运行结果:

总结:我们的Chinese类是一个抽象类,它不能实例化。我们通过给它添加<lookup-method.../>元素,告诉spring需要实现哪个抽象方法spring容器为抽象方法提供实体后,抽象方法变成类的具体方法,这个类也就变成了具体类,接下来spring就可以创建该bean的实例了。

spring会采用运行时动态增强的方式来实现<lookup-method.../>元素所指定的抽象方法:如果目标抽象类实现过接口,spring会采用JDK动态代理来实现该抽象类,并为之实现抽象方法;如果目标抽象类没有实现过接口,spring会采用cglib实现该抽象类,并为之实现抽象方法。spring-4.0.4已经在核心包中集成了cglib类库,无需额外添加cglib的jar包。

spring-第八篇之容器中的bean的生命周期的更多相关文章

  1. SpringBean容器启动流程+Bean的生命周期【附源码】

    如果对SpringIoc与Aop的源码感兴趣,可以访问参考:https://javadoop.com/,十分详细. 目录 Spring容器的启动全流程 Spring容器关闭流程 Bean 的生命周期 ...

  2. Spring扩展:替换IOC容器中的Bean组件 -- @Replace注解

    1.背景:     工作中是否有这样的场景?一个软件系统会同时有多个不同版本部署,比如我现在做的IM系统,同时又作为公司的技术输出给其他银行,不同的银行有自己的业务实现(比如登陆验证.用户信息查询等) ...

  3. spring中的bean的生命周期

    bean的生命周期:bean的创建 —— 初始化 ——销毁的过程 容器管理bean的生命周期,我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期就会调用我们的方法 在xml配置文件中是在 ...

  4. Spring重点—— IOC 容器中 Bean 的生命周期

    一.理解 Bean 的生命周期,对学习 Spring 的整个运行流程有极大的帮助. 二.在 IOC 容器中,Bean 的生命周期由 Spring IOC 容器进行管理. 三.在没有添加后置处理器的情况 ...

  5. Spring Bean的生命周期,《Spring 实战》书中的官方说法

    连着两天的面试 ,都问到了 Spring 的Bean的生命周期,其中还包括 昨晚一波阿里的电话面试.这里找到了Spring 实战中的官方说法.希望各位要面试的小伙伴记住,以后有可能,或者是有时间 去看 ...

  6. 简:Spring中Bean的生命周期及代码示例

    (重要:spring bean的生命周期. spring的bean周期,装配.看过spring 源码吗?(把容器启动过程说了一遍,xml解析,bean装载,bean缓存等)) 完整的生命周期概述(牢记 ...

  7. 【Spring】Spring中的Bean - 4、Bean的生命周期

    Bean的生命周期 简单记录-Java EE企业级应用开发教程(Spring+Spring MVC+MyBatis)-Spring中的Bean 了解Spring中Bean的生命周期有何意义? 了解Sp ...

  8. Spring基础14——Bean的生命周期

    1.IOC容器中的Bean的生命周期方法 SpringIOC容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行定制的任务.SpringIOC容器对Bean的生命周期进行管理 ...

  9. Spring学习-- IOC 容器中 bean 的生命周期

    Spring IOC 容器可以管理 bean 的生命周期 , Spring 允许在 bean 声明周期的特定点执行定制的任务. Spring IOC 容器对 bean 的生命周期进行管理的过程: 通过 ...

随机推荐

  1. [资料] 常见的IC芯片解密方法与原理解析!

    其实了解芯片解密方法之前先要知道什么是芯片解密,网络上对芯片解密的定义很多,其实芯片解密就是通过半导体反向开发技术手段,将已加密的芯片变为不加密的芯片,进而使用编程器读取程序出来.   芯片解密所要具 ...

  2. Taro -- 原生微信小程序转taro

    微信小程序转Taro  (转发https://nervjs.github.io/taro/docs/taroize.html) Taro 可以将你的原生微信小程序应用转换为 Taro 代码,进而你可以 ...

  3. Cookie-based认证实现

    (使用ASP.NET 4.X 中的时候一定都用过Forms认证即FormsAuthentication做登录用户的身份认证,FormsAuthentication的核心就是Cookie,ASP.NET ...

  4. 一、ARM

    1.1 ARM 分类 1.1.1 版本号分类 以前分类的是 ARM7,ARM9... ARM11,在 ARM11 之后,就是以 Cortex 系列分类了: Cortex-R:应用在实时系统上的系列 C ...

  5. alert(1) to win 9

    function escape(s) { function htmlEscape(s) { return s.replace(/./g, function(x) { return { '<': ...

  6. 装饰器模式-Decerator

    一.定义 装饰器模式又叫做包装模式(Wrapper).装饰器模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 在以下情况下应该使用装饰器模式: 1.需要扩展一个类的功能,或给一个类增 ...

  7. DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

  8. HTML表单(来自MDN的总结)

    表单介绍 HTML表单是用户和web站点或应用程序之间交互的主要内容之一.它们允许用户将数据发送到web站点.大多数情况下,数据被发送到web服务器,但是web页面也可以拦截它自己并使用它. HTML ...

  9. Django组件---Django请求生命周期和中间件

    Django组件---Django请求生命周期和中间件 Django请求生命周期 说明: client代表浏览器,浏览器的内部为我们封装了socket,Django的WSGI模块也为我们封装了sock ...

  10. Categorical Data

    This is an introduction to pandas categorical data type, including a short comparison with R's facto ...