【Spring】面试问题整理

本文详细探讨了Spring框架的各个核心概念,包括Spring容器启动流程、IOC、Bean定义、依赖注入、循环依赖解决方案、Bean的生命周期以及AOP。通过对BeanFactory、FactoryBean的区别分析,展示了Spring如何管理组件。此外,还讲解了@Controller的安全性,@Autowired注解的工作原理,以及Spring中的各种扩展点,如BeanFactoryPostProcessor和后置处理器的作用。文章深入剖析了Spring的事务管理、AOP功能开启以及事务注解的工作原理,为读者提供了全面的Spring面试准备资料。
摘要由CSDN通过智能技术生成

Spring容器启动流程

当我们new AnnotationConfigApplicationContext() 就会开始Spring启动流程

  • init()
    • 初始化一个Reader: Reader可以用来注册单个BeanDefinition
    • 初始化一个Scanner:Scanner用来扫描得到BeanDefinition
      通过Reader把配置类注册为一个BeanDefinition
  • refresh()
    • prepareRefresh() 刷新前预处理
    • obtainFreshBeanFactory() 先获取一个Bean工厂
    • prepareBeanFactory(beanFactory) 预先往Bean工厂中添加一些Bean后置处理器,和一些单例bean,和一些其他的配置
    • postProcessBeanFactory(beanFactory)
    • invokeBeanFactoryPostProcessors(beanFactory) 执行Bean工厂的后置处理器,把BeanDefinition注册到容器中
    • registerBeanPostProcessors(beanFactory)实例化bean的后置处理器并且排序,然后添加到Bean工厂中去
    • initMessageSource() 初始化用来进行国际化的MessageSource
    • initApplicationEventMulticaster() 初始化事件广播器
      默认为我们提供SimpleApplicationEventMulticaster
    • registerListeners() 注册事件监听器
      这里注册的是ApplicationListener类型的beanName
    • finishBeanFactoryInitialization(beanFactory) 开始实例化非懒加载的单例bean
      这里所有bean创建完成后,会执行实现了
      SmartInitializingSingleton接口的bean的afterSingletonsInstantiated方法,这里会把@EventListener注解的监听器添加到事件管理器中
    • finishRefresh()
      发布ContextRefreshedEvent事件

IOC

谈谈对spring ioc的理解

spring ioc的核心功能就是 通过控制反转和依赖注入来创建和管理组件。
控制反转:把对象创建和对象之间的调用过程,交给Spring进行管理
依赖注入:对IOC容器中各个组件的依赖关系赋值

也就是原来需要程序员手动创建对象,现在由Spring来负责去进行实例化得到对象,Spring负责通过反射去给对象中的属性进行赋值,把创建好的对象放在map中进行统一的管理。

BeanFactory和FactoryBean的区别

FactoryBean是一种特殊的Bean,它实现了FactoryBean接口,当我们从容器中拿FactoryBean的时候调的是它的getObject方法里返回的实例而不是它本身。

BeanFactory是容器,里面放着bean。

Controller不是线程安全的为什么使用没有出问题

因为Controller是栈封闭,也就是局部变量的栈都是线程私有的

Bean定义

Bean定义的属性

  • BeanClass 实例化Bean的类型

Spring导入Bean的方式: 这样得到的是BeanDefinition

  • xml方式 <Bean></Bean>
  • @Bean
  • @Import、@ImportSource
  • @ComponentScanner+@Component、@Service、@Controller、@Repository

Bean定义解析流程

refresh() 执行invokeBeanFactoryPostProcessors时,主要就是执行了

  • BeanDefinitionRegistryPostProcessor 主要作用就是注册bean定义到容器中
    对于实现了BeanDefinitionRegistry接口的,就会按优先级(实现了PriorityOrdered、Ordered、和未实现)执行所有实现了BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry 来注册beanDefinition。
    Spring默认提供了ConfigurationClassPostProcessor来注册BeanDefinition,也就是解析配置类的@Bean、@Import、@ImportSource、@ComponentScanner,就获得了bean定义对象
    • 其中ConfigurationClassPostProcessor 扫描@Import 时,如果@Import导入了实现ImportBeanDefinitionRegistrar接口的类,其BeanDefinition就会被加入。reader 注册时就会ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,来修改BeanDefinition。
  • BeanFactoryPostProcessor 修改Bean定义

因此Bean定义在实现了这三个接口的类的相应方法里都可以对BeanDefinition进行修改。
BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor、ImportBeanDefinitionRegistrar执行的时候都可以对Bean定义进行修改。
具体描述这三个什么时候执行 执行的时候做了什么事说清楚

可以修改Bean定义在源码中有哪些实际的应用呢

Mybatis等第三方组件和Spring整合就是通过实现ImportBeanDefinitionRegistrar接口做的。

Mybatis的注册逻辑:实现ImportBeanDefinitionRegistrar接口,重写其registerBeanDefinitions方法 :注册mapper接口,并将接口声明为FactoryBean,设置拦截方法,生成代理类。

这样做的原因:
扫描@Component是不支持接口的,也就是我们在接口上写@Component注解在容器里是得不到这个接口的BeanDefinition的,所以我们需要 想办法把BeanDefinition注入进去,就用的是@Import导入实现ImportBeanDefinitionRegistrar接口的类,然后重写其方法

Spring依赖注入

Spring中有哪些依赖注入的方式

  • 手动注入,通常使用XML的方式定义Bean的时候就会通过手动方式给属性进行赋值
    spring根据手动指定的方式通过构造方法或set方法注入
    • 手动指定值
    • 手动指定bean的名字
    • 实例工厂方法得到bean
    • 静态工厂方法得到bean
  • 自动注入,利用@Autowired注解利用Spring自动给属性进行赋值
    根据@Autowired标在的地方进行相应的注入
    • 通过属性自动注入
    • 通过set方法自动注入
    • 通过构造方法自动注入

Spring中的@Autowired注解的工作原理是怎样的?

创建bean的时候在对bean进行属性赋值阶段,Spring就会解析标了@Autowired注解的地方

  • 对于标在属性上的@Autowired
    Spring就会先根据属性的类型(byType)去Spring容器找对应类型的bean,如果找到一个则通过反射赋值给该属性。找到多个再根据属性的名字确定一个bean,也是通过反射赋值给该属性。没找到则报错。
  • 对于标在普通方法上(setter方法)的@Autowired
    Spring会根据setter方法的参数类型去找bean,找到多个再根据根据属性的名字去进行筛选,找到了bean之后再调用set方法进行赋值。
  • 对于标在构造器上的方法

循环依赖

在创建一个Bean的过程中如果依赖的另外一个Bean还没有创建,就会需要去创建依赖的那个Bean,而如果两个Bean相互依赖的话,就会出现循环依赖的问题。

如何解决循环依赖问题

Spring使用三级缓存解决了循环依赖的问题:

  1. 第一级缓存是单例池singletonObjects:它是用来保存经过了完整生命周期之后的bean。
  2. 第二级缓存是earlySingletonObjects:它是用来保存暂时还没有经过完整生命周期的bean。
  3. 第三级缓存是singletonFactories:它是用来保存提前进行AOP的工厂,它的作用是预先准备一个工厂,这个工厂是在需要时才执行

Spring中为什么要用三级缓存来解决循环依赖?

  1. 为什么要用三级缓存解决循环依赖
    因为用三级缓存可以保证两个代理对象能够相互拿到循环依赖
    对普通的对象的相互依赖 用两级缓存就可以解决

Spring中Bean的生命周期

在spring容器创建时,在postProcessBeanFactory(beanFactory)中会得到BeanDefinition
当这个bean要用到的时候(getBean())就会通过容器中的BeanDefinition来创建doCreateBean()

  • 如果有多个构造方法,则要推断构造方法
  • 进行实例化得到一个对象
    对对象中的加了@Autowired注解的属性进行属性填充
  • 属性赋值
  • 初始化bean
    • 会先回调xxxAware接口的方法
    • 调用BeanPostProcessor的初始化前的方法
    • 执行初始化方法
    • 调用BeanPostProcessor的初始化后的方法,在这里会进行AOP
  • 使用bean
  • Spring容器关闭时调用DisposableBean中destory()方法

BeanDefinition到单例对象的过程

Spring扫描得到BeanDefinition–>getBean—>得到单例对象。

  • BeanDefinition->getBean
    这个过程是BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor 来注册BeanDefinition对象到容器中
  • getbean-> bean实例
    这个过程是BeanPostProcessor后置处理器来进行修改

后置处理器

Spring在很多地方都会调用后置处理器的方法来实现相应功能,程序员可以自定义后置处理器,Spring的很多功能的实现也是通过后置处理器。

Spring中的BeanFactory的后置处理器是什么意思

BeanFactory的后置处理器

  • BeanDefinitionRegistryPostProcessor
    Spring就是用这个后置处理器来注册BeanDefinition的,默认提供ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()对配置文件解析解析得到BeanDefinition,注册到beanFactory中
  • BeanFactoryPostProcessor
    可以用来修改beanDefinition

Spring中Bean的后置处理器是什么意思?

Bean的后置处理器

  • InstantiationAwareBeanPostProcessor
    • 实例化前方法:postProcessBeforeInstantiation方法
      在实例化bean之前调用这个方法尝试返回代理对象
    • 实例化后方法:postProcessAfterInstantiation方法
      在属性赋值之前调用这个方法,这是属性赋值前最后一次修改bean的机会
    • postProcessPropertyValues() 方法
      分别解析@Autowired、@Resource、@Value,去Spring容器里找到对应的属性值
  • BeanPostProcessor
    • 初始化前方法:postProcessBeforeInitialization()
      这里会处理 @PostConstruct 和 @PreDestroy 注解
    • 初始化后方法:postProcessAfterInitialization()
      AOP功能就是这里实现的:AnnotationAwareAspectJAutoProxyCreator重写的postProcessAfterInitialization()对对象进行增强

AOP

对AOP的理解

AOP就是面向切面编程, 主要的工作就是对对象的方法进行增强,不用在对原方法进行改动而添加想要的功能。具体就是通过动态代理为对象生成一个代理对象,然后调用执行对象方法的时候通过拦截器链来调用方法。

切面解析

  • 解析切面
  • 生成代理对象
  • 切面注入 到目标对象 链式递归调用

spring 的aop体现哪三个方面:

  • @aspectJ
  • @Transactional 、@GlobalTransactional Spring的事务是通过AOP来实现的。
  • 接口+FactoryBean

AOP功能的开启:@EnableAspectJAutoProxy

  • @EnableAspectJAutoProxy 注解通过Import导入了AspectJAutoProxyRegistrar
    AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,所以Spring容器创建时invokeBeanFactoryPostProcessors(beanFactory) 在
    注册beanDefinition时就会调用AspectJAutoProxyRegistrar重写的registerBeanDefinitions方法,这个方法里注册了AnnotationAwareAspectJAutoProxyCreator的beanDefinition
  • AnnotationAwareAspectJAutoProxyCreator就是用来实现AOP功能的,主要实现了两个接口
    • BeanFactoryAware 实现这个接口就可以拿到容器中所有的beanDefinition
      当AnnotationAwareAspectJAutoProxyCreator初始化的时候就会调用它重写的initBeanFactory方法
    • SmartInstantiationAwareBeanPostProcessor 后置处理器
      由于是后置处理器所以在Spring容器创建的registerBeanPostProcessors(beanFactory) 就会被注入到容器中,在每个对象被实例化的时候 它的方法就会被调用实现相应的功能
      • postProcessBeforeInstantiation方法:在bean实例化前尝试返回代理对象
      • postProcessAfterInitialization()方法:如果当前对象需要增强,就为其生成代理对象
        当执行对项目标方法时,通过获取目标方法的拦截器链(把增强器包装成拦截器),然后递归调用每一个拦截器进行执行

目标方法的执行流程

当对象是通过AOP动态代理生成的对象,这个对象的目标方法被调用时的流程:

  • 获取拦截器链:把和方法匹配的增强器包装成MethodInterceptor
    遍历Advisor(和这个对象匹配的增强器),通过每个Advisor 对应的getPointcut,把和这个方法匹配的advisor用对应的Adapter封装为MethodInterceptor放到拦截器链interceptorList中
  • 执行拦截器链 proceed()
    每个拦截器都会调用InvocationMethod的proceed(),没调用一次索引加1,就获得了下一个拦截器。
    • 首先进入@AfterThrowing对应的拦截器AspectJAfterThrowingAdviceInterceptor
      • return InvocationMethod的proceed() 调用下一个拦截器
      • try catch 捕获到异常就会执行标了@AfterThrowing的方法
    • 进入@AfterRetruning对应的拦截器AspectJAfterRetruningAdviceInterceptor
      • 调用下一个拦截器
      • 执行标了@AfterRetruning的方法,有异常这个方法就不会执行
    • 进入@After对应的拦截器AspectJAfterAdviceInterceptor
      • 调用下一个拦截器
      • 执行标了@After的方法,因为这个方法的执行是放在finally的,所以不管有没有异常都会执行
    • 进入@Before对应的拦截器MethodBeforeAdviceInterceptor
      • 先执行标了@Before的方法
      • 调用下一个拦截器,这时是最后一个拦截器了就会invokeJoinpoint() 调用被增强的方法

所以最后方法的执行顺序就是:
正常执行:前置通知->目标方法->后置通知->返回通知
正常执行:前置通知->目标方法->后置通知->异常通知


为什么要用拦截器链
通过拦截器链的机制,保证通知方法和目标方法的执行顺序

Spring中的@Transactional注解的工作原理是怎样的?

在某个方法上增加@Transactional注解,就可以开启事务,这个方法中所有的sql都会在一个事务中执行,统一成功或失败。

在一个方法上加了@Transactional注解后,Spring会基于这个类生成一个代理对象,会将这个代理对象作为bean,当在使用这个代理对象的方法时,如果这个方法上存在@Transactional注解,那么代理逻辑会先把事务的自动提交设置为false,然后再去执行原本的业务逻辑方法,如果执行业务逻辑方法没有出现异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑方法出现了异常,那么则会将事务进行回滚。
当然,针对哪些异常回滚事务是可以配置的,可以利用@Transactional注解中的rollbackFor属性进行配置,默认情况下会对RuntimeException和Error进行回滚。

Spring中有哪些扩展点?

  • BeanDefinitionRegistryPostProcessor: 在Spring启动的过程中可以用来注册、移除、修改BeanDefinition
  • BeanFactoryPostProcessor: 在Spring启动的过程中可以用来操作BeanFactory
  • BeanPostProcessor:在创建Bean的过程中可以对实例化及其前后进行干涉,初始化及其前后进行干涉
  • Aware接口:比如BeanNameAware, BeanFactoryAware等等,Aware表示回调,Spring会回调Aware接口中的方法,而程序员就可以在Aware接口的方法中去进行干涉
  • 事件机制:Spring在不同的阶段会发布不同的事件,程序员可以定义监听器来监听这些事件
  • FactoryBean:允许程序员自定义一个bean对象
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值