avatar

SpringIOC容器设计

Spring IOC容器回顾

Spring IOC容器启动方式

  • 纯XML方式启动:bean信息定义全部配置在xml中
    • javaSE应用:
      //javaSE两种方式
      ApplicationContext context = new ClassPathXmlApplicationContext("path");
      ApplicationContext context1 = new FileSystemXmlApplicationContext("path");
      `
    • javaWeb应用:ContextLoaderListener监听器去加载xml
  • xml+注解混合:一部分存在于bean中一部分存在于注解中,一般自己开发的使用注解,第三方jar包使用xml加到容器中
  • 纯注解模式:所有的bean都存在于注解定义
    • javaSE应用:
      //AnnotationConfigApplicationContext可以传入bean工厂,也可以传入主配置类class
      ApplicationContext context = new AnnotationConfigApplicationContext();
    • javaWeb应用:ContextLoaderListener监听器去加载注解配置类

      BeanFactory与AppliactionContext关系

      BeanFactory是SpringIOC容器的顶级接口,只是用来定义一些基础功能,是SpringIOC的基础容器
      ApplicationContextBeanFactory的一个子接口,是SpringIOC的高级接口。

BeanFactory主要是用于管理bean,而AppliactionContext除了管理bean的作用,还支持了配置文件的加载(XML、java配置类)、国际化支持。这里体现了Spring的设计巧妙

Spring IOC容器创建bean的三种方式

  • 使用无参构造器(推荐)
    <bean id="connectUtils" class="com.lagou.edu.utils.ConnectionUtils"/>
  • 该方式主要是为了我们自己new的对象加到SpringIOC容器中进行管理,使用静态方法
    • 首先需要创建一个静态方法,静态方法返回值就是加到容器中的对象
      public class ConnectUtilsFactory {

      public static ConnectionUtils getConnectionUtils(){
      return new ConnectionUtils();
      }
      }
    • 将该方法通过配置加到容器中,factory-method就是静态方法
      <bean id="connectUtils" class="com.lagou.edu.factory.ConnectUtilsFactory" factory-method="getConnectionUtils"/>
  • 使用实例化方法将对象加入到容器中
    • 首先我们需要创建实例化方法
      public class ConnectUtilsFactory {
      public ConnectionUtils getConnect(){
      return new ConnectionUtils();
      }
      }
    • ConnectUtilsFactory加到容器中
      <bean id="connectUtilsFactory" class="com.lagou.edu.factory.ConnectUtilsFactory"/>
    • 然后在创建connectUtils时,使用之前创建的connectUtilsFactory中获取到对象
      <bean id="connectUtils" factory-bean="connectUtilsFactory" factory-method="getConnect"/>

      bean的作用范围及生命周期

      配置文件中scope指定bean的作用范围

      在xml中创建bean的时候可以通过scope定义bean的作用范围
      Scope 作用
      singleton 单例,IOC容器中只有一个该类的对象
      prototype 原型,每次使用该类的对象(getBean),都返回给你一个新的对象,Spring只创建对象,不管理对象
      request 针对web而言,每次请求都会返回一个对象
      session 针对web而言,每一个session会产生一个对象
      application 针对web而言,每一个ServletContext是一个对象
      websocket 针对webSocket而言,每有一个webSockert就会产生一个对象

如果scope缺失,默认值为singleton

bean的生命周期

参考[https://www.cnblogs.com/javazhiyin/p/10905294.html]
Spring在创建bean的过程是非常复杂的,这里仅仅简单的介绍一下bean创建的几个重要的环节

  • Spring启动之后,查找并加载需要被Spring管理的bean,进行Bean的实例化
  • Bean实例化后将Bean的引用和值注入到Bean的属性中
  • 如果Bean实现了BenaNameAware接口的话,Spring将Bena的id传递给setBeanName方法
  • 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
  • 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
  • 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法,初始化之前调用。
  • 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
  • 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法,初始化之后调用。
  • 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
  • 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。

    Spring DI依赖注入配置

  • set方法注入:set方法注入使用的是property标签,如果注入的是另外一个bean那么使用ref属性,如果注入的是普通值没那么使用的是value属性
  • 构造函数注入 使用constructor-arg标签,使用name或者index指定注入的是哪个参数需要注入

Spring IOC高级特性

lazy-init 延迟加载

延迟加载指的是singleton bean对象的延迟加载,或者说是延迟创建。
ApplicationContext容器的默认行为是在启动服务器时将所有singleton bean提前进行实例化。提前实例化意味着作为初始化过程中的一部分,ApplicationContext实例会创建并配置所有的singleton bean。
是否对bean进行延迟加载,是通过lazy-init属性进行控制的,默认为false。修改为true时,此bean就会设置为延迟实例化。

  • 使用xml时,
    <!--针对全局bean lazy-init的默认属性修改 default-lazy-init="true"-->
    <beans default-lazy-init="true" 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-->
    <bean id="connectUtils" class="com.lagou.edu.utils.ConnectionUtils" lazy-init="true"/>
  • 使用注解方式
    @lazy
    public class connectUtils {}

    应用场景

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

FactoryBean与BeanFactory

BeanFactory

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

FactoryBean

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

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

String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会保存到Spring的单例缓存池中
*/
@Nullable
T getObject() throws Exception;

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

default boolean isSingleton() {
return true;
}
}
使用方法
  • 首先我们需要定义一个需要注入到Spring容器中的Bean

    public class Company {

    private String name;

    private String address;

    private int scale;
    ...get/set...
    }
  • 然后我们创建一个工厂类,继承FactoryBean接口,该接口主要目的就是生产Company

    public class CompanyFactoryBean implements FactoryBean<Company> {

    public Company getObject() throws Exception {
    //创建复杂对象Company
    Company company = new Company();
    company.setAddress("我不知道");
    company.setName("my");
    company.setScale(1);
    return company;
    }

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

    /**
    * 是否单例
    * @return
    */
    public boolean isSingleton() {
    return true;
    }
    }
  • 我们需要将该类(CompanyFactoryBean)注册到Spring中

    <bean id="companyBean" class="com.zenshin.spring.CompanyFactoryBean"/>

    我们吧工厂类加入后,使用companyBean就可以从spring中获取Company;类实例

  • 从容器中取出该类

    @Test
    public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    //获取Company
    Object companyBean = context.getBean("companyBean");
    System.out.println(companyBean);
    //加入& 获取CompanyFactoryBean
    Object companyFactoryBean = context.getBean("&companyBean");
    System.out.println(companyFactoryBean);
    }

后置处理器

Spring提供了两种处理Bean的扩展接口,分别为BeanPostProcessorBeanFactoryPostProcessor,两者在使用上是有所区别的。
在BeanFactory初始化后可以使用BeanFactoryPostProcessor进行后置处理做一些事情
在Bean对象实例化(并不是Bean的整个生命周期完成)后可以使用BeanPostProcessor进行后置处理做一些事情

BeanPostProcessor

BeanFactoryPostProcessor

Spring源码解析

default boolean isSingleton() {
    return true;
}

}

文章作者: zenshin
文章链接: https://zlh.giserhub.com/2021/09/12/cl35o0nmw00rip4tg6afh8l6t/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zenshin's blog
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论