Spring之IOC高级特性

Spring IOC 的高级特性

1. lazy-Init 延迟加载

Bean的延迟加载(延迟创建)

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

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

lazy-init=“false”,⽴即加载,表示在spring启动时,⽴刻进⾏实例化。如果不想让⼀个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean设置为延迟实例化。设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,⽽是第⼀次向容器通过 getBean 索取 bean 时实例化的。

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

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

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

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

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

延迟加载应用场景

  • 开启延迟加载⼀定程度提⾼容器启动和运转性能
  • 对于不常使⽤的 Bean 设置延迟加载,这样偶尔使⽤的时候再加载,不必要从⼀开始该 Bean 就占⽤资源

2. FactoryBean 和 BeanFactory

简单介绍工厂bean(FactoryBean)

BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的⼀个⼯⼚,具体使⽤它下⾯的⼦接⼝类型,⽐如ApplicationContext;此处我们重点分析FactoryBean

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

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

获取FactoryBean,而不是其生产的对象则需要在id之前添加“&”

// 可以让我们⾃定义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;
}
}

具体代码实现

/**
 * 苹果类
 * @author lane
 * @date 2021年03月26日 下午3:35
 */
public class Apple {

    private int id;
    private String color;

    public void setId(int id) {
        this.id = id;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "id=" + id +
                ", color='" + color + '\'' +
                '}';
    }
}

/**
 * 苹果bean的加工厂既是 factoryBean
 * @author lane
 * @date 2021年03月26日 下午3:38
 */
public class AppleFactoryBean implements FactoryBean<Apple> {

    private String appleInfo;

    public void setAppleInfo(String appleInfo) {
        this.appleInfo = appleInfo;
    }

    @Override
    public Apple getObject() throws Exception {

        String[] split = appleInfo.split("-");
        Apple apple = new Apple();
        apple.setId(Integer.valueOf(split[0]));
        apple.setColor(split[1]);
        return apple;
    }

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

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

    @Override
    public String toString() {
        return  "这是apple的factory 不是apple了";
    }
}
<!-- xml配置-->
<bean id="apple" class="com.lagou.edu.pojo.Apple"></bean>
<!--    工厂bean-->
    <bean id="appleBean" class="com.lagou.edu.factory.AppleFactoryBean">
        <property name="appleInfo" value="1-红色"></property>
    </bean>
 				//从工厂生产出的苹果
        Apple apple = (Apple) applicationContext.getBean("appleBean");
        System.out.println(apple);
        //加上&符号就把工厂放入Spring容器中了
        AppleFactoryBean appleFactoryBean = (AppleFactoryBean) applicationContext.getBean("&appleBean");
        System.out.println(appleFactoryBean);
				//打印的效果
				//Apple{id=1, color='红色'} 这是apple的factory 不是apple了

3. Spring 的后置处理器

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

在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情(比如替换数据库配置文件的占位符)。

在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处理做⼀些事情

注意:对象不⼀定是springbean,⽽springbean⼀定是个对象

SpringBean在工厂初始化之后的生命周期

image-20210326163118599

image-20210326163234844

BeanFactoryPostProcessor

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

BeanFactory级别的处理 是针对整个Bean的⼯⼚进⾏处理,典型应⽤:PropertyPlaceholderConfigurer 此接⼝只提供了⼀个⽅法,⽅法参数为ConfigurableListableBeanFactory,该参数类型定义了⼀些⽅法

image-20210326163502147

image-20210326163535210

其中有个⽅法名为getBeanDefinition的⽅法,我们可以根据此⽅法,找到我们定义bean 的BeanDefinition对象。然后我们可以对定义的属性进⾏修改,以下是BeanDefinition中的⽅法,⽅法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿到BeanDefinition对象时,我们可以⼿动修改bean标签中所定义的属性值。

image-20210326163610835

BeanPostProcessor

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

image-20210326163943752

该接⼝提供了两个⽅法,分别在Bean的初始化⽅法前和初始化⽅法后执⾏,具体这个初始化⽅法指的是什么⽅法,类似我们在定义bean时,定义了init-method所指定的⽅法

定义⼀个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进⾏处理。如果要对具体的某个bean处理,可以通过⽅法参数判断,两个类型参数分别为Object和String,第⼀个参数是每个bean的实例,第⼆个参数是每个bean的name或者id属性的值。所以我们可以通过第⼆个参数,来判断我们将要处理的具体的bean。注意:处理是发⽣在Spring容器的实例化和依赖注⼊之后。在Spring内部可用于事务和aop相关生成代理类。

代码实现Spring生命周期的接口方法(同上)

普通类实现Spring生命周期中接口

/**
 * @author lane
 * @date 2021年03月26日 下午3:35
 */
public class Apple implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean  {

    private int id;
    private String color;

    public void setId(int id) {
        this.id = id;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "id=" + id +
                ", color='" + color + '\'' +
                '}';
    }
    @Override
    public void setBeanName(String s) {
        System.out.println("我的名字是"+s);
    }
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("我是在哪个工厂创建的"+beanFactory);
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("高级容器接口"+applicationContext);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行生命周期中的afterPropertiesSet");
    }
    public void initMethod(){
        System.out.println("执行了init-method 方法");
    }
    @PostConstruct
    public void postConstruct(){
        System.out.println("执行postConstruct 注解的方法");
    }
    @PreDestroy
    public void preDestory(){
        System.out.println("执行preDestroy方法");
    }
}

Spring的后置处理器之BeanPostProcessor

/**
 * 拦截实例化之后的对象(实例化了且属性注入了)
 * @author lane
 * @date 2021年03月26日 下午4:49
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      //默认拦截所有的,指定某一个bean
        if ("apple".equalsIgnoreCase(beanName)){
            System.out.println("MyBeanPostProcessor 的before方法执行了"+beanName+"被拦截了,挺急的");
        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if ("apple".equalsIgnoreCase(beanName)){
            System.out.println("MyBeanPostProcessor 的after方法执行了"+beanName+"被拦截了,不用急");
        }

        return bean;
    }

}

具体调用bean

 Apple apple2= (Apple) applicationContext.getBean("apple");
        System.out.println(apple2);

打印结果

我的名字是apple
我是在哪个工厂创建的org.springframework.beans.factory.support.DefaultListableBeanFactory@563f38c4:
高级容器接口org.springframework.context.support.ClassPathXmlApplicationContext@dd3b207, started on Fri Mar 26 16:26:26 CST 2021

MyBeanPostProcessor 的before方法执行了apple被拦截了,挺急的
执行postConstruct 注解的方法
执行生命周期中的afterPropertiesSet
执行了init-method 方法
MyBeanPostProcessor 的after方法执行了apple被拦截了,不用急

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值