1. lazy-init 延遲加載
Bean的延遲加載
Application容器默認(rèn)行為是在啟動服務(wù)器的時候?qū)⑺械膯卫齜ean提前實(shí)例化,提前實(shí)例化意味著初始化過程中ApplicationContext實(shí)例會創(chuàng)建并配置所有的singleton。
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="true" />
- 設(shè)置了lazy-init為true的bean將不會再啟動時提前被實(shí)例化,而是第一次向容器通過getbean獲取bean時實(shí)例化。
- 如果一個設(shè)置了立即加載的bean1,引用了一個延遲加載的bean2,那么bean1在容器啟動時被實(shí)例化,而bean2由于被bean1引用,所以也被實(shí)例化,這種情況也符合延時加載的bean在第一次調(diào)用時才被實(shí)例化的規(guī)則。
- 也可以在容器層次中通過元素使用"default-lazy-init"屬性來控制延時初始化,如下
<beans default-lazy-init="true">
<!-- no beans will be eagerly pre-instantiated... -->
</beans>
- 如果一個bean的scope屬性為scope="prototype"時,那么即使設(shè)置了lazy-init="false",容器啟動也不會初始化bean,而是getBean方法實(shí)例化的。
應(yīng)用場景
- 開啟延遲加載一定程度提高容器啟動和運(yùn)轉(zhuǎn)性能。
- 對于不常使用的Bean設(shè)置延遲加載,這樣偶爾使用的時候再加載,不必要從一開始該bean就占用資源。
2. FactoryBean和BeanFactory
BeanFactory接口是容器的頂級接口,定義了容器的一些基礎(chǔ)行為,負(fù)責(zé)生產(chǎn)和管理Bean的一個工廠,具體使用它下面的子接口的類型,比如ApplicationContext;
此處我們重點(diǎn)分析FactoryBean。
Spring中Bean有兩種,一種是普通Bean,一種是工廠Bean(FactroyBean),F(xiàn)actoryBean可以生成某一個類型的Bean實(shí)例(返回給我們),也就是說我們可以借助于它自定義Bean的創(chuàng)建流程。
Bean創(chuàng)建的三種方式中的靜態(tài)方法和實(shí)例化方法和FactoryBean作用類似,F(xiàn)actoryBean使用較多,尤其在Spring框架中一些組件會使用,還有其他框架和Spring框架整合時使用。
// 可以讓我們?定義Bean的創(chuàng)建過程(完成復(fù)雜Bean的定義)
public interface FactoryBean<T> {
// 返回FactoryBean創(chuàng)建的Bean實(shí)例,如果isSingleton返回true,
// 則該實(shí)例會放到Spring容器的單例對象緩存池中Map
@Nullable
T getObject() throws Exception;
// 返回FactoryBean創(chuàng)建的Bean類型
@Nullable
Class<?> getObjectType();
// 返回作?域是否單例
default boolean isSingleton() {
return true;
}
}
Company類
package com.lagou.edu.pojo;
public class Company {
private String name;
private String address;
private int scale;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getScale() {
return scale;
}
public void setScale(int scale) {
this.scale = scale;
}
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", scale=" + scale +
'}';
}
}
CompanyFactory類
package com.lagou.edu.factory;
import com.lagou.edu.pojo.Company;
import org.springframework.beans.factory.FactoryBean;
public class CompanyFactoryBean implements FactoryBean<Company> {
private String companyInfo; // 公司名稱,地址,規(guī)模
public void setCompanyInfo(String companyInfo) {
this.companyInfo = companyInfo;
}
@Override
public Company getObject() throws Exception {
// 模擬創(chuàng)建復(fù)雜對象Company
Company company = new Company();
String[] strings = companyInfo.split(",");
company.setName(strings[0]);
company.setAddress(strings[1]);
company.setScale(Integer.parseInt(strings[2]));
return company;
}
@Override
public Class<?> getObjectType() {
return Company.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
xml配置
<bean id="companyBean" class="com.lagou.edu.factory.CompanyFactoryBean">
<property name="companyInfo" value="拉勾,中關(guān)村,500"/>
</bean>
測試,獲取FactoryBean產(chǎn)生的對象
Object companyBean = applicationContext.getBean("companyBean");
System.out.println("bean:" + companyBean);
// 結(jié)果如下
bean:Company{name='拉勾', address='中關(guān)村', scale=500}
測試,獲取FactoryBean,需要在id之前添加“&”
Object companyBean = applicationContext.getBean("&companyBean");
System.out.println("bean:" + companyBean);
// 結(jié)果如下, 可以通過包名看到就是剛才新建的factoryBean
bean:com.lagou.edu.factory.CompanyFactoryBean@53f6fd09
3. 后置處理器
Spring提供了兩種后處理bean的擴(kuò)展接口,分別是BeanPostProcessor和BeanFactoryPostProcessor。兩者使用上是有所區(qū)別的。
工廠初始化(BeanFactory)- -> Bean對象
在BeanFactory初始化之后可以使用BeanFactoryPostProcessor進(jìn)行后置處理做一些事情。
在Bean對象實(shí)例化(并不是Bean的整個生命周期完成)之后可以使用BeanPostProcessor進(jìn)行后置處理做一些事情。
3.1 BeanPostProcessor
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
該接口提供兩個方法,分別在bean的初始化方法前和初始化方法后執(zhí)行(這個初始化方法值得是類似我們定義bean的時候定義了init-method所指定的方法)。
定義了一個類是實(shí)現(xiàn)了BeanPostProcessor,默認(rèn)會對整個Spring容器中所有的bean進(jìn)行處理,如果要對某個bean處理,可以通過方法參數(shù)判斷,兩個類型參數(shù)分別為Object和String,第一個參數(shù)是每一個bean的實(shí)例,第二個參數(shù)就是每個bean的name或者id屬性的值。蘇益我們可以通過第二個參數(shù)來判斷我們將要處理的具體bean。
注意:處理是發(fā)生在Spring容器的實(shí)例化和依賴注入之后。
3.2 BeanFactoryPostProcessor
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
該接口只提供了一個方法postProcessBeanFactory,接口參數(shù)為ConfigurableListableBeanFactory,該接口參數(shù)定義了一些方法。

根據(jù)其中一個方法getBeanDefinition可以找到定義的bean的BeanDefinition對象,然后我們可以對定義的屬性進(jìn)行修改,以下是BeanDefinition中的方法。

方法名字類似我們bean標(biāo)簽的屬性,setBeanClassName對應(yīng)bean標(biāo)簽的class屬性,所以當(dāng)我們拿到BeanDefinition對象時,我們可以手動修改bean標(biāo)簽中所定義的屬性值。
BeanDefinition對象:我們在XML中定義的bean標(biāo)簽。Spring解析bean標(biāo)簽成為一個JavaBean,這個JavaBean就是BeanDefinition 。
注意:調(diào)用BeanFactoryPostProcessor方法時,這時候bean還沒有實(shí)例化,此時bean剛被解析為BeanDefinition對象。