文章目录

  • Spring IOC 高级特性
  • lazy-Init 延迟加载
  • BeanFactory 和 FactoryBean
  • 后置处理器
  • BeanPostProcessor
  • BeanFactoryPostProcessor


Spring IOC 高级特性

lazy-Init 延迟加载

ApplicationContext 容器的默认行为是在启动服务器时将所有的 singleton bean 提前进行实例化。

提前实例化意味着作为初始化过程的一部分,ApplicationContext 实例会创建并配置所有的singleton bean

如果我们想在Bean使用的时候再实例化它,SpringBean也可配置延迟加载,Spring为我们提供了以下两种方式来实现::

  • xml配置方式
<!-- lazy-init:延迟加载,true代表延迟,false代表立即加载,默认是false(立即加载) -->
<bean id="lazyResult" class="com.spring.myspring.pojo.Result"  lazy-init="true"/>
  • 注解方式
    @Lazy 在类上添加@Lazy注解

应用场景:

  • 开启延迟加载在一定程度上可以提高容器启动和运转性能
  • 对应不常用使用的 Bean 设置延迟加载,需要使用的时候再加载,不必从一开始就加载资源

普通Bean 的初始化是在容器启动初始化阶段执行的,而被 lazy-init = true 修饰的 bean 则是在从容器里第一次进行 context.getBean() 时实例化的。

Spring 启动的时候会把所有的bean信息(包括注解和xml)解析转换成Spring 能够识别的BeanDefinition 并存到HashMap 里供后面的初始化使用,然后对每个BeanDefinition 进行处理,如果是延迟加载的bean ,则在容器初始化的阶段不处理,其他的单例bean则在容器初始化阶段进行实例化并注入依赖。

总结 :

  • 对于被修饰为lazy-init的bean Spring 容器初始化阶段不会进行 init 并且依赖注入,当第⼀次 进⾏getBean时候才进⾏初始化并依赖注入
  • 对于非延迟加载的bean,getBean的时候会从缓存⾥头获取,因为容器初始化阶段 Bean 已经 初始化完成并缓存了起来

BeanFactory 和 FactoryBean

BeanFactory 接口是IOC容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean的一个工厂,具体使用它下面的子接口类型,比如 ApplicationContext

我们重点来看下 FactoryBean:

Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean),FactoryBean可以生产某一类型的Bean实例(返回给我们),也就是说我们可以借助它来定义Bean的创建过程,或者构建复杂对象。

Bean 创建的三种方式中,静态方法和实例方法和FactoryBean作用类似,FactoryBean使用较多,尤其在Spring框架一些组件中会使用,还有其他框架整合Spring时使用

FactoryBean 接口

/**
 * 可以让我们自定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {

	/**
	 * Return an instance (possibly shared or independent) of the object
	 * managed by this factory.
	 * 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器的单例对象缓存池中的Map
	 */
	@Nullable
	T getObject() throws Exception;

	/**
	 * Return the type of object that this FactoryBean creates,
	 * 返回FactoryBean创建的Bean类型
	 */
	@Nullable
	Class<?> getObjectType();

	/**
	 * Is the object managed by this factory a singleton? That is,
	 * 返回作用域是否单例
	**/
	default boolean isSingleton() {
		return true;
	}

}

接下来,我们创建一个Config 类,来演示自定义类的创建过程:

Config 类:

public class Config {
    private String url;
    private int port;
    private String username;
    private String password;
    private String charset;
    private int timeout;
}

ConfigFactoryBean类

该类实现了FactoryBean接口,用于生产自定义的Config对象,生成Bean的逻辑写在getObject 方法里

public class ConfigFactoryBean implements FactoryBean<Config> {

    private String info;

    public void setInfo(String info) {
        this.info = info;
    }

    @Override
    public Config getObject() throws Exception {
        Config config = new Config();
        String[] strings = info.split(",");
        config.setUrl(strings[0]);
        config.setPort(Integer.parseInt(strings[1]));
        config.setUsername(strings[2]);
        config.setPassword(strings[3]);
        config.setCharset(strings[4]);
        config.setTimeout(Integer.parseInt(strings[5]));
        return config;
    }

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

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

在applicationContext.xml文件里配置Bean

<bean id="config" class="com.spring.myspring.factory.ConfigFactoryBean">
    // 注入info,用于构建config
    <property name="info" value="localhost,3306,root,root,utf-8,3600"/>
</bean>

测试

@Test
public void testFactoryBean(){
    // 启动容器(容器初始化)
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    Object config = applicationContext.getBean("config");
    System.out.println(config);
    applicationContext.close();
}

输出:

Config{url='localhost', port=3306, username='root', password='root', charset='utf-8', timeout=3600}

可以看到,FactoryBean正常帮我们生产出来了Config对象

后置处理器

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

工厂初始化(BeanFactory) ----> Bean对象

  • 在BeanFactory初始化之后可以使用BeanFactoryPostProcessor 进行后置处理做一些事情
  • 在Bean对象实例化(并不是Bean的整个生命周期完成)之后可以使用BeanPostProcessor进行后置处理做一些事情

注意:对象不一定是springbean,而springbean一定是个对象

BeanPostProcessor

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

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的nam或者id属性的值。所以我们可以通过第二个参数,来判断我们要处理的具体的Bean。

/**
 * 拦截实例化之后的对象(实例化了并且属性注入了)
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if("user".equalsIgnoreCase(beanName)){
            System.out.println("**********postProcessBeforeInitialization 执行了**********");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if("user".equalsIgnoreCase(beanName)){
            System.out.println("**********postProcessAfterInitialization 执行了**********");
        }
        return bean;
    }
}

我们来看下Bean的创建过程的执行顺序

<bean id="user" class="com.lagou.edu.pojo.User"  init-method="initMethod"></bean>
public class User {
    private Integer id;
    private String username;

    public void initMethod(){
        System.out.println("**********initMethod**********");
    }
}

测试方法

@Test
public void testHandler(){
    // 启动容器(容器初始化)
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    Object user = applicationContext.getBean("user");
    System.out.println(user);
    applicationContext.close();
}

输出:

**********postProcessBeforeInitialization 执行了**********
**********initMethod**********
**********postProcessAfterInitialization 执行了**********

可以看到 BeanPostProcessor的before和after是在initMethod 前后执行的

注意:处理是发生在Spring容器实例化和依赖注入之后。

java spring的接口延时 spring延迟加载_java spring的接口延时

BeanFactoryPostProcessor

BeanFactory级别的处理,是针对整个Bean的工厂进行处理

@FunctionalInterface
public interface BeanFactoryPostProcessor {

	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

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

java spring的接口延时 spring延迟加载_java spring的接口延时_02


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

java spring的接口延时 spring延迟加载_初始化_03

方法名字类似我们Bean标签的属性,setBeanClassName对应<bean>标签中的class属性,所以我们当我们拿到BeanDefinition对象时,我们可以手动修改<bean>标签中所定义的属性值。

BeanDefinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为⼀个 JavaBean, 这个JavaBean 就是 BeanDefinition 注意:调⽤ BeanFactoryPostProcessor ⽅法时,这时候bean还没有实例化,此时 bean 刚被解析成 BeanDefinition对象