Spring面试问题整理
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使用三级缓存解决了循环依赖的问题:
- 第一级缓存是单例池singletonObjects:它是用来保存经过了完整生命周期之后的bean。
- 第二级缓存是earlySingletonObjects:它是用来保存暂时还没有经过完整生命周期的bean。
- 第三级缓存是singletonFactories:它是用来保存提前进行AOP的工厂,它的作用是预先准备一个工厂,这个工厂是在需要时才执行
Spring中为什么要用三级缓存来解决循环依赖?
- 为什么要用三级缓存解决循环依赖
因为用三级缓存可以保证两个代理对象能够相互拿到循环依赖
对普通的对象的相互依赖 用两级缓存就可以解决
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容器里找到对应的属性值
- 实例化前方法:postProcessBeforeInstantiation方法
- BeanPostProcessor
- 初始化前方法:postProcessBeforeInitialization()
这里会处理 @PostConstruct 和 @PreDestroy 注解 - 初始化后方法:postProcessAfterInitialization()
AOP功能就是这里实现的:AnnotationAwareAspectJAutoProxyCreator重写的postProcessAfterInitialization()对对象进行增强
- 初始化前方法:postProcessBeforeInitialization()
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()方法:如果当前对象需要增强,就为其生成代理对象
当执行对项目标方法时,通过获取目标方法的拦截器链(把增强器包装成拦截器),然后递归调用每一个拦截器进行执行
- BeanFactoryAware 实现这个接口就可以拿到容器中所有的beanDefinition
目标方法的执行流程
当对象是通过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() 调用被增强的方法
- 首先进入@AfterThrowing对应的拦截器AspectJAfterThrowingAdviceInterceptor
所以最后方法的执行顺序就是:
正常执行:前置通知->目标方法->后置通知->返回通知
正常执行:前置通知->目标方法->后置通知->异常通知
为什么要用拦截器链
通过拦截器链的机制,保证通知方法和目标方法的执行顺序
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对象