Spring-IOC

2021-09-08  本文已影响0人  Zeppelin421

基础

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容器的方式

  • ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
  • FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
  • AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
  • 从xml启动容器
<web-app>
 <display-name>Archetype Created Web Application</display-name>
 <!-- 配置Spring ioc容器的配置文件 -->
 <context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:applicationContext.xml</param-value>
 </context-param>
 <!-- 使用监听器启动Spring的IOC容器 -->
 <listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
</web-app>
  • 从配置类启动容器
<web-app>
 <display-name>Archetype Created Web Application</display-name>
 <!-- 告诉ContextLoaderListener我们使用注解的方式启动IOC容器 -->
 <context-param>
   <param-name>contextClass</param-name>
   <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
 </context-param>
 <!-- 配置启动类的全限定类名 -->
 <context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>com.hooda.config.SpringConfig</param-value>
 </context-param>
 <!-- 使用监听器启动Spring的IOC容器 -->
 <listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
</web-app>

实例化Bean的三种方式

<bean id="userService" class="com.hooda.service.UserServiceImpl"/>
public class DefinedBeanFactory {
  public static UserService getUserService() {
    return new UserServiceImpl();
  }
}
<bean id="userService" class="com.hooda.factory.DefinedBeanFactory" factory-method="getUserService"/>
public class DefinedBeanFactory {
  public UserService getUserService() {
    return new UserServiceImpl();
  }
}
<bean id="definedBeanFactory" class="com.hooda.factory.DefinedBeanFactory"/>
<bean id="userService" factory-bean="definedBeanFactory" factory-method="getUserService"/>

Bean的属性及生命周期

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<T> {
    // 返回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<Company> {
    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配置
<bean id="companBean" class="CompanyFactoryBean">
    <property name="companyInfo" value="xx,xx,1"/>
</bean>
// 获取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)方法
上一篇 下一篇

猜你喜欢

热点阅读