lazy-Init 延迟加载

ApplicationContext 容器的默认行为是在启动服务器时将所有 singleton bean 提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext 实例会创建并配置所有的 singleton bean。

1
2
3
<bean id="lazyResult" class="com.lagou.edu.pojo.Result"/>
该bean默认的设置为
<bean id="lazyResult" class="com.lagou.edu.pojo.Result" lazy-init="false"/>

lazy-init=”false”,立即加载,表示在spring启动时,立刻进行实例化。如果不想让一个singleton bean 在 ApplicationContext 实现初始化时被提前实例化,那么可以将 lazy-init 设置为true。

1
<bean id="lazyResult" class="com.lagou.edu.pojo.Result" lazy-init="true"/>

设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,而是第一次向容器通过 getBean 获取 bean 时实例化的。
如果一个设置了立即加载的 bean1,引用了一个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,而 bean2 由于被 bean1 引用,所以也被实例化,这种情况也符合延时加载的 bean 在第一次调用时才被实例化的规则。
也可以在容器层次中通过在元素上使用 “default-lazy-init” 属性来控制延时初始化。如下面配置:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8" ?>
<beans default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

</beans>

如果一个 bean 的 scope 属性为 scope=”pototype” 时,即使设置了 lazy-init=”false”,容器启动时也不会实例化bean,而是调用 getBean 方法实例化的。

FactoryBean

Spring 中 Bean 有两种,一种是普通 Bean,一种是工厂 Bean(FactoryBean),FactoryBean 可以生成某一个类型的 Bean 实例,我们可以借助于它自定义 Bean 的创建过程。
Bean 创建的三种方式中的静态方法和实例化方法和 FactoryBean 作用类似,FactoryBean 使用较多,尤其在 Spring 框架一些组件中会使用,还有其他框架和 Spring 框架整合时使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface FactoryBean<T> {

// 自定义Bean的创建过程(完成复杂Bean的定义)
@Nullable
T getObject() throws Exception;

// 返回FactoryBean创建的Bean类型
@Nullable
Class<?> getObjectType();

// 返回作用域是否单例
default boolean isSingleton() {
return true;
}

}

案例

1
2
3
4
5
6
7
8
9
public class Company {

private String name;

private String address;

private int scale;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class CompanyFactoryBean implements FactoryBean<Company> {

//公司名称,地址,规模
private final String companyInfo;

public CompanyFactoryBean(String companyInfo) {
this.companyInfo = companyInfo;
}

@Override
public Company getObject() throws Exception {
String[] info = companyInfo.split(",");
Company company = new Company();
company.setName(info[0]);
company.setAddress(info[1]);
company.setScale(Integer.parseInt(info[2]));

return company;
}

@Override
public Class<?> getObjectType() {
return Company.class;
}

@Override
public boolean isSingleton() {
return true;
}

}

xml 配置:

1
2
3
<bean id="companyBean" class="com.litianyi.factory.CompanyFactoryBean">
<constructor-arg value="拉钩,中关村,500"/>
</bean>

测试方法:

1
2
3
4
5
6
7
8
9
10
public class IoCTest {
@Test
public void test() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
Object companyBean = applicationContext.getBean("companyBean");
Object companyBeanFactory = applicationContext.getBean("&companyBean");
System.out.println(companyBean);
System.out.println(companyBeanFactory);
}
}

结果:

1
2
Company{name='拉钩', address='中关村', scale=500}
com.litianyi.factory.CompanyFactoryBean@49070868

后置处理器

Spring 提供了两种后处理 bean 的扩展接口,分别为 BeanPostProcessor 和
BeanFactoryPostProcessor,两者在使用上是有所区别的。

工厂初始化(BeanFactory) –> Bean 对象
在 BeanFactory 初始化之后可以使用 BeanFactoryPostProcessor 进行后置处理做一些事情。
在 Bean 对象实例化(并不是Bean的整个生命周期完成,此时还不是一个 SpringBean)之后可以使用 BeanPostProcessor 进行后置处理做一些事情。

BeanPostProcessor

BeanPostProcessor 是针对 Bean 级别的处理,可以针对某个具体的 Bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
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的初始化方法前和初始化方法后执行,类似我们在定义bean时,定义了 init-method 所指定的方法。
定义一个类实现了 BeanPostProcessor,默认是会对整个 Spring 容器中所有的 bean 进行处理。如果要对具体的某个 bean 处理,可以通过方法参数判断,两个类型参数分别为Object 和 String,第一个参数是每个bean 的实例,第二个参数是每个 bean 的 name 或者 id 属性的值。所以我们可以通过第二个参数,来判断我们将要处理的具体的 bean。
注意:处理是发生在 Spring 容器的实例化和依赖注入之后。(可以参照 Spring 生命周期)

BeanFactoryPostProcessor

BeanFactory 级别的处理,是针对整个 Bean 的工厂进行处理。
典型应用:PropertyPlaceholderConfigurer

1
2
3
4
5
6
@FunctionalInterface
public interface BeanFactoryPostProcessor {

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

此接口只提供了一个方法,方法参数为 ConfigurableListableBeanFactory,该参数类型定义了一些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface ConfigurableListableBeanFactory
extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {

void ignoreDependencyType(Class<?> type);

void ignoreDependencyInterface(Class<?> ifc);

void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue);

boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
throws NoSuchBeanDefinitionException;

BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

Iterator<String> getBeanNamesIterator();

void clearMetadataCache();

void freezeConfiguration();

boolean isConfigurationFrozen();

void preInstantiateSingletons() throws BeansException;
}

其中有个方法名为 getBeanDefinition 的方法,我们可以根据此方法,找到我们定义 bean 的 BeanDefinition对象。然后我们可以对定义的属性进行修改,以下是BeanDefinition 中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

int ROLE_APPLICATION = 0;

int ROLE_SUPPORT = 1;

int ROLE_INFRASTRUCTURE = 2;

void setParentName(@Nullable String parentName);

@Nullable
String getParentName();

void setBeanClassName(@Nullable String beanClassName);

@Nullable
String getBeanClassName();

void setScope(@Nullable String scope);

@Nullable
String getScope();

void setLazyInit(boolean lazyInit);

boolean isLazyInit();

void setDependsOn(@Nullable String... dependsOn);

@Nullable
String[] getDependsOn();

void setAutowireCandidate(boolean autowireCandidate);

boolean isAutowireCandidate();

void setPrimary(boolean primary);

boolean isPrimary();

void setFactoryBeanName(@Nullable String factoryBeanName);

@Nullable
String getFactoryBeanName();

void setFactoryMethodName(@Nullable String factoryMethodName);

@Nullable
String getFactoryMethodName();

ConstructorArgumentValues getConstructorArgumentValues();

default boolean hasConstructorArgumentValues() {
return !getConstructorArgumentValues().isEmpty();
}

MutablePropertyValues getPropertyValues();

default boolean hasPropertyValues() {
return !getPropertyValues().isEmpty();
}

void setInitMethodName(@Nullable String initMethodName);

@Nullable
String getInitMethodName();

void setDestroyMethodName(@Nullable String destroyMethodName);

@Nullable
String getDestroyMethodName();

void setRole(int role);

int getRole();

void setDescription(@Nullable String description);

@Nullable
String getDescription();

boolean isSingleton();

boolean isPrototype();

boolean isAbstract();

@Nullable
String getResourceDescription();

@Nullable
BeanDefinition getOriginatingBeanDefinition();

}

方法名字类似我们 bean 标签的属性,setBeanClassName 对应 bean 标签中的 class 属性,所以当我们拿到 BeanDefinition 对象时,我们可以手动修改 bean 标签中所定义的属性值。
BeanDefinition 对象:我们在 XML 中定义的 bean 标签,Spring 解析 bean 标签成为一个 JavaBean, 这个JavaBean 就是 BeanDefinition。
注意:调用 BeanFactoryPostProcessor 方法时,这时候 bean 还没有实例化,此时 bean 刚被解析成 BeanDefinition 对象。