Spring IoC 扩展特性

一、lazy-init 延迟加载

1、定义

1)原本默认的配置

<bean id="testBean" class="cn.lagou.LazyBean"/>
<!-- 该 bean 默认的设置为: -->
<bean id="testBean" class="cn.lagou.LazyBean" lazy-init="false"/>

lazy-init = “false”,立即加载,表示在 Spring 启动时,立刻进行实例化。

2)延迟加载的配置

如果不想让一个 singleton bean 在 ApplicationContext 实现初始化时被提前实例化,那么可以将 bean 设置为延迟实例化。

<bean id="testBean" class="cn.lagou.LazyBean" lazy-init="true"/>

设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,而是第一次向容器通过 getBean 索取 bean 时实例化的。

如果一个设置了立即加载的 bean1,引用了一个延迟加载的 bean2,那么 bean1 在容器启动时被实例化,而 bean2 由于被 bean1 引用,所以也被实例化,这种情况也符合延迟加载的 bean 在第一次调用时才被实例化的规则。

3)全局配置

也可以在容器层次中通过在元素上使用 “default-lazy-init” 属性来控制延迟初始化,如下配置:

<beans default-lazy-init="true">
    <!-- no beans will be eagerly pre-instantiated... -->
</beans>

4)注意事项

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

2、应用场景

  1. 开启延迟加载一定程度提高容器启动和运转性能;
  2. 对于不常使用的 Bean 设置延迟加载,这样偶尔使用的时候再加载,不必要从一开始该 Bean 就占用资源。

二、FactoryBean 和 BeanFactory

1、BeanFactory

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

2、FactoryBean

Spring 中 Bean 有两种,一种是普通 Bean,一种是工厂 Bean(FactoryBean),FactoryBean 可以生成某一个类型的 Bean 实例(返回给我们),也就是说我们可以借助于它自定义 Bean 的创建过程。

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

例子:

想看一下 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;
    }
}

Company 类。

public class Company {
    
    private String name;
    private String address;
    private int scale;
    
    // get、set 方法略
    
    @Override
    public String toString() {
        return "Company{" + 
                    "name='" + name + '\'' + 
                    ", address='" + address + '\'' +
                    ", scale=" + scale +
                '}';
    }
}

CompanyFactoryBean 类。

public class CompanyFactoryBean implements FactoryBean<Company> {
    
    private String companyInfo; 
    
    // 公司名称,地址,规模
    public void setCompanyInfo(String companyInfo) {
        this.companyInfo = companyInfo;
    }
    
    @Override
    public Company getObject() throws Exception {
        // 模拟创建复杂对象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.demo.edu.factory.CompanyFactoryBean">
    <property name="companyInfo" value="拉勾,中关村,500"/>
</bean>

测试,获取 FactoryBean 产生的对象。

Object companyBean = applicationContext.getBean("companyBean");
System.out.println("bean:" + companyBean);
// 结果如下
bean:Company{name='拉勾', address='中关村', scale=500}

测试,获取 FactoryBean,需要在 id 之前添加“&”。

Object companyBean = applicationContext.getBean("&companyBean");
System.out.println("bean:" + companyBean);
// 结果如下 
bean:com.demo.edu.factory.CompanyFactoryBean@53f6fd09

三、后置处理器

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

1、两者区别

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

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

2、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 的 name 或者 id 属性的值。
    所以我们可以通过第二个参数,来判断我们将要处理的具体的 bean。
  • 注意:处理是发生在 Spring 容器的实例化和依赖注入之后。

3、BeanFactoryPostProcessor

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

@FunctionalInterface
public interface BeanFactoryPostProcessor {

   void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
           throws BeansException;
}

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

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 中的部分源码。

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();
   ResolvableType getResolvableType();
   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 对象。

文章内容输出来源:拉勾教育Java高薪训练营;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jason559

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值