1.什么是Spring的循环依赖?
(1)构造器注入的循环依赖,这种更是我们无法解决的,只能抛出异常。
(2)单例模式下singleton的filed注入的循环依赖是没有问题的。
(3)但是prototype的filed注入的循环依赖
这种情况下岂容是不会报错的,因为默认非单例模式的bean是不会自动初始化的,所以我们只需要手动getBean就可以了。
2.Spring的循环依赖解决方案?
(1)设置三级缓存结构,一级缓存就是存放我们真正初始化好的对象,也就是我们能够使用的Bean。
(2)二级缓存为已经实例化好了,但是还没有进行依赖注入的这样的对象,也就是变量还没有赋值,但是已经实例化完成。
(3)三级缓存存放也是实例化好了但是未经过属性填充的对象。
(4)首先会在一级缓存里找,没有的话,就去二级缓存。
(5)二级缓存没有就去三级缓存,其创建好了之后再在二级缓存存一份并将在三级缓存里存放的给删除掉。
(6)然后二级缓存就返回了,这个返回的里面还没有赋值,实际就已经解决循环依赖的问题了。
(7)其实如果没有AOP的话,我们只需要创建两个缓存就可以,一级缓存和三级缓存就可以了,但是如果有AOP,每次我们调用的时候,都会去创建一个新的对象,由于我们是单例的,所以需要一个二级缓存保存这个创建好了的代理对象。
3.SpringMVC的工作流程?
(1)用户发送请求到前端控制器DispatcherServlet。
(2)前端控制器收到请求送给HandlerMapping(处理器映射器)(维护URL到handler的映射,就是处理器)不同类型的对应不用的处理器。
(3)找到了具体的处理器之后返回给前端控制器。
(4)前端控制器调用处理器适配器HandlerAdapter,之后找到具体的处理器(后端处理器,实际就是我们的业务逻辑)
(5)后端处理器返回一个ModelAndView,之后适配器返回一个ModelAndView给前端控制器,前端控制器将其返回给视图解析器。
(6)视图解析器解析后得到具体的View,前端控制器将根据View渲染视图后响应用户。
4.SptingMVC的九大组件?
(1)HandlerMapping,就是根据我们映射的URL找到handler也就是处理器。
(2)HandlerAdapter,但是我们的handler有很多种形式,形式不同,肯定处理得逻辑不同,这个就根据我们的handler的形式来找到特定的处理逻辑就完事了,之后进入里面去执行里面的逻辑。
(3)HandlerExceptionResolver,处理异常的
(4)ViewResolver视图解析器
(5)RequestToViewNameTranslator:处理没有返回值得场景得
(6)LocalResolver:国际化
(7)ThemeResolver:解析主题得
(8)MultipartResolver:处理文件上传
(9)FlashMapManager:处理重定向中得参数。
5.SpringBoot得自动配置原理?
(1)SpringBootConfiguration:实际上和@Configuration是一回事
(2)ComponentScan就是定义扫描包路径得。
(3)@AutoConfiguration里面使用@Impot导入了一个Regist内部类,将我们得扫描路径保存到全局变量去提供查询得。
(4)@Import导入了一个AutoConfigurationImportSelector,在这个里面有一个selectImport方法,其获取META-INF/spring.factories中需要自动配置得名字。
(5)之后将这些东西全都装配。
6.如何理解SpringBoot的starter?
当我们要继承一个组件的时候,正常需要在xml里面去一一配置,但是我们有了这个starter就会把我们需要写入的东西放在spring.factories里面就完事了。
7.BeanFactory和FactoryBean?
(1)BeanFactory是一个bean工厂,是一个工厂类,负责生产和管理一个bean工厂,是一个ioc容器,是spring用来管理和装配普通bean的ioc容器。
(2)FactoryBean是一个bean,是一个可以生产对象和装饰对象的工厂bean,由spring管理,生产的对象是由getObject决定的。
8.谈一谈IOC?
(1)其就是一个容器,是一个map创建好对象之后将其存放在一个map里面。
(2)控制反转就是创建对象的时机由什么时候需要,什么时候创建,不再是我们每次需要新的对象得时候去new出来。
(3)控制反转之后将值注入(依赖注入是实现控制反转的一种手段)。
9.BeanFactory和ApplicationContext的区别?
(1)父子关系,Application对其做出了功能上的扩展,国际化,同时载入多个配置文件等等。
(2)BeanFactory是懒加载的,每个Bean是只有在使用的时候才进行创建,这样要是内部由什么问题是发现不了的;ApplicationContext是容器启动的时候一次性创建所有的对象存放在容器里面。
10.SpringBean的生命周期?
(1)首先解析类,得到bean定义
(2)有多个构造方法的时候,选择合适的
(3)确定好构造方法之后取实例化对象
(4)完成依赖注入
(5)回调AWARE接口,可以进行切入
-比如BeanNameAware就是根据beanId来设置名字。
-比如BeanFactoryAware就是根据传来的bean实例来调用。
(6)调用BeanPostProcessor方法
-postProcessorBeforeInitialization
(7)调用初始化方法
-实现InitializationBean接口,使用afterProoertiesSet方法。
(8)调用BeanPostProcessor后置方法
-postProcessorAfterInitialization
(9)单例的话就放入单例池子
11.Bean的作用域?
(1)singleton就是每一个容器中只有一个;
(2)Prototype一个Bean定义,可以有多个对象
(3)Request一个Http请求会产生一个对象,也就是每一个Http请求都自己的Bean实例。
(4)Session限定一个Bean的作用域是HttpSession的生命周期。
(5)Global session一个Bean的作用域为全局HttpSession的生命周期。
12.Bean如何进行依赖注入?
(1)我们首先反射拿到这个对象。
(2)之后我们去找到所有的标记了@Autowired注解的属性。
(3)注入的时候要是没有的话,还需要重新创建。
13.整个Spring的一个Bean创建流程
(1)首先根据.class文件的无参构造方法创建一个对象。
-这里面会进行一个推断构造方法。
-如果要是有无参的构造方法,默认的情况下会使用无参的。
-有参的话(同时没有无参的),会首先看看参数是不是在Spring中有,有的话就直接用,没有的话会注入一下。
-要是多个得话,那么就会需要去找那个@Autowired的,找不到就报错。
-在这种情况下,要是我们用Component注册了一个,@Bean又注册了两个,一共三个,怎么使用有参构造去注入?首先根据类型找,之后根据名字筛选,如果没有对的上的就报错。
-在这里还需要判断一下有没有出现循环依赖
(2)之后进行依赖注入。
(3)初始化前的操作(@PostConstruct)其实就是扫描所有的方法,看看他有没有这个注解,有这个注解,就执行(应用场景,我们有一个属性user,但是user对象是数据库中查询得到的,所以需要在函数里面查询一下)。
(4)初始化, 需要看看实现了InitializeBean接口的Bean,只要是实现了这个接口,那么就需要去执行里面的afterPropertiesSet方法。
(5)初始化后(AOP)在这个过程中去进行切面操作。
(6)产生代理对象。
-这个代理对象里面会重写我们被代理对象的方法,之后在里面执行切面的逻辑,但是这个切面逻辑中间是包含了我们正常方法的逻辑的。这时候代理对象里面会有一个属性,这个属性的值就是我们被代理对象本身,用这个被代理对象去调用其本身的方法,之后继续执行切面逻辑。
(7)添加到单例池。
14.Spring事务的流程?
(1)开启事务,之后建立一个数据库连接。
(2)之后去将自动提交给设置成false。
其实事务也是面向切面编程的一个例子,我们在事务里面会创建代理对象之后利用代理对象里面的逻辑去执行方法,但是以下情况会失效。
这样失效的原因是,直接调用a,我们实际是用当前对象去调用的,而不是大力对象去调用的,我们解决这种问题有两种方式,1是自己注入自己,之后用自己调用a;另一种就是拆开类,使用另一个类中咋里面写a方法,之后这个去注入另一个类,完了调用另一个类里面的方法。
(3)事务传播的时候,需要将数据库连接进行传递,传递过程中使用的是ThreadLocal,key为ThreadLocal本身,value是Map
15.三级缓存解决循环依赖?
Spring如果有循环依赖得状况,那么就需要在第一步得时候就进行aop,就创建代理对象,如果没有循环依赖得话,那么就需要在属性什么得都填充好了再进行AOP。
(1)第一级缓存:singletonObjects。
(2)第二级缓存:earlySingletonObjects。
(3)第三级缓存:singletonFactories。
-在我们创建对象得时候,最开始是看看有没有再单例池里面,有的话就用,没有就创建,首先需要在creatingSet里面看看自己要注入得属性是不是在里面,要是在的话,那么就认为有循环依赖了,有的话就先进行AOP,之后产生的代理对象放到二级缓存里面(这个前提是我们再二级缓存里面没有发现代理对象,如果有的话就直接用了,这种是防止多个对象同时依赖一个类的对象,要是每次都创建新的就不是单例了)
-之后我们就进行属性填充。
-之后我们就把二级缓存里面的东西删掉之后放到单例池里面。
但是只有两级缓存,打破不了循环依赖, 因为我们AOP实际上是根据原本的对象进行AOP操作的,但是我们原本的对象,他压根就没有,我们需要去哪里拿呢?这个就是我们的第三级缓存的作用。
-三级缓存中存放的是一个喇嘛大表达式(不管你是不是循环依赖),当二级缓存里面没有的时候,就去三级缓存,三级缓存,找到了名字对应的表达式并执行,之后生成一个代理对象(如果要是没有AOP那么就直接返回正常的对象)返回放在二级缓存,三级缓存的删掉,要是不删掉就不能保证单例了。
16.整个Spring初始化的流程?
(1)obtainFreshBeanFactory()就是初始化beanFactory,并且将找到的bean去封装成一个个的BeanDefinition对象。
(2)prepareBeanFactory()为beanFactory设置一些属性(不是很重要)
(3)postProcessBeanFactory()给beanfactory设置一些后置属性
(4)invokeBeanFactoryPostProcessor()完成BeanDefinitionRegistryPostProcessor和另一个接口BeanFactoryPostProcessor后置处理器的调用
(5)registryBeanPostProcessor()注册BeanPostProcessor对象
(6)initMessageSource()国际化处理
(7)initApplicationEventMylticaster()初始化事件管理类
(8)onRefresh()扩展方法
(9)registerListeners()注册事件管理类中监听类
(10)finishBeanFactoryInitialization()bean实例化的核心方法,ioc注入依赖,BeanPostProcessor执行,aop的入口。
17.AOP的相关概念?
(1)Aspect是切面(也叫advisor),其既包括了横切逻辑的定义,也包括了连接点的定义。
(2)PointCut是切点,表示原来就存在的函数,我们相当于对切点进行增强。
(3)Advice是增强,就是目标方法的增强实现。
-@Before:在切点方法执行之前执行。
-@AfterReturning:在切点方法正常执行之后执行。
-@AfterThrowing:在切点方法抛出异常之后执行。
-@After:在切点方法执行之后执行,不管是否执行成功。
-@Around:在切点方法执行之前和之后都执行。
(4)JoinPoint是连接点,是切点的组成部分,每一个函数都称之为一个连接点。
(5)Target就是需要被织入的对象。
18.什么是依赖注入?
依赖注入就是不需要创建对象,只需要在配置文件中描述对象怎么创建,之后把组件和服务链接起来,只需要在配置文件中描述需要哪些服务即可。
19.Spring中可以通过多少种方式完成依赖注入?
(1)使用构造方法注入。
(2)使用setter注入。
20.BeanFactory和ApplicationContext的区别?
(1)B是懒加载/A是创建即加载。
(2)B是A的父接口,没有A的功能多(事件发布,国际化支持)
21.SpringMVC的工作流程?
(1)用户发送请求给中央控制器(DispacherServlet)
(2)中央控制器调用处理器映射器(HandlerMapping),寻找处理器。
(3)处理器映射器返回HandlerExecutionChain给中央控制器,包含了HandlerIntercepter(处理器拦截器)和Handler(处理器对象)。
(4)通用处理器适配器HandlerAdapter调用具体的处理器对象。
(5)调用处理器Controller之后处理完成返回HandlerAdapter之后返回给DispacherServlet一个ModelAndView。
(6)调用viewResolver解析之后得到View响应用户。
22.Spring中的bean是线程安全的吗?
(1)Spring没有对bean做一个多线程的处理,所以不是安全的,
(2)但是一般这个bean不会存储数据,所以他也可以说是安全的。
(3)想要保证安全的话,可以设置prototype来保证,或者是对于存储数据的bean使用ThreadLocal。或者最粗暴的加锁。
23.Spring中的事务传播机制?
(1)当前有就用,没有就创建(require)
(2)必须新建,有的话就挂起(require_new)
(3)有事务就在里面,没有就不用事务(supports)
(4)不能在事务中,有的话就挂起。(not_supports)
(5)在当前事务中,没有就抛异常。(mandatory)
(6)不能在事务中运行,有就抛异常(never)
(7)当前有事务就嵌套,没有就创建(nexted)。
24.多个AOP如何排序?
(1)看看有没有实现Ordered和PriorityOrdered接口。
(2)值越小,越先执行。
25.为什么JDK动态代理只能对实现了接口的类生成代理?
因为其动态代理生成的类已经继承了Proxy类,无法再使用继承的方式去实现。
26.Spring事务的实现的原理?
ThreadLocal,try catch,动态代理。
(1)动态代理:需要逻辑增强的地方使用动态代理。
(2)ThreadLocal:线程间隔离资源,实现不同线程使用不同的数据源。
(3)Try catch:根据业务是不是抛出异常来决定是否回滚。
27.Spring的事务传播机制?
(1)当前有事务,就加入,没有就创建。
(2)当前有事务,就加入,没有就以非事务执行。
(3)当前有事务,就加入,没有就抛异常。
(4)创建新事务,无论当前是否存在。
(5)以非事务方式执行,当前要是有事务,就挂起
(6)以非事务方式执行,当前有事务,就抛出异常。
28.Spring初始化过程?
(1)prepareRefresh():准备初始化
-首先需要设置一下当前容器状态等;
-getEnviroment.validateRequiredProperties():判断是否存在要求的属性在系统中不存在的情况,出现的话就终止加载;
(2)obtainFreshBeanFactory():通过一个工具将解析好的beanDefinition对象注册到BeanFactory中。
(3)preparebeanFactory():对实例化好的beanFactory进行相关设置。
(4)postProcessorBeanFactory():是个空方法。
(5)invokeBeanFactoryPostProcessors():查询在当前的beanfactory中是否有指定类型的Bean信息,有的话就获取并执行相关方法。
(6)registBeanPostProcessor():处理processor的,不是立即调用,是在实例化bean的之前调用processor;
(7)initMessageSource();
(8)initApplicationEventMulticaster():初始化一个时间广播器;
(9)onRefresh()
(10)registerListener():注册应用监听器,将这个类型的Bean都添加到广播器中。
(11)finishFresh():刷新完成。
29.如何实现一个拦截器?
(1)设置一个配置类,实现webmvcconfig
(2)在这个配置类里面添加拦截器。
(3)拦截器实现handlerinterceptor接口,对里面的方法去进行添加拦截路径之类的。
30.Spring事务失效的情况?
(1)数据库压根不支持事务,比如MyISAM就不支持事务。
(2)事务压根没有被Spring管理,就是service层执行的东西,没有加上注解。
(3)不带有事务的方法调用带有事务的方法。
(4)没有配置事务管理器。
(5)对于异常的处理,只有RuntimeException才回滚,其余的不会滚。
(6)只有public修饰的方法才能使事务生效。
31.Spring中bean的生命周期?
(1)ReasorceLoader加载配置信息。
(2)BeanDefinationReader解析配置信息,生成一个个的BeanDefination。
(3)BeanDefination由BeanDefinationRegistry管理起来。
(4)BeanFactoryPostProcessor对配置信息加工。
(5)实例化Bean。
(6)Bean实现了InstantiationAwareBean就调用响应的方法。
(7)使用BeanWrapper进行属性配置。
(8)Bean实现了Aware接口,就调用相应的方法。
(9)BeanPostProcessor里面的before和after方法。
(10)放入HashMap里面。
32.Spring三级缓存和解决方案?
(1)singletonObjects一级缓存;earlySingletonObjects二级缓存。singletonFactories三级缓存。
(2)首先一级缓存寻找,找到了直接返回;没找到就去二级缓存找,找到了直接返回,没找到三级缓存,三级缓存创建,之后剪切到二级缓存,这个时候是只有定义,并没有赋值,所以就这样解决了循环依赖。
(3)整个流程下来,例如a和b相互依赖,首先会建立a的一个三级缓存存放,之后在过程中调用创建b的三级缓存存放,在这个过程中b对象里面有a类型的属性,之后有回到a的getBean在这个里面去a的三级缓存存放中拉出来a的一个对象(执行lambda表达式,b的属性是null)并将其存放到二级缓存里面同时删除三级缓存里面的a,此时二级缓存中的a是一个半成品,但是b已经赋值完成,是一个成品对象了;之后把b放到一级缓存里面,同时把三级缓存里面的东西也删掉了。之后回头给a赋值,a也变成成品对象了,把a存放到一级缓存里面,将二级缓存中的a删掉。
33.Spring中用到的设计模式?
(1)工厂模式创建对象。
(2)代理模式面向切面编程。
(3)单例模式。
(4)模板方法模式。
34.Mybatis如何获取自增id?
35.Spring如何实现解耦?
将所有的对象都交由IOC容器进行接管,对象需要调用的时候,不需要再去里面创建,而是只需要去IOC容器里面拿就行了。
36.Spring依赖注入的方式?
(1)setter方式注入。
(2)构造方法注入。
(3)Autowired注入。
37.我们初始化IOC容器的时候需要进行加载的是什么?
实际上在这里我们加载的是我们的BeanDefinition对象。
38.BeanFactory和FactoryBean的区别?
(1)BeanFactory:是一个根接口,同时也是一个容器。BeanFactoryPostProcessor就是对我们的BeanDefination对象进行扩展实现,同时再BeanFactory里面有一个beanDefinationMap去存放这个Bean定义。我们的BeanFactoryPostProcessor就是对BeanDefition进行赋值或者是其他的操作的。BeanFactory是一套标准的流水线工作,需要经过完整的步骤和过程才能创建出具体的对象。
(2)FactoryBean:是一种Bean的类型,我们可以通过实现接口来保证返回任何类型的Bean对象,更像是一个私人定制的东西,是按照用户的需求创建出具体的对象。
39.Bean的生命周期?
(1)首先是实例化,就是为对象在堆中开辟空间,并使用反射来创建对象。
(2)之后就是初始化:
——给自定义属性进行赋值,使用popularBean
——给容器属性进行赋值,使用aware,实现特定的aware接口
在这个时候Bean实际上已经完成了赋值并且可以拿出来进行使用了,但是要考虑一件事,就是如果我们后续的bean需要进行扩展怎么办,这个时候就需要预留扩展点。
——执行前置方法BeanPostProcessor,AOP,动态代理,cglib/jdk
——执行初始化方法并判断是否实现了initializingBean的方法,实现了的话就去执行afterPropertiesSet方法。
——执行后置方法BeanPostProcessor,AOP,动态代理,cglib/jdk
(3)对象使用
(4)对象销毁
40.小问题,抽象类和接口的区别?
(1)接口是从上到下的,预先定义好约束和规范,后续按照这个约束进行开发。
(2)抽象类更像是由下向上的,将我们公用的东西去抽象出来进行使用。
41.容器启动的流程?
(1) 准备工作,设置标志位(比如当前容器状态,开关),准备某些容器对象。
(2) 创建容器DefultListableBeanFactory
(3) 加载配置文件(读取我们的配置项)
(4) 给容器对象进行初始化操作,进行赋值。
(5) 预留一个扩展点,空方法。
(6) 执行BeanFactoryPostProcessor,执行BeanFactory的前置处理器,实际就是为我们的BeanDefinition进行修改属性一类的操作。
(7) 进入实例化的准备环节
-准备BeanPostProcesor
-准备广播器对象
-准备监听器对象
-进行国际化配置对象
-进行扩展点的预留onrefresh
(8) 进行对象的实例化,初始化操作。
(9) 进行某些扫尾工作。
42.如果只有两级缓存为什么不能解决循环依赖?
如果运行过程中不会创建代理对象,那么二级缓存能够解决循环函数依赖的问题;如果需要代理对象,就必须要使用三级缓存。
由于创建代理对象是再BeanPostProcessor里面的方法去进行创建的,再创建代理对象之后我们还需要考虑,究竟返回的是代理对象还是对象本身,但是机器没办法去判断什么时候返回代理对象,什么时候返回对象本身,这个时候三级缓存的作用就来了,它里面存放的是一个表达式而不是直接就是一个对象,他是在我们获取的时候才会去判断是不是实现了这种AOP的接口,是的话根据具体情况进行代理对象的创建,之后存放到二级缓存里面。