Spring 启动过程(上)

简介: Spring 是我们最常用的框架之一,我们今天一起来带大家一些探究一下 Spring 的启动过程。首先,Spring 的启动过程分为 12 个步骤主要是完成容器的初始化,以及对单实例非懒加载 Bean 完成创建和Bean 属性的赋值注入和初始化,以及消息派发器的创建和启动过程消息的触发。补充:本文和后续版本基于 spring-5.1.14 版本展开

Spring 使用 Demo


public class DemoApplicationTest {
  public static void main(String[] args) {
    // 创建容器
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
    // 获取 UserService 对象
    UserService userService = applicationContext.getBean(UserService.class);
    // 执行 test 方法
    userService.test();
  }
}
@Configuration
@Import(UserService.class)
class AppConfig {
}
// UserSerivce 类
@Service
public class UserService {
  public String test() {
    return "test";
  }
}


Spring 的启动过程以及方法入口


// 入口方法 
AbstractApplicationContext#refresh()
// 1. 刷新前的预处理工作
prepareRefresh();
// 2. 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. BeanFactory 预处理工作
prepareBeanFactory(beanFactory);
// 4.BeanFactory 完成后进行的后置处理工作
postProcessBeanFactory(beanFactory);
// 5. 执行 BeanFactoryPostProcessors
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册 Bean 后置处理器  [intercept bean creation.]
registerBeanPostProcessors(beanFactory);
// 7. 初始化 MessageSource 组件(做国际化功能, 消息绑定,消息解析)
initMessageSource();
// 8. 初始化事件派发器
initApplicationEventMulticaster();
// 9. 留给自容器(子类)
onRefresh();
// 10. 给容器中将所有的项目中的 ApplicationListener 注册进来
registerListeners();
// 11. 初始化所有的非懒加载单实例Bean
finishBeanFactoryInitialization(beanFactory);
// 12. 执行Spring容器的生命周期(启动)和发布事件
finishRefresh();


Spring 启动过程描述


1. prepareRefresh() 刷新前的预处理工作


  • 记录启动事件


  • 允许子容器可以设置一些属性到 environment


  • 检查衍生属性是否合法,是否包含必须的属性


  • 实现代码如下:


protected void prepareRefresh() {
    // Switch to active.
    this.startupDate = System.currentTimeMillis();
    //容器是否关闭
    this.closed.set(false);
    //容器启动
    this.active.set(true);
    if (logger.isDebugEnabled()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Refreshing " + this);
        }
        else {
            logger.debug("Refreshing " + getDisplayName());
        }
    }
    // Initialize any placeholder property sources in the context environment.
    // 1. 初始化一些属性设置, 允许子容器设置一些内容到 environment 中
    initPropertySources();
    // Validate that all properties marked as required are resolvable:
    // see ConfigurablePropertyResolver#setRequiredProperties
    // 2. 校验必填属性是否有值
    getEnvironment().validateRequiredProperties();
    // Store pre-refresh ApplicationListeners...
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        // Reset local application listeners to pre-refresh state.
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);
    }
    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    // 3. 创建集合保存容器的一些早期事件
    this.earlyApplicationEvents = new LinkedHashSet<>();
}


  • 在Spring MVC 中对 initPropertySources 方法做了实现,将 Servlet 容器相关的信息放到了 environment 中,实现如下


//Spring MVC 的 GenericWebApplicationContext 类
protected void initPropertySources() {
    ConfigurableEnvironment env = getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
    }
}


2. obtainFreshBeanFactory() 获取 BeanFactory 对象


  • 刷新 BeanFactory


// 创建 BeanFactory 是在 AbstractApplicationContext 的无参构造方法中初始化
// GenericApplicationContext 类
public GenericApplicationContext() {
    this.beanFactory = new DefaultListableBeanFactory();
}
// AnnotationConfigApplicationContext 类的定义签名和继承关系
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
  // ......
}


  • 调用 obtainFreshBeanFactory 方法返回 BeanFactory


protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 1. 刷新【创建】BeanFactory
    refreshBeanFactory();
    // 2. 返回 BeanFactory
    return getBeanFactory();
}


  • 注意:refreshBeanFactory 方法有两个实现类 AbstractRefreshableApplicationContext ,GenericApplicationContext 我们当前创建容器是你用的 AnnotationConfigApplicationContext 类。它是 GenericApplicationContext 的子类,所以当前容器 不支持重复刷新。如果需要重复刷新的话可以选择 AnnotationConfigWebApplicationContext 类。


AnnotationConfigApplicationContext application =new AnnotationConfigApplicationContext();
application.register(ApplicationConfig.class);
application.refresh();
application.refresh(); //报错: IllegalStateException


3. prepareBeanFactory(beanFactory) 准备 BeanFactory


protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  // 1. 设置 BeanFactory 的类加载器,支持表达式解析器 ....
  beanFactory.setBeanClassLoader(getClassLoader());
  // el 解析器
  beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
  // 默认类型转换器
  beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
  // 2. 添加部分的 BeanFactory 的 BeanPostProcessor [ApplicationContextAwareProcessor]
  // Bean 后置处理器
  beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
  // 3. 设置忽略的自动装配的接口 EnvironmentAware 、EmbeddedValueResolverAware
  // 如果实现了这些接口重写的 set 方法,那么 Spring 就不会去自动装配
  beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
  beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
  beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
  beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
  beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
  beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
  // 4. 注册可以解析的自动装配,我们能够直接在任何组件中自动注入:
  // BeanFactory、ResourceLoader、ApplicationEventPublisher、 ApplicationContext
  beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
  beanFactory.registerResolvableDependency(ResourceLoader.class, this);
  beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
  beanFactory.registerResolvableDependency(ApplicationContext.class, this);
  // 5. 添加 BeanPostProcessor 【ApplicationListenerDetector】
  beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
  // 6. 添加编译时的 AspectJ 支持
  if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    // Set a temporary ClassLoader for type matching.
    beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
  }
  // 7. 给 BeanFactory 中注册一些能用的组件:
  //    environment 【ConfigurableEnvironment】
  //      systemProperties 【Map<String, Object>】
  //    systemEnvironment【Map<String, Object>】
  if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
  }
  if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
    beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
  }
  if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
    beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
  }
}



相关文章
|
12月前
|
存储 Java Spring
【Spring容器的启动过程】
【Spring容器的启动过程】
|
IDE 网络协议 Java
2021最新 IDEA 启动失败 & 启动Spring boot 项目端口被占用问题 彻底解决方案
2021最新 IDEA 启动失败 & 启动Spring boot 项目端口被占用问题 彻底解决方案
804 0
2021最新 IDEA 启动失败 & 启动Spring boot 项目端口被占用问题 彻底解决方案
|
2月前
|
设计模式 缓存 Java
深入Spring Boot启动过程:揭秘设计模式与代码优化秘籍
深入Spring Boot启动过程:揭秘设计模式与代码优化秘籍
|
4月前
|
监控 Java Spring
深入理解Spring Boot的启动过程
深入理解Spring Boot的启动过程
|
5月前
|
XML Java 数据格式
Spring5源码(15)-IoC容器启动过程简析及XmlBeanFactory初始化
Spring5源码(15)-IoC容器启动过程简析及XmlBeanFactory初始化
63 1
|
XML Java 数据格式
Spring的IoC容器启动过程之源码级分析
Spring的IoC容器启动过程之源码级分析
270 0
|
存储 XML 安全
Spring - FactoryBean扩展实战_MyBatis-Spring 启动过程源码解读
在理解 MyBatis-Spring 的启动过程时,需要重点把握的是 `SqlSessionTemplate` 核心类的设计理念及其实现过程,使用了JDK动态代理机制。
149 0
Spring - FactoryBean扩展实战_MyBatis-Spring 启动过程源码解读
|
JSON SpringCloudAlibaba 负载均衡
【微服务35】分布式事务Seata源码解析三:从Spring Boot特性来看Seata Client 启动时都做了什么
【微服务35】分布式事务Seata源码解析三:从Spring Boot特性来看Seata Client 启动时都做了什么
846 0
【微服务35】分布式事务Seata源码解析三:从Spring Boot特性来看Seata Client 启动时都做了什么
|
IDE Java 测试技术
Spring 5 启动性能优化之 @Indexed
背景 Spring 经过近20年的发展,目前版本已经迭代到了5.x,每个版本 Spring 都有不同的改进,版本 5.x 中,Spring 把重心放到了性能优化上。我们知道,Spring 注解驱动编程中,Spring 启动时需要对类路径下的包进行扫描,以便发现所需管理的 bean。如果在应用启动前能够确定 Spring bean,不再进行扫描,那么性能就会大大提高,Spring 5 对此进行了实现。
1152 0
Spring 5 启动性能优化之 @Indexed