关于ApplicationContext和BeanFactory
如果使用ApplicationContext
,则配置的Bean如果scope
属性是singleton
,那么当容器被加载时,这些Bean就会被实例化。好处是可以预先加载,速度快;缺点是耗内存。
如果使用BeanFactory
,则当你实例化该对象的时候,配置的Bean不会被马上实例化,当你使用的时候才被实例化。BeanFacotry
会延迟加载所有的Bean。这样的好处是节约内存,缺点是速度。
一般没有特殊要求,应当使用ApplicationContext
。
ApplicationContext
是Spring更加高级的容器,功能非常强大:
1.提供文本解析工具,包括对国际化的支持
2.提供载入文件资源的通用方法,如图片。
3.可以向注册为监听器的bean发送事件。
ApplicationContext三种经常用到的实现:
1.ClassPathXmlApplicationContext:从类路径中加载。
2.FileSystemXmlApplicationContext:从文件系统加载。
3.XmlWebApplicationContext:从Web系统中加载。
Bean的生命周期
Bean被加载到容器中时,他的生命周期就开始了:
- 1.容器寻找Bean的定义信息并实例化。
当我们的程序加载beans.xml(或spring-config.xml)文件的时候,把我们的Bean(前提是scope=singleton)实例化到内存。
- 2.使用依赖注入,Spring按Bean定义信息配置Bean的所有属性。
使用setter方法设置属性,所以,Bean类必须为属性提供setter方法。
- 3.若Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。
setBeanName(String s)
方法可以获取正在实例化的Bean的ID号。
比如下例,我们让入门【Spring——入门】案例中的UserService
类实现BeanNameAware
接口,然后重写其setBeanName(String s)
方法
package com.gavin.service;
import org.springframework.beans.factory.BeanNameAware;
public class UserService implements BeanNameAware{
private String name;
private ByeService byeService;
public UserService() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ByeService getByeService() {
return byeService;
}
public void setByeService(ByeService byeService) {
this.byeService = byeService;
}
public void sayHello() {
System.out.println("Hello, " + name);
byeService.sayBye();
}
@Override
public void setBeanName(String s) {
System.out.println("当前Bean的ID号为:" + s);
}
}
此时运行TestMain
主方法后,结果如下:
可以看到,我们在UserService
类中获取到了当前类的ID,这个ID正是我们配置在Spring
配置文件中的ID。
- 4.若Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身。
setBeanFactory(BeanFactory beanFactory)
方法可以给我们传递工厂自身。
与BeanNameAware
类似,实现这个接口可以让我们在Bean对象中获取当前的工厂。
5.若Bean实现了ApplicationContextAware接口,工厂调用setApplicationContext()方法传入上下文。
6.若BeanPostProcessor(Bean后置处理器)和Bean关联,则它们的postProcessBeforeInitialization()方法被调用。
后置处理器BeanPostProcessor有点类似于Web中的Filter。
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("postProcessBeforeInitialization方法被调用");
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("postProcessAfterInitialization方法被调用");
return o;
}
}
在beans.xml中的配置:
<bean id="myBeanPostProcessory" class="com.gavin.beanlife.MyBeanPostProcessor"/>
假设配置了后置处理器,不管是实例化哪个Bean,都会先调用 postProcessBeforeInitialization
方法。
后置处理器的应用需求有:
- 记录每个对象被实例化的时间
- 过滤每个调用对象的IP
给所有对象添加一个属性,获取其他的函数(所谓AOP,面向切面编程,针对所有对象编程)
7.若Bean实现了
InitializingBean
接口,则会调用afterPropertiesSet()
方法。
该方法会在自己定制的初始化方法和后置处理器的postProcessAfterInitialization()方法之前调用。
- 8.如果自己定制了init-method,则调用自己定制的初始化方法
<bean id="personService" init-method="init" class="com.gavin.beanlife.PersonService">
<property name="name" value="Gavin"/>
</bean>
9.最后,若有
BeanPostProcessor
和Bean关联,则它们的postProcessAfterInitialization()
方法被调用。10.使用Bean
11.容器关闭
12.当容器关闭后,若实现了
DisposableBean
接口,则会调用destroy()
方法。13.如果自己定制了销毁方法,配置了destroy-method,则会调用自己定制的销毁方法,配置如下:
<bean id="personService" init-method="init" destroy-method="destroy" class="com.gavin.beanlife.PersonService">
<property name="name" value="Gavin"/>
</bean>
【注意】在实际开发中可能并不需要上述所有的步骤。根据需要即可。一般的步骤:1->2->6->9->10->11
问题:通过BeanFactory来获取Bean,和通过ApplicationContext来获取Bean,生命周期是否一样的?
答案:不一样,BeanFactory获取Bean的生命周期简单得多!
如下两幅图对比: