- 1、Spring的容器和对象的创建流程?
- 2、Spring的Bean的生命周期
- 3、Spring是如何加载配置文件到应用程序中的?
- 4、为什么会产生循环依赖问题?循环依赖问题在Spring中是如何解决的(三级缓存相关)?
- 5、循环依赖、三级缓存,还是回头多看下视频和源码
- 6、谈谈对Spring IOC的理解、原理和实现?
- 7、谈一下Spring IOC的底层实现?
- 8、BeanFactory和FactoryBean有什么区别?
- 9、Spring中使用到的设计模式?
- 10、Spring中AOP的底层实现原理?
- 11、Spring的事务是如何回滚的(Spring的事务管理是如何实现的)?
- 12、谈一下Spring的事务传播特性?
- 13、Spring事务的隔离级别有哪些?
- 14、Spring Bean的作用域?
- 15、Spring Bean的线程安全问题?
- 16、BeanFactory和ApplicationContext的区别?
- 17、Spring注入bean的方式?
- 18、@Autowired和@Resource注解的区别?
- 18、将一个类声明为Bean的注解有哪些?
- 19、对SpringIOC和SpringAOP的理解?
- 20、@Component和@Bean的注解的区别?
- 21、@Bean注解的作用?
- 22、@Autowired注解的作用?
源码怎么看:
1.先梳理脉络,然后再抠细节;
2.不要忽略注释;
3.善用各种小工具,如翻译工具;
4.见名知意;源码中方法等的名字是很规范的;
5.大胆猜测,小心验证;
6.坚持看;
spring bean 的实例化和初始化:
实例化:(不论是spring的bean,还是new,反射等得出来的bean),实例化都是在堆空间中开辟空间,属性都是默认值;
初始化:1.填充属性值; 2.调用初始化方法(完成额外的扩展功能);
https://blog.csdn.net/a745233700/article/details/80959716/
1、Spring的容器和对象的创建流程?
第一步:创建容器;
第二步:加载配置文件,将Bean信息封装成BeanDefiniton;
第三步:调用执行BeanFactoryPostprocessor;
第四步实例化对象前的准备工作:
准备BeanPostProcessor;
准备监听器、事件和广播器;
第四步:实例化对象;
第五步:初始化对象;
第六步:获取到完整的对象;
2、Spring的Bean的生命周期
大体上,分为Bean实例化、Bean的初始化以及Bean的使用和销毁,当然这里面还有很多细节:
- Spring容器根据Spring Bean的定义,也就是Bean Definition,通过反射创建出Bean的实例;
- 然后会给Bean的属性值赋值;
- 如果Bean实现类一些以Aware为后缀结尾的接口(*Aware),就调用相应重写的方法(比如实现了BeanNameAware接口,就会调用setBeanName()方法,传入Bean的名字);
- 如果有和容器相关的BeanPostProecesspor对象(也就是后置处理器,即是否实现了BeanPostProcessor接口,重写了里面的方法, 并放到了容器中),则执行后置处理器的before方法(方法名:postProcessBeforeInitialization());
- 如果Bean实现了InitializaingBean接口,则执行afterPropertiesSet()方法;
- 如果Bean在配置文件中的定义,包含init-method属性,则执行指定的方法;
- 如果有和容器相关的BeanPostProecesspor对象,执行后置处理器的after方法(方法名postProcessAfterInitialization());
- 当要销毁Bean的时候,如果Bean实现了Disposablebean接口,则执行destory()方法;
- 当要销毁Bean的时候,如果Bean在配置文件中定义了包含destory-method属性,执行指定的方法;
3、Spring是如何加载配置文件到应用程序中的?
主要是loadBeanDefinitons();这个方法,进行了初始化documentReader,并进行xml文件的读取解析;
- 首先,要有spring的xml配置文件;
- 通过IO流读取配置文件;
- 解析配置文件为document的形式;
- 遍历document的每一个元素,并进行解析操作;
- 解析时,找到对应的处理器handler,对不同的元素进行解析操作;
- 解析出的元素都是放到BeanDefition中;
4、为什么会产生循环依赖问题?循环依赖问题在Spring中是如何解决的(三级缓存相关)?
->什么是循环依赖?
循环依赖就是好比一个类A,A里面有个B属性;然后B本身也是一个类,B里面又有个A属性;(这里,A中的B要把属性注入进去,B中的A也要注入进去)这样Spring在创建A的时候,发现需要依赖B,那就要去创建B实例,发现B又依赖于A,又去创建A,这样形成一个闭环,无法停止下来。这就是循环依赖;
->循环依赖产生的原因?
就是两个Bean在创建的过程中互相依赖,但始终有一个B不能够率先完成创建,导致了一个类似于死循环的问题;
->循环依赖在Spring中是如何解决的?
跟Spring的三级缓存有关;
首先,Spring中Bean的创建过程,是先实例化,再进行初始化(在初始化一开始的时候会进行属性填充);
1.先创建A对象,实例化A对象,此时A对象中的b属性为null;
2.这时会从容器中查找B对象,如果找到了,则直接赋值(循环依赖时不走这步)中,找不到就会去创建B对象;
3.创建了B对象,对B对象进行实例化,此时B对象中的a属性为null,填充属性a;
4.为了给B对象填充属性a,从容器中查找A对象,找不到则直接创建;
这四步是形成闭环的原因,但是,为了给B对象填充属性A的时候,A对象是存在的,只不过A对象还不是一个完整的状态,只完成了实例化,但是没有完成初始化;也就是程序在调用过程中,拥有了某个对象的引用,spring在后期给他完成赋值操作,优先把非完整的状态对象进行赋值,等待后续操作来完成赋值,相当于提前暴露了某个不完整对象的引用。所以解决问题的核心在于把实例化和初始化分开操作,也是解决循环依赖问题的关键。
当所有的对象都完成实例化和初始化操作之后,还要把完整对象放到容器中。此时在容器中存在对象的有这两种状态:完成实例化但未完成初始化,完整状态。 因为都存在于容器中,所以要使用不同的map结构来进行存储,此时就有了一级缓存和二级缓存,如果一级缓存中有了,那么二级缓存中就不会存在同名的对象,因为他们的查找顺序是1,2,3这样的方式来查找的,1及中如果有了,就不会去二级三级里面找了。一级缓存中放的是完整对象,二级缓存中放的是非完整对象。
为什么需要三级缓存?三级缓存的value类型是ObjectFactory,是一个函数式接口,存在的意义是保证在整个容器的运行过程中同名的bean对象只能有一个。
如果一个对象需要被代理,或者说需要生成代理对象,那么要不要优先生成一个普通对象?要
普通对象和代理对象是不能同时出现在容器中的,因此当一个对象需要被代理的时候,就要使用代理对象覆盖掉之前的普通对象,在实际的调用过程中,是没有办法确定什么时候对象被使用,所以就要求某个对象被调用的时候,优先判断此对象是否需要被代理,类似于一种回调机制的实现,因此传入lambda表达式的时候,可以通过lambda表达式来执行对象的覆盖过程,getEarlyBeanReference()
因此,所有的bean对象在创建的时候要优先放到三级缓存中,在后续的使用过程中,如果需要被代理则返回代理对象,如果不需要被代理,则直接返回普通对象。
Spring通过三级缓存解决循环依赖:
一级缓存为单例池singletonObjects;
二级缓存为早期曝光对象earlySingletonObjects;
三级缓存为早期曝光对象工厂singletonFactories;
=>假设把已经填充了属性值的对象称为成品,实例化但还没有填充值的称为半成品;
A、B两个类发生循环依赖,在A的实例化完成后,就是用实例化后的对象去创建一个对象工厂,添加到三级缓存中,当A进行属性注入的时候,会去创建B,同时B又依赖了A,所以创建B的同时,又会去调用getBean(a)来获取需要的依赖,此时getBean(a)会从缓存中获取:
创建B在getBean(a)的时候,先获取到的是三级缓存中的工厂,调用对象工厂的getObject()方法来获取道对应的对象a,得到这个对象a后将其注入到B,这时B会走完它的生命周期流程;
当B创建完后,会将B注入到A中,此时A在完成它的整个生命周期;
缓存的放置时间和删除时间?
三级缓存:在CreateBeanInstance之后放置(addSingletonFactory方法);
二级缓存:第一次从三级缓存确定对象是代理对象还是不同对象的时候放置,同时删除三级缓存;(getSingleton()方法)
一级缓存:生成完整对象之后放到一级缓存,删除二三级缓存;(addSingleton()方法)
5、循环依赖、三级缓存,还是回头多看下视频和源码
6、谈谈对Spring IOC的理解、原理和实现?
总:
IOC就是控制反转。原来的对象是由使用者进行控制,有了spring后,可以把整个对象交给spring帮我们进行管理;它对应还有个DI:依赖注入,把对应的属性的值注入到具体的对象中;
我们使用Spring的IOC,是指我们使用Spring的IOC容器:是具体的对象的存储容器,是使用map来存储,在spring中一般存在三级缓存,在一级缓存singletonObjects中存放完整的bean对象,整个bean的生命周期都由ioc容器来管理。
【分】的部分看情况说;
分:
1.聊IOC容器一般要涉及到容器的创建过程(BeanFactory,DefaultListableBeanFactory):
首先,容器有个最上层的根接口BeanFactory,这只是一个根接口,没有提供对应的子类实现,我们在实际调用过程中,普遍使用的是DeafultListableBeanFactory这个实现类;创建了BeanFactory之后,会优先向Bean工厂中设置一些参数;
2.容器加载好后,会准备解析bean对象,准备要创建的班对象的定义BeanDefition,这时会涉及到xml或者注解的解析,解析完毕后,才能获得BeanDefition;
3.BeanFactoryPostProcessor的处理,完成一些扩展功能,比如PlaceHolderConfigurSupport(用来处理占位符),ConfigurationClassPostProcessor(用于处理一些配置信息和注解扫描)
4.BeanPostProcessos后置处理器的注册功能,方便后续对Bean对象完成具体到扩展功能;
5.通过反射的方式,将BeanDefition对象实例化成具体到Bean对象;
6.Bean对象的初始化过程(填充属性、调用aware子类的方法、调用后置处理器的前置处理方法、调用init-method方法、调用后置处理器的后置处理方法);
7.生成完整的Bean对象,通过getBean()方法可以直接获取;
8.销毁过程;
spring的IOC容器中的Bean都是通过反射的方式生成,它比较核心的除了通过创建Bean的实例外,还有对Bean的属性的填充,以及对Bean的生命周期的管理;
7、谈一下Spring IOC的底层实现?
谈底层实现可能涉及的地方: 工作原理,过程,数据结构,流程,设计模式,设计思想
谈的时候应着手的地方: 你对它的理解和你了解的实现过程
针对本问题: 反射、工厂、设计模式(会的说,不会的不说)、关键的几个方法
createBeanFactory , getBean ,doGetBean , createBean , doCreateBean,createBeanInstance(getDeclaredConstructor(),newinstance),populateBean
示例:
1.先通过creatBeanFactory()这个方法创建一个Bean工厂,这个工厂的类型是DefaultListableBeanFactory;
2.Bean工厂创建完毕后,开始循环创建对象;因为容器中的Bean默认都是单例的,所以优先通过getBean()、doGetBean()方法从容器中查找Bean,找不到的话,就通过createBean()、doCreateBean()方法,以反射方式创建对象,一般情况下使用的是无参构造器(getDeclaredConstructor(),newinstance);
3.然后进行对象的属性填充,即populateBean()方法;
4.填充完属性后,进行其他初始化操作initializingBean();
8、BeanFactory和FactoryBean有什么区别?
相同点:他们都是用来创建Bean对象的;
不同点:使用BeanFactory创建Bean对象的时候,必循要遵循严格的生命周期流程; 如果想要简单的自定义某个对象的创建,同时创建完成的对象想要交给Spring来管理,就必须实现FactoryBean接口;
FactoryBean接口里面有三个方法:
isSingleton() : 是否是单例对象
getObjectType(): 返回获取对象的类型
getObject(): 自定义创建对象的过程(可以自己new、反射性、动态代理等)
FactoryBean:使用示例:
https://www.csdn.net/tags/OtDaAg4sOTk3NDQtYmxvZwO0O0OO0O0O.html
https://blog.csdn.net/feiying0canglang/article/details/121367985
9、Spring中使用到的设计模式?
单例模式:Spring中的Bean默认都是单例的;
原型模式:可以指定Bean的作用域为prototype;
工厂模式: Bean工厂BeanFactory就是工厂模式;
模板方法模式:postProecessBeanFactory(),onRefresh(),initProperyValue();
策略模式:XmlBeanDefitionReader,PropertiesBeanDefinitionReader
观察者模式:listener,event,multicast
适配器模式:Adapter
装饰者模式:BeanWrapper
责任链模式:使用aop的时候会生成一个拦截器
代理模式:动态代理
委托者模式:delegate
10、Spring中AOP的底层实现原理?
AOP是IOC的一个扩展功能,先有的IOC,再有的AOP;AOP是在IOC整个流程中,新增的一个扩展点;
AOP针对的对象是Bean,主要是BeanPostProcessor(后置处理器)接口
总:
AOP的概念:AOP是面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(比如:事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,有利于系统的可拓展性和可维护性;
基于动态代理:Spring AOP是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP 会使用JDK Proxy区创建代码对立,而对于没有实现接口的对象,就无法使用JDK动态代理了,Spring会使用Cglib去省城一个被代理对象的子类来作为代理;当然,也可以使用AspectJ,Spring AOP已经继承lAspectJ;
分:
Bean的创建过程中,有一个步骤可以对Bean进行扩展实现,AOP本身就是一个扩展功能,所以在BeanPostProcessor的后置处理方法中,进行实现;
1.代理对象的创建过程(advioce、切面、切点)
2.通过jdk或cglib的方式生成代理对象;
3.在执行方法调用的时候,会调用到生成的字节码文件中,直接会找到DynamicAdvisoredInterceptor类中的intercept方法,从此方法开始执行
4.根据之前定义好的通知来生成拦截器
5.从拦截器链中依次获取每一个通知开始进行执行,在执行过程中,为了方便找到下一个通知是哪个,会有一个CglibMethodInvocation的对象,找的时候是从-1的位置依次开始查找并且执行的。
11、Spring的事务是如何回滚的(Spring的事务管理是如何实现的)?
总:
Spring的事务是由AOP来实现的。首先要生成具体的代理对象,然后按照AOP的整套流程来执行具体的操作逻辑。正常情况下要通过通知来完成核心功能,但是在Spring中,事务不是通过通知来实现的,而是通过TransactionInterceptor来实现的,然后调用invoke来实现具体的逻辑。
分:
1.先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开启新事务;
2.当需要开启时,获取数据库连接,关闭自动提交功能,开启事务;
3.执行具体的sql逻辑操作;
4.在操作过程中,如果执行失败了,那么会通过completeTransactionAfterThrowing来完成事务的回滚操作,回滚的具体逻辑时通过doRollBack()方法来实现的,实现的时候,也是要先获取连接对象,通过连接对象来回滚;
5.如果在执行过程中,没有任何意外情况的发生,那么通过commitTransactionAfterReturning来完成事务的提交操作,提交的具体逻辑是通过doCommit方法来实现的,实现的时候也要获取链接,通过链接对象来提交;
6..当事务执行完毕之后需要清除相关的事务信息cleanupTransactionInfo
如果想要聊的更加细致的话,需要知道TransactionInfo,TransactionsStatus
12、谈一下Spring的事务传播特性?
Spring的事务传播特性有七种:
Required,Requireds_new,nested,Support,Not_Support,Never,Mandatory
某个事务嵌套另一个事务的时候怎么办?
A方法调用B方法,AB方法都有事务,并且传播特性不同,那么A如果有异常,B怎么办?B如果有异常,A怎么办?
总:
事务的特性指的时,不同的方法的嵌套调用过程中,事务应该如何进行处理,是用同一个事务还是用不同的事务,当出现异常的时候,会回滚还是提交,两个方法之间的相互影响;在日常工作中,使用比较多的是required和requireds_new,nested;
分:
1.事务可以分为三类:支持当前事务、不支持当前事务和嵌套事务这三种
2.如果外层方法是required,内层方法是required、requireds_new、nested;
3.如果外层方法是requireds_new,内层方法是:required,requireds_new,nested
4.如果外层方法是nested,内层方法是:required,requireds_new,nested
->核心处理逻辑十分简单:
判断内外方法是否是同一事务,如果是同一事务,则异常统一在外层方法处理;
如果不是,内层方法有可能影响到外层方法,但是外层方法是不会影响内层方法的(大致可以这么理解,但是有个别情况不同:nested)
13、Spring事务的隔离级别有哪些?
Spring中的事务隔离级别就是数据库的隔离级别,有以下四中:
read_uncommitted:
read_committed:
repeatable read:
serializable:
再进行配置等时候,如果数据库和spring代码中的隔离级别不同,那以spring的配置为主;
14、Spring Bean的作用域?
15、Spring Bean的线程安全问题?
因Spring中默认Bean是单例的,所以是存在线程安全问题的;
16、BeanFactory和ApplicationContext的区别?
BeanFactory和ApplicationContext都是接口,ApplicationContext是BeanFactory的子接口;
BeanFactory采用延迟加载的方式注入Bean,而ApplicationContext是在IOC容器启动时,就一次性创建出所有Bean。 (好处是可以及时发现Spring配置文件中的错误);
BeanFactory是Spring中最顶层的接口,只是提供了最简单的容器功能,包含了各种Bean的定义、加载、实例化、依赖注入和生命周期管理,而ApplicationContext在它的基础之上,提供了更多的功能:比如支持国际化、资源文件的访问,载入多个配置文件等;
17、Spring注入bean的方式?
通过注解注入Bean,可以通过以下注解:
@Autowired注解;@Resource注解;@Inject注解;
通过XML配置文件方式注入Bean:
set方法;构造器方法;
静态工厂了;实例工厂;
18、@Autowired和@Resource注解的区别?
@Auotwired是Spring内置的注解,默认的注入方式是byType,根据类型进行匹配;也就是说会优先根据接口类型去匹配并注入Bean(注入接口的实现类);
->这会有什么问题呢?
当一个接口存在多个实现类的话,byType这种方式就无法正确注入对象了,因为这个时候Spring会找到多个满足条件的选择,默认情况下就不知道该选择哪一个;
这时,它的注入方式会变成byName,根据名称进行匹配,这个名称默认情况下就是类名的小驼峰(首字母小写)的形式,这里比较建议配合@Qualifier注解,显示指定名称;
@Resource注解是JDK提供的注解,默认注入方式是byName,根据名称匹配;如果无法通过名称匹配对应的Bean的话,注入方式会变成byType;
@Resource有两个比较重要的属性,name和type。如果仅指定了name属性,则注入方式为byName,如果仅指定了type属性,则注入方式为byType,如果同时指定name和type属性,则注入方式为byName+byType;
—-> 当一个接口存在多个实现类的情况下,@Autowired和@Resource都需要通过名称才能正确匹配到对应的Bean,@Autowired可以配@Qualifier注解来显式指定名称,而@Resource可以通过name属性来显示式指定名称;
18、将一个类声明为Bean的注解有哪些?
@Component:通用的注解,可以标注任意类为Spring组件;如果不知道Bean属于哪个层,可以用@Component注解来标注;
@Repository:对应持久层,也就是DAO层,主要用于数据库相关操作;
@Service:对应service层,主要涉及一些逻辑操作;
@Controller:对应控制层,主要用于接收用户请求和返回响应;
19、对SpringIOC和SpringAOP的理解?
20、@Component和@Bean的注解的区别?
21、@Bean注解的作用?
@Bean注解用于告诉方法,产生一个Bean对象,把这个对象交给Spring管理也就是交给IOC容器管理;产生这个Bean对象的方法Spring只会调用一次,随后Spring将会把这个Bean对象放在IOC容器中;
它是一个方法级的注解,可以用在@Configuration注解标注的配置类里面,也可以用在@Component注解的类里;
一般都是用于配置类的内部,标注内部的方法上,把方法的返回值交给spring管理,bean的名字默认是方法名,也可以自己指定名字。
比较常见的应用场景是整合第三方资源,比如:
整合mybatis的分页插件;整合线程池信息;解决跨域信息的配置类等;