spring面试

springboot -启动流程

1. refresh() 步骤

spring面试_第1张图片

功能分类

  • 1 为准备环境

  • 2 3 4 5 6 为准备 BeanFactory

  • 7 8 9 10 12 为准备 ApplicationContext

  • 11 为初始化 BeanFactory 中非延迟单例 bean

1.1 prepareRefresh

  • 这一步创建和准备了 Environment 对象,它作为 ApplicationContext 的一个成员变量

  • 管理各种 键值信息
    @Value 值注入

1.2. obtainFreshBeanFactory

beanFactory 和application 组合关系,application 有beanfactory的引用。

  • 这一步获取(或创建) BeanFactory

  • 理解 BeanFactory 的作用

  • 理解 BeanDefinition 的作用

  • BeanDefinition 从何而来

BeanFactory 的作用是负责 bean 的创建、依赖注入和初始化
BeanDefinition 作为 bean 的设计蓝图,规定了 bean 的特征,如单例多例、依赖关系、初始销毁方法等
BeanDefinition 的来源有多种多样,可以是通过 xml 获得通过配置类获得通过组件扫描获得,也可以是编程添加

1.3 prepareBeanFactory

这一步会进一步完善 BeanFactory,为它的各项成员变量赋值
比如el 表达式解析器,类型转换器 内置的beanpostprocessor

  • beanExpressionResolver 用来解析 SpEL,常见实现为 StandardBeanExpressionResolver
  • propertyEditorRegistrars 会注册类型转换器
    • 它在这里使用了 ResourceEditorRegistrar 实现类
    • 并应用 ApplicationContext 提供的 Environment 完成 ${ } 解析
  • beanPostProcessors 是 bean 后处理器集合,会工作在 bean 的生命周期各个阶段,此处会添加两个:
    • ApplicationContextAwareProcessor 用来解析 Aware 接口
    • ApplicationListenerDetector 用来识别容器中 ApplicationListener 类型的 bean

1.4 postProcessBeanFactory

  • 这一步是空实现,留给子类扩展。
    • 一般 Web 环境的 ApplicationContext 都要利用它注册新的 Scope,完善 Web 下的 BeanFactory
  • 这里体现的是模板方法设计模式

1.5 invokeBeanFactoryPostProcessors

  • 这一步会·调用 beanFactory 后处理器 后置处理器扩展bean工厂
  • beanFactory 后处理器,作用 :充当 beanFactory 的扩展点可以用来补充或修改 BeanDefinition

比如 ConfigurationClassPostProcessor 解析配置类相关的注解 @Bean @Import @Congifuration。 @PropertySourcePlaceHolderConfigure 解析${}占位符

  • 常见的 beanFactory 后处理器有

    • ConfigurationClassPostProcessor – 解析 @Configuration、@Bean、@Import、@PropertySource 等
      *PropertySourcesPlaceHolderConfigurer– 替换 BeanDefinition 中的 ${ }
    • MapperScannerConfigurer – 补充 Mapper 接口对应的 BeanDefinition

1.6 registerBeanPostProcessors

准备bean的后置处理器 ,beanFactory 中找出 bean 后处理器,添加至 beanPostProcessors 集合中 ,用于第11步创建bean的各个阶段时用到的。
beanpostprocessor 作用 :,作用 充当 bean 的扩展点可以工作在 bean 的实例化、依赖注入、初始化阶段
掌握常见的bean 后处理器

  • 解析@Autowired注解的
    • AutowiredAnnotationBeanPostProcessor
  • 解析@Resource 注解的
    • CommonAnnotationBeanPostProcessor
  • 创建代理类的
    • AnnotationAwareAspectJAutoProxyCreator

理解bean的后置处理器的作用
掌握常见的bean 后处理器

  • 这一步是继续从 beanFactory 中找出 bean 后处理器,添加至 beanPostProcessors 集合中
  • bean 后处理器,作用 充当 bean 的扩展点可以工作在 bean 的实例化、依赖注入、初始化阶段,常见的有:
    • AutowiredAnnotationBeanPostProcessor 功能有:解析 @Autowired,@Value 注解
    • CommonAnnotationBeanPostProcessor 功能有:解析 @Resource,@PostConstruct,@PreDestroy
    • AnnotationAwareAspectJAutoProxyCreator 功能有:为符合切点的目标 bean 自动创建代理

1.7 initMessageSource 国际化

  • 这一步是为 ApplicationContext 添加 messageSource 成员,作用 实现国际化功能

  • 去 beanFactory 内找名为 messageSource 的 bean,如果没有,则提供空的 MessageSource 实现

1.8 initApplicationContextEventMulticaster 事件广播器

作用 为ApplicationContext 准备事件发布器,用来发布事件。

  • 这一步为 ApplicationContext 添加事件广播器成员,即 applicationContextEventMulticaster
  • 事件广播成员 它的作用是发布事件给监听器
  • 去 beanFactory 找名为 applicationEventMulticaster 的 bean 作为事件广播器,若没有,会创建默认的事件广播器
  • 之后就可以调用 ApplicationContext.publishEvent(事件对象) 来发布事件

1.9 onRefresh 空实现

  • 这一步是空实现,留给子类扩展
    • SpringBoot 中的子类在这里准备了 WebServer,即内嵌 web 容器
  • 体现的是模板方法设计模式

1.10 registerListeners 事件监听,收事件

一句话: 为Application 准备监听器。用来接收事件广播器发布的事件

  • 理解事件监听器作用
  • 监听器从何而来
  • 如何接收事件
  1. 用来接收事件
  2. 一部分监听器是事先编程添加的、另一部分监听器来自容器中的 bean、还有一部分来自于 @EventListener 的解析
  3. 实现 ApplicationListener 接口,重写其中 onApplicationEvent(E e) 方法即可
  • 这一步会从多种途径找到事件监听器,并添加至 applicationEventMulticaster
  • 事件监听器顾名思义,用来接收事件广播器发布的事件,有如下来源
    • 事先编程添加的
    • 来自容器中的 bean
    • 来自于 @EventListener 的解析
  • 要实现事件监听器,只需要实现 ApplicationListener 接口,重写其中 onApplicationEvent(E e) 方法即可

1.11 finishBeanFactoryInitialization

·初始化所有非延迟加载的单例bean,在初始化的过程中,执行bean的后置处理器.·

  • conversionService 也是一套转换机制,作为对 PropertyEditor 的补充

  • embeddedValueResolvers 即内嵌值解析器,用来解析 @Value 中的 ${ },借用的是 Environment 的功能

  • singletonObjects 即单例池,缓存所有单例对象

    • 对象的创建都分三个阶段,每一阶段都有不同的 bean 后处理器参与进来,扩展功能

    1.12 finishRefresh

准备生命周期管理器。发布ContextRefreshed 事件。整个refresh 完成。

  • 这一步会为 ApplicationContext 添加 lifecycleProcessor 成员,用来控制容器内需要生命周期管理的 bean
  • 发布 ContextRefreshed 事件,整个 refresh 执行完成

2. spring bean的生命周期。

spring面试_第2张图片

2.1 构造bean

  • AutowiredAnnotationBeanPostProcessor
    ① 优先选择带 @Autowired 注解的构造;
    ② 若有唯一的带参构造,也会入选
  • 采用默认构造
    如果上面的后处理器和 BeanDefiniation 都没找到构造,采用默认构造,即使是私有的

3. spring 事务失效的场景

3.1. 抛出检查异常导致事务不能正确回滚

@Service
public class Service1 {

    @Autowired
    private AccountMapper accountMapper;

    @Transactional
    public void transfer(int from, int to, int amount) throws FileNotFoundException {
        int fromBalance = accountMapper.findBalanceBy(from);
        if (fromBalance - amount >= 0) {
            accountMapper.update(from, -1 * amount);
            new FileInputStream("aaa");
            accountMapper.update(to, amount);
        }
    }
}
  • 原因:Spring 默认只会回滚非检查异常 也就是运行时异常。

  • 编译期异常,spring 不回滚

  • 解法:配置 rollbackFor 属性

    • @Transactional(rollbackFor = Exception.class)

3.2 业务方法内自己 try-catch 异常导致事务不能正确回滚

@Service
public class Service2 {

    @Autowired
    private AccountMapper accountMapper;

    @Transactional(rollbackFor = Exception.class)
    public void transfer(int from, int to, int amount)  {
        try {
            int fromBalance = accountMapper.findBalanceBy(from);
            if (fromBalance - amount >= 0) {
                accountMapper.update(from, -1 * amount);
                new FileInputStream("aaa");
                accountMapper.update(to, amount);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • 原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
  • 解法1:异常原样抛出
    • 在 catch 块添加 throw new RuntimeException(e);
  • 解法2:手动设置 TransactionStatus.setRollbackOnly()
    • 在 catch 块添加 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

3.3 aop 切面顺序导致导致事务不能正确回滚

@Service
public class Service3 {

    @Autowired
    private AccountMapper accountMapper;

    @Transactional(rollbackFor = Exception.class)
    public void transfer(int from, int to, int amount) throws FileNotFoundException {
        int fromBalance = accountMapper.findBalanceBy(from);
        if (fromBalance - amount >= 0) {
            accountMapper.update(from, -1 * amount);
            new FileInputStream("aaa");
            accountMapper.update(to, amount);
        }
    }
}

@Aspect
public class MyAspect {
    @Around("execution(* transfer(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        LoggerUtils.get().debug("log:{}", pjp.getTarget());
        try {
            return pjp.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }
}

最外层调用事物切面
中间一层 自定义切面
最里面一层 才是目标 方法


所以目标方法抛出异常,中间一层的自定义切面处理, 
try catch 掉了异常,没有向外抛出。
所以最外层事物切面 不知道发生异常, 会提交事物。


  • 原因:事务切面优先级最低(在最外面一层),但如果自定义的切面优先级和他一样, 则还是自定义切面在内层,这时若自定义切面没有正确抛出异常…
  • 解法1、2:同情况2 中的解法:1、2
  • 解法3:调整切面顺序,在 MyAspect 上添加 @Order(Ordered.LOWEST_PRECEDENCE - 1) (不推荐)

3.4 非 public 方法事物失效

  • 原因:Spring 为方法创建代理、添加事务通知、前提条件都是该方法是 public 的
  • 解法1:改为 public 方法
  • 解法2:添加 bean 配置如下(不推荐)
@Bean
public TransactionAttributeSource transactionAttributeSource() {
    return new AnnotationTransactionAttributeSource(false);
}

3.5 父子容器导致的事物失效

父容器

现在配置了父子容器,WebConfig 对应子容器,AppConfig 对应父容器,发现事务依然失效

  • 原因:子容器扫描范围过大,把未加事务配置的 service 扫描进来
  • 解法1:各扫描各的,不要图简便 (子容器扫描 自己该扫描的包)
  • 解法2:不要用父子容器,所有 bean 放在同一容器

3.6 调用本类方法导致传播行为失效

下面代码 期望foo方法 和bar 都有事物。

@Service
public class Service6 {
	// required 之前没有事物,创建事物。如果已经有了事物,加入。
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void foo() throws FileNotFoundException {
        LoggerUtils.get().debug("foo");
        this.bar();// this 不是代理对象,而是目标对象本身
    }
		//  不管当前有无事物,都开启事物
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void bar() throws FileNotFoundException {
        LoggerUtils.get().debug("bar");
    }
}
  • 原因:本类方法调用不经过代理,因此无法增强
  • 解法1:依赖注入自己(代理)来调用
  • 解法2:通过 AopContext 拿到代理对象,来调用
  • 解法3:通过 CTW,LTW 实现功能增强

让bar 方法也开启事务

@Service
public class Service6 {

	@Autowired
	private Service6 proxy; // 本质上是一种循环依赖

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void foo() throws FileNotFoundException {
        LoggerUtils.get().debug("foo");
		System.out.println(proxy.getClass());// 代理对象
		proxy.bar();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void bar() throws FileNotFoundException {
        LoggerUtils.get().debug("bar");
    }
}

解法2,还需要在 AppConfig 上添加 `@EnableAspectJAutoProxy(exposeProxy = true)
spring面试_第3张图片

@Service
public class Service6 {
    
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void foo() throws FileNotFoundException {
        LoggerUtils.get().debug("foo");
        ((Service6) AopContext.currentProxy()).bar();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void bar() throws FileNotFoundException {
        LoggerUtils.get().debug("bar");
    }
}

3.7 @Transactional 没有保证原子行为

@Service
public class Service7 {

    private static final Logger logger = LoggerFactory.getLogger(Service7.class);

    @Autowired
    private AccountMapper accountMapper;

    @Transactional(rollbackFor = Exception.class)
    public void transfer(int from, int to, int amount) {
        int fromBalance = accountMapper.findBalanceBy(from);
        logger.debug("更新前查询余额为: {}", fromBalance);
        if (fromBalance - amount >= 0) {
            accountMapper.update(from, -1 * amount);
            accountMapper.update(to, amount);
        }
    }

    public int findBalance(int accountNo) {
        return accountMapper.findBalanceBy(accountNo);
    }
}

上面的代码实际上是有 bug 的,假设 from 余额为 1000,两个线程都来转账 1000,可能会出现扣减为负数的情况

  • 原因:事务的原子性仅涵盖 insert、update、delete、select … for update 语句,select 方法并不阻塞
  • spring面试_第4张图片
  • 如上图所示,红色线程和蓝色线程的查询都发生在扣减之前,都以为自己有足够的余额做扣减

3.8 @Transactional 方法导致的 synchronized 失效

针对上面的问题,能否在方法上加 synchronized 锁来解决呢?

@Service
public class Service7 {

    private static final Logger logger = LoggerFactory.getLogger(Service7.class);

    @Autowired
    private AccountMapper accountMapper;

    @Transactional(rollbackFor = Exception.class)
    public synchronized void transfer(int from, int to, int amount) {
        int fromBalance = accountMapper.findBalanceBy(from);
        logger.debug("更新前查询余额为: {}", fromBalance);
        if (fromBalance - amount >= 0) {
            accountMapper.update(from, -1 * amount);
            accountMapper.update(to, amount);
        }
    }

    public int findBalance(int accountNo) {
        return accountMapper.findBalanceBy(accountNo);
    }
}

答案是不行,原因如下:
synchronized 锁的是目标对象 里面的3条sql 语句,没有锁住代理对象的commit
所以不行。

  • synchronized 保证的仅是目标方法的原子性,环绕目标方法的还有 commit 等操作,它们并未处于 sync 块内

  • 可以参考下图发现,蓝色线程的查询只要在红色线程提交之前执行,那么依然会查询到有 1000 足够余额来转账
    spring面试_第5张图片

  • 解法1:synchronized 范围应扩大至代理方法调用

  • 解法2:使用 select … for update 替换 select

3.9 被 final 、static 修饰的方法

事物失效

3.10 总结

spring事务失效的12种场景

事务不生效

1.方法访问权限问题,只支持public
2.方法用final修饰,被static 修饰,动态代理不能代理final方法
3.方法内部调用,同一对象内调用没有使用代理 this 调用,未被aop事务管理器控制
4.未被spring管理 该类 没有加@service
5.多线程调用,事务管理内部使用threadLocal,不同线程间不在同一事务

  • spring 事物的事物是通过数据库连接来实现的,数据库连接通过threadloacl 来保存的。
  • 多线程下,threadlocal 是数据隔离的。

6.表不支持事务 比如myisam 引擎。
7.未配置事务

事务不回滚

8.错误的传播属性
9.自己吞了异常 未在catch 块 抛出异常。
10.手动抛了别的异常 spring 只针对 runtimeException 和 error 回滚,对于普通的非运行异常 不回滚
11.`自定义了回滚异常与事务回滚异常不一致 ``
rollbackFor=BusinessException.class
抛出的异常 不是自定义异常
12.嵌套事务回滚多了,需要局部回滚的地方未做异常控制

4. enableAutoConfiguration

4.1 @configuration

给@bean 返回值 的是生成代理对象。

@bean 配合@Value , 返回 bean工厂 postprocessor对象,可能导致@value 注入失败。
解决 返回@Bean bean工厂处理器, 使用 static 修饰。不会影响@value的注入
spring面试_第6张图片

4.2 主配置类 和import 配置类

spring面试_第7张图片

  1. 同意配置类中 @Import 先解析,@Bean 后解析。
  2. 同名定义,默认后面的解析会覆盖前面的解析。( 输出容器中的 bean1)
  3. 在不允许覆盖的情况下,如何能够让 MyConfig(主配置类)的配置优先,(虽然覆盖方式能解决)
  4. DeferrdImportSelector 最后工作,最有先解析@Bean 在解析@Import

解决 :

deferredimportSelector

spring面试_第8张图片

5 springboot 的自动配置

  • @springbootapplication
    • @componentScan
    • @SpringBootConfigcuration
    • @EnableAutoConfiguration

5.1 componentScan 过滤

spring面试_第9张图片

5.2 @EnableAutoConfiguration

5.3 原理

自动配置原理

@SpringBootConfiguration 是一个组合注解,由 @ComponentScan、@EnableAutoConfiguration 和 @SpringBootConfiguration 组成

  1. @SpringBootConfiguration 与普通 @Configuration 相比,唯一区别是前者要求整个 app 中只出现一次
  2. @ComponentScan
    • excludeFilters - 用来在组件扫描时进行排除,也会排除自动配置类
  3. @EnableAutoConfiguration 也是一个组合注解,由下面注解组成
    • @AutoConfigurationPackage – 用来记住扫描的起始包
    • @Import(AutoConfigurationImportSelector.class) 用来加载 META-INF/spring.factories 中的自动配置类

为什么不使用 @Import 直接引入自动配置类

有两个原因:

  1. 让主配置类和自动配置类变成了强耦合,主配置类不应该知道有哪些从属配置
  2. 直接用 @Import(自动配置类.class),引入的配置解析优先级较高,自动配置类的解析应该在主配置没提供时作为默认配置

因此,采用了 @Import(AutoConfigurationImportSelector.class)

  • AutoConfigurationImportSelector.class 去读取 META-INF/spring.factories 中的自动配置类,实现了弱耦合。
  • 另外 AutoConfigurationImportSelector.class 实现了 DeferredImportSelector 接口,让自动配置的解析晚于主配置的解析

6 spring 设计模式

单例模式 跟我们理解的不同。
构建者模式
工厂方法模式

  • beanfactory.getbean() 、
  • factoryBean

适配器模式 adapter

  • 必须适配 HandlerAdapter 接口

代理模式
责任链模式

  • handlerInterceptor

观察者模式

  • spring 中的发布和监听

6 循环依赖

使用两级缓存 能解决平通对象循环依赖注入的问题,
但是不能解决代理对象的循环依赖注入。
因为代理对象创建对象过晚。

代理对象循环依赖解决方法。

6.1 构造循环依赖的解决

  • 思路1

    • a 注入 b 的代理对象,这样能够保证 a 的流程走通
    • 后续需要用到 b 的真实对象时,可以通过代理间接访问
      spring面试_第10张图片
  • 思路2

    • a 注入 b 的工厂对象,让 b 的实例创建被推迟,这样能够保证 a 的流程先走通
    • 后续需要用到 b 的真实对象时,再通过 ObjectFactory 工厂间接访问
      spring面试_第11张图片

用 @Lazy 为构造方法参数生成代理

注入的是代理对象

   static class A {
        private static final Logger log = LoggerFactory.getLogger("A");
        private B b;

        public A(@Lazy B b) {
            log.debug("A(B b) {}", b.getClass());
            this.b = b;
        }

        @PostConstruct
        public void init() {
            log.debug("init()");
        }
    }

    static class B {
        private static final Logger log = LoggerFactory.getLogger("B");
        private A a;

        public B(A a) {
            log.debug("B({})", a);
            this.a = a;
        }

        @PostConstruct
        public void init() {
            log.debug("init()");
        }
    }

示例2:用 ObjectProvider 延迟依赖对象的创建

  static class A {
        private static final Logger log = LoggerFactory.getLogger("A");
        private ObjectProvider<B> b;

        public A(ObjectProvider<B> b) {
            log.debug("A({})", b);
            this.b = b;
        }

        @PostConstruct
        public void init() {
            log.debug("init()");
        }
    }

    static class B {
        private static final Logger log = LoggerFactory.getLogger("B");
        private A a;

        public B(A a) {
            log.debug("B({})", a);
            this.a = a;
        }

        @PostConstruct
        public void init() {
            log.debug("init()");
        }
    }
		// a 里面用到b,属性b 是工厂对象,getObject 是B 对象
System.out.println(context.getBean(A.class).b.getObject());
        System.out.println(context.getBean(B.class));

@scope

@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)//创建b生成代理对象
@Component
class B {
    private static final Logger log = LoggerFactory.getLogger("B");
    private A a;

    public B(A a) {
        log.debug("B({})", a);
        this.a = a;
    }

    @PostConstruct
    public void init() {
        log.debug("init()");
    }
}



@Component
class A {
    private static final Logger log = LoggerFactory.getLogger("A");
    private B b;  // b代理对象,那么就可以推迟b的创建

    public A(B b) {
        log.debug("A(B b) {}", b.getClass());
        this.b = b;
    }

    @PostConstruct
    public void init() {
        log.debug("init()");
    }
}

6.2 总结

  • 单例set方法 (包括成员变量) 循环依赖,spring 会例用三级缓存解决,不需要额外的配置

    • 一级缓存,也就是单例池,放成品对象。
    • 二级缓存,存放了 发生循环依赖时的产品对象( 也就是 半成品对象, 原始的bean,可能是代理的bean )
    • 三级缓存存放工厂对象,发生循环依赖的时候,会调用工厂获得半成品对象,放入二级缓存。
    • spring 期望在初始化的时候创建代理对象,但是如果发生了循环依赖,会由工厂提前创建,后续初始化就不必重复创建代理对象。
    • 二级缓存的意义在于,如果提前创建了代理对象,在最后阶段,需要从二级缓存中获取代理对象,作为最终的结果。
  • 构造方法 以及 多例的循环依赖。

  • @Lazy 产生代理对象,解决。

  • @Scope 产生代理对象,解决。

  • ObjectFactory&ObjectProvider 工厂方式推迟注入对象的获取,解决。

7. springboot 启动流程

new SpringApplication(primarySources)
.run(args);

7.1 springapplication 构造方法分析

  1. 获取BeanDefinition 的来源。
  2. 判定应用容器类型 根据jar包(非web 应用,servletweb 类型,reactorweb 类型)
  3. 添加ApplicationContext 的 初始化器。 对applicationcontext 做扩展。
  4. 添加监听和事件。
  5. 主类推断。记录主类。

7.2 run 方法分析

  1. 得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器
    从配置中获取重要的事件发布器:SpringApplicationRunListeners·

    • 发布 application starting 事件1️⃣
      解释: 事件发布器:在springboot 启动过程中 一些重要节点,执行完,发布对应事件。
  2. 封装启动 args

  3. 准备 Environment 添加命令行参数(*)
    3-6 环境变量有关。

  4. ConfigurationPropertySources 处理(*)

    • 发布 application environment 已准备事件2️⃣
  5. 通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)

    • application.properties,由 StandardConfigDataLocationResolver 解析
    • spring.application.json
  6. 绑定 spring.main 到 SpringApplication 对象(*)

  7. 打印 banner(*)

  8. 创建容器 构造方法推断出的容器类型创建容器。

  9. 准备容器
    回调调用之前(构造函数时)添加的初始化器,进行增强。

    • 发布 application context 已初始化事件3️⃣
  10. 加载 bean 定义
    read.register() beandefinition 进行加载beandefinition

    • 发布 application prepared 事件4️⃣
  11. refresh 容器

    • 发布 application started 事件5️⃣
  12. 执行 runner
    执行 CommandLineRunner 、ApplicationRunner 接口的run()方法。
    commandLineRunner 和ApplicationRunner 区别 参数区别
    c 参数来自main方法参数。

    • 发布 application ready 事件6️⃣

    • 这其中有异常,发布 application failed 事件7️⃣

7.3 自己总结

整个spring框架启动分为两部分,构造SpringBootApplication对象和执行run方法

核心注解@SpringBootConfiguration标识启动类为配置类,
@EnableAutoConfiguration通过内部@Import注解AutoConfigurationImportSelector.class实现自动装配,
@ComponentScan默认扫描当前目录及子目录下的bean。

SpringBootApplication的构造方法主要做了几件事。

根据是否加载servlet类判断是否是web环境
获取所有初始化器,利用spi机制,扫描所有META-INF/spring.factories下的ApplicationContextInitializer子类通过反射拿到实例,在spring实例启动前后做一些回调工作。
获取所有监听器 applicationLister,同2,也是扫描配置加载对应的类实例。
定位main方法

run方法主要
1.读取META-INF/spring.factores,将事件发布器 SpringApplicationRunListeners类型存到集合中
事件发布器:在springboot 启动过程中 一些重要节点,执行完,发布对应事件。
2. prepareEnvironment(listeners, applicationArguments);配置环境 将配置文件读取到容器中
3.打印 banner(*)
4. 根据构造方法推断出的容器类型,创建容器。
5.准备容器
回调调用之前(构造函数时)添加的初始化器,进行增强。
6.加载 bean 定义
read.register() beandefinition 进行加载beandefinition
7.refreshContext(context); 刷新容器
8.执行 runner
执行 CommandLineRunner 、ApplicationRunner 接口的run()方法。

7.4 自动配置类原理

在SpringBootApplication 注解里面有一个@EnableAutoConfiguration 注解实现自动装配。
主要以考三步骤。

  1. 引入的starter,组件需要包含@Configuration配置类类,通过@Bean 注解声明需要装配到IOC的容器的bean对象。
  2. 通过springFactoryload 读取classpath下,/META-INF/spring.factories文件中的配置类。使用了spi机制。
  3. springboot 拿到所以配置类后,通过@AutoConfigurationImportSelector类,加载配置类注入到容器中。

7.5 事件监听器

异步发送事件

7.5.1 eventlistener 解析原理

你可能感兴趣的:(spring,spring)