Spring面试题学习
- 1、Spring是什么?
- 2、Spring的优缺点是什么?
- 3、谈谈你对AOP的理解
- 4、谈谈你对IOC的理解
- 5、BeanFactory和ApplicationContext有什么区别?
- 6、描述一下Spring Bean的生命周期
- 7、解释下Spring支持的几种Bean的作用域
- 8、Spring框架中的单例Bean是线程安全的么?
- 9、Spring框架中都用到了哪些设计模式
- 10、Spring事务的实现方式和原理以及隔离级别
- 11、Spring事务传播机制
- 12、Spring事务什么时候会失效
- 13、什么是Bean的自动装配,有哪些装配方式
- 14、Spring Boot、Spring MVC、Spring有什么区别
- 15、SpringMVC工作流程
- 16、SpringBoot中的starter
- 17、SpringCloud和Dubbo的区别
- 18、SpringCloud核心组件及其应用
- 19、分布式事务如何处理,怎么保证事务一致性
- 20、Spring框架中Bean的创建过程是怎样的
- 21、Spring如何处理循环依赖问题
- 22、SpringMVC中的控制器是不是单例模式?如果是,如何保证线程安全
1、Spring是什么?
- Spring是一个轻量级的开源的非侵入式的J2EE框架.它是一个容器框架,用来装JavaBean,中间层框架,可以起到一个连接作用,可以让企业级开发更快,更简洁
- Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架
- 从大小与开销两方面而言Spring都是轻量级的
- 通过控制反转(IOC)的技术达到松耦合的目的
- 提供了面向切面编程的支持,允许通过分离应用的业务逻辑与系统关机服务进行内聚性的开发
- 包含并管理应用对象(Bean)的配置和生命周期,这个意义上是一个容器
- 将简单的组件配置、组合成为复杂的应用,这个意义上是一个框架
2、Spring的优缺点是什么?
- 优点
- 方便解耦,简化开发,方便维护对象
- AOP编程的支持,使我们可以在不修改代码的情况下对业务代码进行扩展和增强,对于通用任务进行集中管理,提供了更好的复用
- 声明式事务的支持,提高了开发效率
- 方便程序的测试
- 方便集成各种优秀的开源框架,只需要简单的配置就可以集成第三方框架
- 降低Java EE API的使用难度,简化开发,帮我们封装了很多功能性代码
- Java源码是经典学习范例,底层的实现,反射…设计模式… 都是我们值得学习,提供许多扩展接口供外部进行扩展
- 缺点
- 从应用层面说是没有缺点的
- 简化开发,如果向深入到底层去了解就非常困难(上层使用越简单,底层封装的就越复杂)
- 源码缺点: 由于Spring大而全(要继承这么多框架、提供非常非常多的扩展点,经过十多年的代码迭代),代码量非常庞大,一百多万行,对于深入学习源码带来了一定困难
3、谈谈你对AOP的理解
- 系统是由许多不同的组件所组成的,每一个组件都有其特定的核心功能,除了这些核心功能之外,这些组件通常还承担着一些通用的职责.
- 例如日志、事务管理、安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务京曾被称为横切关注点,因为他们会跨越系统的多个组件
- 当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力.也就是说OOP允许你定义从上倒下的关系,但并不适合定义从左到右的关系.例如日志功能
- 日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用
- AOP将程序中的交叉业务逻辑封装成一个切面,然后租入到目标对象的具体业务逻辑中去.AOP可以对某个对象或某些对象的功能进行增强,可以在执行某个方法之前额外的做一些事情或在某个方法执行之后额外的做一些事情
4、谈谈你对IOC的理解
- 容器概念
- IOC容器是将上就是一个map,里面存的是各种对象,在项目启动的时候会读取配置文件中的bean节点,根据全限定类名使用反射创建对象放到map中,扫描到有注解的类还是通过反射创建对象放到map里
- 这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI(依赖注入)
- 控制反转
- 没有引入IOC容器之前,无论时创建还是使用,控制权都在自己手上,一如IOC容器之后,对象与对象之间失去了直接联系,在需要的时候容器会主动获取或创建需要的对象,控制权颠倒
- 全部对象的控制权全部上缴给第三方IOC容器,所以,IOC容器成了整个系统的关键核心(粘合剂)
- 依赖注入
- 获得依赖对象的过程被反转了
- 控制被反转后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入
- 依赖注入时实现IOC的方法,就是由IOC容器在运行期间,动态的将某种依赖关系注入到对象中
5、BeanFactory和ApplicationContext有什么区别?
- ApplicationContext是BeanFactory的子接口
- ApplicationContext提供了更完整的功能
- 继承MessageSource,因此支持国际化
- 统一资源文件访问方式
- 提供在监听器中注册Bean的事件
- 同时加载多个配置文件
- 载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的web层
- BeanFactory采用的是延迟加载形式来注入Bean的,只有在使用某个Bean时,才会对这个Bean进行记载实例化,不容易发现一些存在的Spring配置问题
- ApplicationContext在容器启动时一次性把所有的Bean全部创建,在容器启动时就能发现Spring中存在的一些配置错误,有利于检查所依赖的属性是否注入,并且也缩短使用时的响应时间
- 相比于基本的BeanFactory,ApplicationContext唯一的不足时占用内存空间,当应用程序配置Bean较多时,程序启动较慢
- BeanFatory通常以编程的方式被创建,Application还能以声明的方式创建,如使用ContextLoader
- BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而Application则是自动注册
6、描述一下Spring Bean的生命周期
- 解析类得到BeanDefinition
- 如果有多个构造方法,则要推断构造方法
- 确定好构造方法之后,进行实例化得到一个对象
- 对对象中的加了@Autowired注解的属性进行属性填充
- 回调Aware方法,比如BeanNameAware,BeanFactoryAware
- 调用BeanPostProcessor的初始化前的方法
- 调用初始化方法
- 调用BeanPostProcessor的初始化后的方法,在这里会进行AOP
- 如果当前创建的Bean是单例的则会把Bean放入单例池
- 使用Bean
- Spring容器关闭时调用DisposableBean中的destory()方法
7、解释下Spring支持的几种Bean的作用域
- singleton(单例) : 默认,每个容器中只有一个Bean实例,由BeanFactory来维护
- prototype(非单例) : 每次主任也都创建一个新的对象
- request (单例)
- session (单例)
- application (单例)
- websocket (单例)
8、Spring框架中的单例Bean是线程安全的么?
-
Spring中Bean默认是单例模式的,并没有对Bean进行多线程的封装处理
-
如果Bean是有状态的,就需要开发人员来保证线程安全,如换为原型模式,这样每次请求都相当于是
new Bean()
9、Spring框架中都用到了哪些设计模式
-
工厂模式
- BeanFactory
-
单例模式 保证了一个类仅有一个实例
-
适配器模式
- HaddlerAdapter
-
装饰器模式
-
代理模式
- AOP
-
观察者模式
- 监听器
-
策略模式
-
模板模式
-
委派模式
-
责任链模式
-
访问者模式
10、Spring事务的实现方式和原理以及隔离级别
-
两种
- 编程式 写在代码中 TransactionTemplate
- 声明式 @Transactional
-
原理
- 首先,事务这个概念是数据库层面的,Spring只是基于数据库中的事务进行了扩展,以及提供了一些能让程序员更加方便操作事务的方式
- 在一个方法上加了@Transaction注解之后,Spring会基于这个类生成一个代理对象,会将这个代理对象作为Bean,当在使用这个代理对象的方法时,如果这个方法上存在@Trancational注解,那么代理逻辑会先把事务的自动提交设置为false,然后去执行原本的业务逻辑方法,如果执行业务逻辑方法没有发生异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑方法出现了异常,那么代理逻辑中就会将事务进行回滚。针对哪些异常回滚事务是可以配置的,可以使用@Trancational注解中的rollbackFor属性进行配置,默认情况下会对运行时异常和Error进行回滚
-
隔离级别
- 读未提交
- 读已提交 (oracle默认)
- 可重复读 (mysql默认)
- 串行化
11、Spring事务传播机制
一个数据库连接对应一个事务
- REQUIRED (默认)如果当前没有事务就自己新建一个事务,如果当前有事务,就加入
- SUPPORTS 当前存在事务,则加入当前事务,如果没有事务,就以非事务方法执行
- MANDATORY 当前存在事务,则假如当前事务,如果没有事务,则抛出异常
- REQUIRES_NEW 创建一个新事务,如果存在当前事务,则挂起当前事务
- NOT_SUPPORTED 以非事务方式执行,如果当前存在事务,则挂起当前事务
- NEVER 不使用事务,如果当前事务存在,则抛出异常
- NESTED 如果当前事务存在,则在嵌套事务中执行,否则新建一个事务
12、Spring事务什么时候会失效
- Spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是AOP不起作用
- 发生自调用
- 方法不是public
- @Trancational只能放在public方法上,否则事务会失效,也可以在非public方法上开启AspctJ代理模式
- 数据库不支持事务
- 没有被Spring管理
- 异常被吃掉,事务不回滚(或者抛出的异常未被定义)
- 业务和Spring事务代码必须在同一线程
13、什么是Bean的自动装配,有哪些装配方式
- 在
<bean>
中定义"autowire"属性- no 缺省状态下通过"ref"或"value"属性手动设定(手动装配)
- byName 根据Bean的属性名称进行自动装配
- byType 根据Bean的类型进行自动装配
- constructor 类似byType,不过是应用于构造器的参数
- autodetect 如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配
14、Spring Boot、Spring MVC、Spring有什么区别
- Spring是一个IOC容器,用来管理Bean,使用依赖注入实现控制反转,可以很方便的整合各种框架,提供AOP机制,弥补了OOP的代码重复问题,更方便将不同类不同方法中的共同处理抽取成切面,自动注入给方法执行
- SpringMVC是Spring对web框架的一个解决方案,提供了一个总的前端控制器Servlet,用来接收请求,然后定义了一套路由策略即适配器执行Handle,将Handle结果使用视图解析技术生成视图展现给前端
- SpringBoot是Spring提供的一个快速开发工具包,让程序员能够更加方便,快速的开发Spring+SpringMVC应用,简化了配置(约定大于配置,约定了默认配置),整合了一系列解决方案(starter),…可以开箱即用
15、SpringMVC工作流程
- 用户发送请求至前端服务器DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理器映射器
- HandlerMapping找到具体的处理器(xml配置、注解),生成处理器以及处理器拦截器(如果有),一并返回给DispatcherServlet一个执行链
- DispatcherServlet根据执行链调用HandlerAdapter处理器适配器
- HanlerAdapter经过适配调用具体的处理器(Controller,后端控制器)
- Controller执行完成返回ModelAndView
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover进行解析后返回具体的View
- DispatcherServlet根据View进行视图渲染
- DispatcherServlet将视图响应给用户
16、SpringBoot中的starter
- starter就是定义一个starter的jar包,写一个@Configuration配置类、将这些Bean定义在里面,然后再starter包的META-INF/spring.factories中写入该配置类,springboot会按照约定来加载该配置类
- 开发人员只需要将相应的starter包依赖进应用,进行相应的属性配置(默认配置不需要配置),就可以直接进行代码开发,使用对应的功能了
17、SpringCloud和Dubbo的区别
- 底层协议
- SpringCloud基于HTTP协议
- Dubbo基于TCP协议 (Dubbo性能相对比较好)
- 注册中心
- SpringCloud使用的Eurka 高可用
- Dubbo推荐使用Zookeeper 强一致
- 模型定义
- SpringCloud将一个应用定义为一个服务
- Dubbo将一个接口定义为一个服务
- SpringCloud是一个生态关注一整套解决方案,Dubbo是SpringCloud生态中关于服务调用的一种解决方案
18、SpringCloud核心组件及其应用
-
Eureka 服务注册与发现
- 注册
- 每个服务都向Eureka等级自己提供服务的元数据,包括服务的IP地址、端口号、版本号、通信协议等
- Eureka将各个服务维护在了一个服务清单中(双层Map,第一层Key是服务名,第二层Key是实例名,value是服务地址加端口)
- 同时对服务维持心跳,剔除不可用的服务,Eureka集群个节点相互注册每个实例中都有一样的服务清单
- 发现
- Eureka注册的服务之间调用不需要指定服务地址,而是通过服务名向注册中心咨询,并获取所有服务实例清单(缓存到本地),然后实现服务的请求访问
- 注册
-
Ribbon 负载均衡
-
Feign 简化调用
- 基于Feign动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求
-
Hystrix 服务熔断和降级
-
Zuul 转发请求给对应服务
SpringCloud提打工了构建微服务系统所需要的一组通用考法模式以及一系列快速实现这些开发模式的工具
通常说的SpringCloud是SpringCloud Netflix,同SpringCloud Alibaba一样都是SpringCloud这一系列开发模式的具体实现
19、分布式事务如何处理,怎么保证事务一致性
- 分布式事务
- 将不同节点上的事务操作,提供原子性保证.同时保证成功或同时失败
- 分布式事务的第一个要点就是要在原本没有直接关联的事务之间建立联系
- HTTP连接: 最大努力通知 + 事后补偿
- MQ: 事务消息通知
- Redis (opKey): 定制出分布式事务机制
- Seate: 通过TC在多个事务之间建立联系
- 两阶段 AT XA 锁资源
- 三阶段 TCC 在两阶段的基础上增加一个准备阶段.在准备阶段是不锁资源的
- SAGA 类似于熔断 业务自己实现正向操作和补偿操作的逻辑 不需要锁资源
20、Spring框架中Bean的创建过程是怎样的
-
首先,简单来说,Spring框架中的Bean经过四个阶段: 实例化 -> 属性赋值 -> 初始化 -> 销毁
-
具体来说
- 实例化 new xxx() BeanDefinition对象保存
- 当客户端向容器申请一个Bean时
- 当容器在初始化一个Bean时发现还需要依赖另一个Bean时
- 设置对象属性
- Spring通过BeanDefinition找到对象依赖的其他对象,并将这些对象赋予当前对象
- 处理Aware接口
- Spring会检测对象是否实现了xxxAware接口,如果实现了,就会调用对应的方法
- BeanNameAware 设置bean的名字
- BeanClassLoaderAware 设置自定义扩展类加载器
- BeanFactoryAware 扩展BeanFactory
- …
- Spring会检测对象是否实现了xxxAware接口,如果实现了,就会调用对应的方法
- BeanProstProcessor前置处理
- 调用BeanPostProcessor的postProcessBeforeInitalization方法
- InitalizingBean
- Spring检测对象如果实现了这个接口,就会执行他的afterPropertiesSet()方法,定制初始化逻辑
- init-method
- 如果Spring发现Bean配置了这个属性,就会调用它的配置方法执行初始化逻辑
- @PostConstruct(xxx)或者
- 如果Spring发现Bean配置了这个属性,就会调用它的配置方法执行初始化逻辑
- BeanPostProcessor后置处理
- 调用BeanPostProcessor的postProcessAfterInitalization方法
到这里,这个Bean的创建过程就完成了,Bean就可以正常使用了
- DisposableBean
- 当Bean实现了这个接口,在对象销毁前就会调用destory()方法
- destory-method
- 或者@PreDestory(xxx )
- 实例化 new xxx() BeanDefinition对象保存
21、Spring如何处理循环依赖问题
-
循环依赖
- 先有蛋还是先有鸡
-
解决(三级缓存)
-
@Lazy 延迟加载
-
三级缓存
-
一级缓存 缓存最终的单例池对象
-
二级缓存(没有属性值的对象) 缓存初始化的对象
-
对于对象之间的普通引用,二级缓存可以解决
-
三级缓存 缓存对象的ObjectFactory
- 如果引用的对象配置了AOP,那在单例池最终就会需要注入动态代理对象,而不是原对象,而生成动态代理是要在初始化完成之后才开始的.于是Spring增加三级缓存,保存所有对象的动态代理配置信息.在发现有循环依赖时,将这个对象的动态代理信息获取出来,提前进行AOP,生成动态代理
-
22、SpringMVC中的控制器是不是单例模式?如果是,如何保证线程安全
- 是单例模式,单例模式下就会有线程安全问题
- Spring中保证线程安全的方法
- 将scop设置为非singleton (并发量大的时候会有非常多对象)
- 将控制器设计为无状态模式(最好的办法)
- 在控制器中不要携带数据,但是可以引用无状态的service和dao