Spring - IOC

基础

bean.xml
定义需要实例化对象的全限定类名以及类之间依赖关系描述

BeanFactory
IOC容器,通过反射技术来实例化对象并维护对象之间的依赖关系

Spring框架IOC实现

纯xml和xml+注解模式下IOC容器启动方式:

JavaSE应用
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");

ApplicationContext applicationContext = new FileSystemXmlApplicationContext("c:/bean.xml");
JavaWeb应用
通过监听器 ContextLoaderListener 去加载xml

纯注解模式下IOC容器启动方式:

JavaSE应用
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
JavaWeb应用
通过监听器 ContextLoaderListener 去加载xml

TIPS:学习注解的技巧,找xml中标签和注解的一一对应关系即可

BeanFactory和ApplicationContext

BeanFactory是Spring框架中IOC容器的顶层接口,它只是用来定义一些基础功能,定义一些基础规范,而ApplicationContext是它的一个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能。
通常我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的高级接口,比BeanFactory要拥有更多的功能,例如国际化支持和资源访问等等

启动IoC容器的方式

  • Java环境下启动IoC容器
  • ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
  • FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
  • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
  • Web环境下启动IoC容器
  • 从xml启动容器

 Archetype Created Web Application
 
 
   contextConfigLocation
   classpath:applicationContext.xml
 
 
 
   org.springframework.web.context.ContextLoaderListener
 

  • 从配置类启动容器

 Archetype Created Web Application
 
 
   contextClass
   org.springframework.web.context.support.AnnotationConfigWebApplicationContext
 
 
 
   contextConfigLocation
   com.hooda.config.SpringConfig
 
 
 
   org.springframework.web.context.ContextLoaderListener
 

实例化Bean的三种方式

  • 方式一:使用无参构造函数

  • 方式二:使用静态方法创建
public class DefinedBeanFactory {
  public static UserService getUserService() {
    return new UserServiceImpl();
  }
}

  • 方式三:使用实例化方法创建
public class DefinedBeanFactory {
  public UserService getUserService() {
    return new UserServiceImpl();
  }
}


Bean的属性及生命周期

  • 标签属性
    Id属性:用于给bean提供一个唯一标识。在一个标签内部,标识必须唯一
    class属性:用于指定创建Bean对象的全限定类名
    name属性:用于给Bean提供一个或多个名称,多个名称用空格分割
    factory-bean属性:用于指定创建当前Bean对象的工厂Bean的唯一标识。当指定了此属性后,class属性失效
    factory-method属性:用于指定创建当前Bean对象的工厂方法,如配合factory-bean属性使用,则class属性失效;如配合class属性使用,则方法必须是static的
    scope属性:用于指定Bean对象的作用范围,通常情况下就是singleton。当要用到多例模式时,可以配置为prototype
    init-method属性:用于指定Bean对象的初始化方法,此方法会在Bean对象装配后调用。必须是无参方法
    destory-method属性:用于指定Bean对象的销毁方法,此方法会在Bean对象销毁前执行。他只能为scope是singleton时起作用
  • 生命周期
    singleton
    单例模式的bean对象生命周期和容器一样
    prototype
    多例模式的bean对象,Spring框架只负责创建,不负责销毁
  • 构造注入(constructor-arg)
    name:用于给构造函数中指定名称的参数赋值
    index:用于给构造函数中指定索引位置的参数赋值
    value:用于指定基本类型或者String类型的数据
    ref:用于指定其他Bean类型的数据,填写的是其他Bean唯一标识
  • set注入(property)
    name:指定注入时调用的set方法名称
    value:指定注入的数据,支持基本类型或者String类型
    ref:指定引用的数据,支持其他Bean类型,写的是其他Bean唯一标识
  • DI注解实现方式
    @Autowired:Spring提供的注解,默认按照类型注入。如果出现一个类型有多个Bean值时,需要配合@Qualifier使用
    @Resource:J2EE提供的注解,默认按照name自动注入。如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到抛异常;如果指定了name,则从上下文中查找id匹配,找不到抛异常;如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或找到多个都会抛异常;
    TIPS:@Resource在JDK11中已经移除,如需使用,单独引入jar包
  • 纯注解
    @Configutation:表明当前类是一个配置类
    @ComponentScan:替代context:component-scan
    PropertySource:引入外部数学配置文件
    @Import:引入其他配置类
    @Value:对变量赋值,可以直接赋值,也可以使用${}读取资源配置文件中的信息
    @Bean:将方法返回对象加入SpringIOC容器

Spring IOC高级特性

lazy-init延迟加载
ApplicationContext容器默认在启动服务时将所有singleton-bean进行实例化。设置lazy-init为true的bean将不会在ApplicationContext启动时提前被实例化,而是第一次向容器通过getBean获取时实例化
如果一个设置了立即加载的bean1引用了一个延迟加载的bean2,那么bean1在容器启动时被实例化,而bean2由于被bean1引用,所以也会被实例化
如果一个bean的scope属性为prototype时,即使设置了lazy-init=false,容器启动时也不会实例化bean,而是调用时实例化。
FactoryBean&BeanFactory
Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean),FactoryBean可以生成某一个类型的Bean实例,也就是说我们可以借助于它自定义Bean的创建过程。

// 可以让我们自定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean {
    // 返回FactoryBean创建的Bean实例,如果isSingleton=true,则该实例会放到Spring容器的单利对象池中
    @Nullable
    T getObject() throws Exception;

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

    // 返回作用域是否单例
    default boolean isSingleton() {
        return true;
    }
}

// 例
public class Company {
    private name, address, scale;
}
public class CompanyFactoryBean implements FactoryBean {
    private String companyInfo;
    
    @Override
    public Company getObject() throws Exception {
        // 模拟创建复杂对象
        Company company = new Company();
        company.setName(companyInfo.split(",")[0]);
        ...
        return company;
    }
    @Override
    public Class getObjectType() {
        return Company.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}
// xml配置

    

// 获取CompanyFactoryBean产生的对象
Object companyBean = applicationContext.getBean("companyBean");
// 获取CompanyFactoryBean对象本身
Object companyBean = applicationContext.getBean("&companyBean");

后置处理器
Spring提供了两种后置处理bean的扩展接口,分别为BeanPostProcessor和BeanFactoryPostProcessor,两者在使用上是有所区别的。
BeanPostProcessor是针对Bean级别的处理,在Bean对象实例化之后。

public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; }
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }
}

该接口提供了两个方法,分别在Bean的初始化方法前后执行,具体初始化方法指的什么方法,类似我们在定义bean时,init-method所指定的方法
定义一个类实现了BeanPostProcessor,默认会对整个Spring容器中所有bean进行处理。如果要对某个bean处理,可以通过beanName来判断我们将要处理的具体bean
BeanFactoryPostProcessor是针对BeanFactory级别的处理,是对整个Bean的工厂进行处理。典型应用:PropertyPlaceHolderConfigurer

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

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


我们可以根据getBeanDefinition方法找到BeanDefinition对象,然后对定义的属性进行修改。
TIPS:调用BeanFactoryPostProcessor方法时bean还没有实例化,此时bean刚被解析成BeanDefinition对象

Bean生命周期关键时机点

Bean对象构造函数调用时机&InitializingBean之afterPropertiesSet
在AbstractApplicationContext.refresh()的finishBeanFactoryInitialization(BeanFactory)处
BeanFactoryPostProcessor初始化和调用时机
BeanFactoryPostProcessor初始化和调用都在AbstractApplicationContext.refresh()的invokeBeanFactoryPostProcessor(beanFactory)
BeanPostProcessor初始化和调用时机
BeanPostProcessor初始化在AbstractApplicationContext.refresh()的registerBeanPostProcessors(beanFactory)处
postProcessBeforeInitialization&postProcessAfterInitialization都在AbstractApplicationContext.refresh()的finishBeanFactoryInitialization(beanFactory)处
Spring IOC容器初始化主流程

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        // 第一步 刷新前的预处理
        prepareRefresh();
        /*
          第二步 获取BeanFactory,默认实现DefaultListableBeanFactory
          加载BeanDefinition并注册到BeanDefinitionRegistry中
        */
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 第三步 BeanFactory的预准备工作(进行一些设置,比如Context的类加载器等)
        prepareBeanFactory(beanFactory);
        try {
            // 第四步:BeanFactory准备工作完成后进行的后置处理工作
            postProcessBeanFactory(beanFactory);
            // 第五步:实例化并调用实现了BeanFactoryPostProcessor接口的Bean
            invokeBeanFactoryPostProcessors(beanFactory);
            // 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean前后执行
            registerBeanPostProcessors(beanFactory);
            // 第七步:初始化MessageSource组件(做国际化功能;消息绑定;消息解析)
            initMessageSource();
            // 第八步:初始化事件派发器
            initApplicationEventMulticaster();
            // 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑
            onRefresh();
            // 第十步:注册实现了ApplicationListener接口的监听器
            registerListener();
            /*
              第十一步:初始化所有剩下的非懒加载单例bean
              初始化创建非懒加载方式的单例bean(未设置属性)
                  填充属性
                  初始化方法调用(比如调用afterPropertiesSet方法,init-method方法)
                  调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
             */
            finishBeanFactoryInitialization(beanFactory);
            // 第十二步:完成context刷新,主要是调用LifecycleProcessor的onRefresh方法,并且发布事件(ContextRefreshEvent)
            finishRefresh();
        }
        ...
    }
}

BeanFactory创建流程

获取BeanFactory子流程

时序图

BeanDefinition加载解析级注册子流程
Resource定位:指对BeanDefinition的资源定位过程。找到定义JavaBean信息的xml文件并将其封装成Resource对象
BeanDefinition载入:把用户定义好的JavaBean表示为IoC容器内部的数据结构
step1:入口在AbstractRefreshableApplicationContext#refreshBeanFactory方法中调用了loadBeanDefinition(beanFactory)
step2:调用多个类的loadBeanDefinition方法,链 AbstractXmlApplicationContext -> AbstractBeanDefinitionReader -> XmlBeanDefinitionReader#doLoadBeanDefinitions,在doLoadBeanDefinition方法中执行registerBeanDefinition(doc, resource)方法

你可能感兴趣的:(Spring - IOC)