Ioc高级特性
Lazy-Init延迟加载
Bean的延迟加载,通常bean会在ApplicationContext容器启动时,默认将bean进行实例化,并加载到SingletonObjects单例池中,但是如果我们为bean配置了Lazy-init以后,就不会再容器启动时创建并配置bean,而是会在我们第一次getbean的时候进行加载。
下面我们用一个例子来进行演示
lazy-init demo
首先我们新建两个类,一个叫Common,一个叫LazyInit
然后,我们对ApplicationContext.xml进行配置,一个使用延迟加载,一个为普通加载
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
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
">
<!--使用懒加载-->
<bean id="lazyInit" class="LazyInit" lazy-init="true"></bean>
<!--不使用懒加载-->
<bean id="common" class="Common"></bean>
</beans>
接着我们用测试方法去debug一下,看看使用懒加载的bean什么时候会加载到singletonObjects里面
方法执行到断点处,这个时候还没有调用getbean方法,我们看一下singletonobjects里面现在都有哪些bean
我们这个时候可以看出,没有使用lazy-init的bean,在applicationContext容器初始化的时候就已经将bean加载到singletonobjects单例池里面了,而使用lazy-init的bean还没有被加载,那么我们接着往下走一步
这个时候我们发现singletonobjects单例池中加载了lazy这个bean,所以使用lazy-init延迟加载的bean会在第一次被getBean的时候进行实例化
那么这种bean适合什么样的使用场景呢?
1、开启延迟加载能在一定程度上提高容器的启动和运转效率
2、对于一些不常用的类,我们可以开启延迟加载,这样就不用从一开始就占用资源
FactoryBean
说起FactoryBean我们很容易想起另一个BeanFactory,但是我们要清楚,他们并不是一个东西。
FactoryBean和BeanFactory
首先我们从命名上就可以看出他们的区别,FactoryBean是一个Bean,而BeanFactory明显是一个工厂。
熟悉Spring的同学知道BeanFactory是spring的顶级接口,他用来生产和管理bean,我们一般使用的是他的子接口,比如我们最常用的ApplicationContext等。
在spring中Bean有两种,一种是普通Bean,而另一种是工厂bean,也就是我们现在所说的FactoryBean,它的作用是能够生成某个类型的实例并返给我们,这也就是说我们可以去自定义实例对象
使用FactoryBean
首先我们看一下FactoryBean接口里面都有哪些方法,以及其作用是什么
// 可以让我们⾃定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {
@Nullable
// 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器
的单例对象缓存池中Map
T getObject() throws Exception;
@Nullable
// 返回FactoryBean创建的Bean类型
Class<?> getObjectType();
// 返回作⽤域是否单例
default boolean isSingleton() {
return true;
}
}
然后我们写一个关于FactoryBean的小demo
Demo
mode
public class User {
private String name;
private String sex;
private int age;
}
UserFactoryBean
public class UserFactoruyBean implements FactoryBean<User> {
// 通过set方法注入user用户信息
private String userIfo;
public void setUserIfo(String userIfo){
this.userIfo=userIfo;
}
@Override
public User getObject() throws Exception {
User user = new User();
String[] split = userIfo.split(",");
user.setName(split[0]);
user.setSex(split[1]);
user.setAge(Integer.parseInt(split[2]));
return user;
}
@Override
public Class<?> getObjectType() {
return User.class;
}
@Override
public boolean isSingleton() {
return false;
}
}
Beans.xml
<bean id="userFactoryBean" class="Factory.UserFactoruyBean">
<!--为属性赋值-->
<property name="userIfo" value="yhy,男,26"></property>
</bean>
接着我们来测试一下
@Test
public void factoryBeanTest(){
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("classpath:Beans.xml");
User userFactoryBean = (User) applicationContext.getBean("userFactoryBean");
System.out.println(userFactoryBean);
}
测试结果
我们所实现的FactoryBean为我们产出了对应的bean对象,但是如果我们想要获取的是FactoryBean本身的实例对象呢?
那我们就需要在获取bean对象的时候,在对象名称前面加一个"&"符号,这样就获取的是FactoryBean本身
我们修改一下测试方法,再进行一下验证
@Test
public void factoryBeanTest(){
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("classpath:Beans.xml");
// 获取User对象
Object userBean = applicationContext.getBean("userFactoruyBean");
// 获取UserFactoryBean对象
Object userFactoruyBean = applicationContext.getBean("&userFactoruyBean");
System.out.println(userBean);
System.out.println(userFactoruyBean);
}
结果正确
后置处理器
spring提供了两种后置处理bean的接口,一个是BeanPostProcessor,另一个是BeanFactoryPostProcessor。
顾名思义,两者之中BeanPostProcessor是对Bean做后置处理,而BeanFactoryPostProcessor是对BeanFactory做后置处理
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;
}
}
BeanPostProcessor有两个方法,postProcessBeforeInitialization和postProcessAfterInitialization他们分别可以在Bean的初始化之前和初始化之后进行处理。
BeanPostProcessor默认会对容器内的所有Bean执行处理
同样我们写一个小demo
首先我们写两个类,一个是TestModel,另一个是PostProcessorTestModel。这样两个空类,我们不用添加任何方法,只需要建立两个空类就可以,仅测试使用。
然后我们写一个后置处理器
public class PPTPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 打印前置处理bean
System.out.println("before"+beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 打印后置处理bean
System.out.println("after"+beanName);
return bean;
}
}
BeanPostProcessor后置处理器是对已经注入的Bean进行处理,所以我们要编辑一下beans.xml配置文件
<bean id="testModel" class="TestModel"></bean>
<bean id="postProcessorTestModel" class="PostProcessorTestModel"></bean>
<bean id="pPTPostProcessor" class="PPTPostProcessor"></bean>
然后写一个测试方法打一个断点测试一下
然后看结果
我们可以看到,后置处理器在bean注入完成后,对所有的bean进行了处理
BeanFactoryPostProcessor
BeanFactoryPostProcessor是对整个BeanFactory进行处理
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
该接口只提供了一个方法postProcessBeanFactory,这个方法的参数是ConfigurableListableBeanFactory
ConfigurableListableBeanFactory同样是一个接口,我们可以看到这个接口下的所有方法
其中getBeaneDefinition可以找到我们定义的BeanDefinition对象,那么BeanDefinition对象是什么呢?就是我们在beans.xml中的bean标签,spring解析bean标签内容会存储在BeanDefinition对象中,注意他不是实例化后的bean对象。
在BeanDefinition对象中有以下方法
我们可以通过调用这些方法修改bean标签中定义的属性
照例,我们通过修改刚才的demo来展示一下
TestModel
public class TestModel {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "TestModel{" +
"name='" + name + '\'' +
'}';
}
}
BeanFactoryPostProcessor
public class PPTBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
// 获取bean
BeanDefinition testModel = configurableListableBeanFactory.getBeanDefinition("testModel");
// 获取属性
MutablePropertyValues propertyValues = testModel.getPropertyValues();
// 为属性赋值
propertyValues.add("name","testModelName");
}
}
Test方法测试
@Test
public void postProcessorTest(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:beans.xml");
Object testModel = applicationContext.getBean("testModel");
System.out.println(testModel);
}
结果
赋值成功