Bean的生命周期

x33g5p2x  于2022-02-21 转载在 其他  
字(6.8k)|赞(0)|评价(0)|浏览(434)

1、Bean的简介

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。而bean的定义以及bean相互间的依赖关系将通过配置元数据来描述。

Spring中bean对象默认都是单例的,同一个beanname获取的bean对象都单例的。对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于JVM,每个JVM内只有一个实例。

2、Bean的实例化过程

①Aware接口及相关子接口

Aware接口从字面上翻译过来是感知捕获的含义。单纯的bean(未实现Aware系列接口)是没有知觉的;实现了Aware系列接口的bean可以访问Spring容器。这些Aware系列接口增强了Spring bean的功能,但是也会造成对Spring框架的绑定,增大了与Spring框架的耦合度。(Aware是“意识到的,察觉到的”的意思,实现了Aware系列接口表明:可以意识到、可以察觉到)

接口源码:

public interface Aware{
    
}

可以发现该接口并没有定义任何方法,所以这只是一个标识接口,该接口的子接口如下:

可以发现子接口,都以Aware结尾,那这些子接口有什么作用呢?

先进入子接口一探究竟

public interface ApplicationEventPublisherAware extends Aware {
    void setApplicationEventPublisher(ApplicationEventPublisher var1);
}
public interface MessageSourceAware extends Aware {
    void setMessageSource(MessageSource var1);
}

我们发现每个子接口都定义了set方法。而方法中的形参类别是接口Aware前面的内容,也就是当前Bean需要感知的内容。所以我们需要在Bean中声明相关的成员变量来接收。

②举例说明
/**
 * 实现了
 * 	ApplicationContextAware
 *  BeanClassLoaderAware
 *  BeanFactoryAware
 *  BeanNameAware
 *  接口
 */
public class User implements ApplicationContextAware,BeanClassLoaderAware,BeanFactoryAware,BeanNameAware{

	private int id;
	
	private String name;
	// 保存感知的信息
	private String beanName;
	// 保存感知的信息
	private BeanFactory beanFactory;
	// 保存感知的信息
	private ApplicationContext ac;
	// 保存感知的信息
	private ClassLoader classLoader;
	
	public BeanFactory getBeanFactory() {
		return beanFactory;
	}

	public ApplicationContext getAc() {
		return ac;
	}

	public ClassLoader getClassLoader() {
		return classLoader;
	}

	public User(){
		System.out.println("User 被实例化");
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getBeanName() {
		return beanName;
	}
	
	/**
	 * 自定义的初始化方法
	 */
	public void start(){
		System.out.println("User 中自定义的初始化方法");
	}
	
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", beanName=" + beanName + "]";
	}

	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		System.out.println(">>> setBeanClassLoader");
		this.classLoader = classLoader;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		System.out.println(">>> setApplicationContext");
		this.ac = applicationContext;
	}

	@Override
	public void setBeanName(String name) {
		System.out.println(">>> setBeanName");
		this.beanName = name;
	}

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		System.out.println(">>> setBeanFactory");
		this.beanFactory = beanFactory;
	}
}

测试类

@Test
public void test1() {
	ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
	User user = ac.getBean(User.class);
	System.out.println("beanFactory:"+user.getBeanFactory());
	System.out.println("beanName:"+user.getBeanName());
	System.out.println("applicationContext:"+user.getAc());
	System.out.println("classLoader:"+user.getClassLoader());
	System.out.println(user);
}

3、Bean的生命周期

①执行过程:
    • Spring对bean进行实例化,默认bean是单例;
  • Spring对bean进行依赖注入;
  • 如果bean实现了BeanNameAware接口,Spring将bean的名称传给setBeanName()方法;
  • 如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory实例传进来;
  • 如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
  • 如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization()方法将被调用;
  • 如果bean中有方法添加了@PostConstruct注解,那么该方法将被调用;
  • 如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet()接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
  • 如果在xml文件中通过标签的init-method元素指定了初始化方法,那么该方法将被调用;
  • 如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization()接口方法将被调用;
  • 此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
  • 如果bean中有方法添加了@PreDestroy注解,那么该方法将被调用;
  • 若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用;

有时候,我们并没有实现那些接口,我们可以除去哪些接口,针对Bean的单例和非单例来描述下bean的生命周期。

②单例Bean

当scope=“singleton”,即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean节点的lazy-init="true"来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。如下配置:

<bean id="serviceImpl" class="cn.csdn.service.ServiceImpl" lazy-init="true"/>

如果想对所有的默认返利bean都应用延迟初始化,可以在根节点beans设置default-lazy-init属性为true,如下所示:

<beans default-lazy-init="true">

默认情况下,Spring在读取xml文件的时候,就会创建对象。在创建对象的时候先调用构造器,然后调用init-method属性值中所指定的方法。对象在被销毁的时候,会调用destroy-method属性值中所指定的方法(例如调用Container.destroy()方法的时候)。写一个测试类,代码如下:

public class LifeBean {
	private String name;  
    
    public LifeBean(){  
        System.out.println("LifeBean()构造函数");  
    }  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        System.out.println("setName()");  
        this.name = name;  
    }  

    public void init(){  
        System.out.println("this is init of lifeBean");  
    }  
      
    public void destory(){  
        System.out.println("this is destory of lifeBean " + this);  
    }  
}

beans.xml配置如下:

<bean id="life_singleton" class="com.bean.LifeBean" scope="singleton" 
			init-method="init" destroy-method="destory" lazy-init="true"/>

测试代码:

public class LifeTest {
	@Test 
	public void test() {
		AbstractApplicationContext container = 
		new ClassPathXmlApplicationContext("life.xml");
		LifeBean life1 = (LifeBean)container.getBean("life");
		System.out.println(life1);
		container.close();
	}
}
③非单例管理的对象

Prototype作用域和singleton刚刚好相反,singleton只会返回同一个对象,Prototype则是返回一个bean对应多个对象实例,Prototype作用域的bean会导致在每次对这个bean请求(将它注入到另一个bean中,或者以程序的方式调用getBean()方法)时都会创建一个新的bean实例,相当于执行newXxxBean()。Prototypes 原型类型,它在我们创建容器时并没有实例化,只会在我们获取bean的时候才会去创建一个对象,而且我们每次创建的对象都不是同一个对象,根据需要,对有状态的bean应该使用Prototype作用域,对无状态的的bean则可以使用singleton作用域。
来看看Prototype作用域的差别,其余代码同上 ,在。beans.xml中把scope改成prototype

<bean id="life_prototype" class="cn.hsd.mybatis.entity.LifeBean" scope="prototype"
          init-method="init" destroy-method="destroy" lazy-init="true"/>

测试代码:

@Test
public void test3(){
    AbstractApplicationContext container = new ClassPathXmlApplicationContext("beans.xml");
    LifeBean life1 = (LifeBean) container.getBean("life_singleton");
    LifeBean life2 = (LifeBean) container.getBean("life_singleton");
    System.out.println(life1==life2);//true

    LifeBean life3 = (LifeBean)container.getBean("life_prototype");
    LifeBean life4 = (LifeBean)container.getBean("life_prototype");
    System.out.println(life3==life4);//false

    container.close();
}

可以发现,对于作用域为prototype的bean,其destroy方法并没有被调用。如果bean的scope设为prototype时,当容器关闭时,destroy方法不会被调用。对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。
ln(life3==life4);//false

container.close();

}

可以发现,对于作用域为prototype的bean,其destroy方法并没有被调用。如果bean的scope设为prototype时,当容器关闭时,destroy方法不会被调用。对于prototype作用域的bean,有一点非常重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。

相关文章