目录
[三、Spring MVC](#三、Spring MVC)
[2.SpringMVC 处理流程](#2.SpringMVC 处理流程)
[四、Spring Boot](#四、Spring Boot)
[1.Spring Boot相比于Sprig的优点](#1.Spring Boot相比于Sprig的优点)
[2.Spring Boot用了哪些设计模式](#2.Spring Boot用了哪些设计模式)
[3.Spring Boot的约定大于配置](#3.Spring Boot的约定大于配置)
[4.Spring Boot的自动装配原理是什么](#4.Spring Boot的自动装配原理是什么)
[5.Spring Boot怎么做到导入就直接使用的](#5.Spring Boot怎么做到导入就直接使用的)
[4.MyBatis中的#和的区别是什么](#和的区别是什么)
一、引言
本篇文章将介绍面试中关于Spring的一些常见问题。
二、Spring
1.Spring框架的特性
1.Ioc容器:Spring通过控制反转这样的思想实现了对象的创建和对象之间的依赖关系,用户只需要定义好Bean和依赖关系,Spring容器负责创建和组装这些对象。
2.AOP:面向切面编程,用户在业务逻辑的某个部分进行切面,在这个切面上引入另外的一段代码进行执行。例如:事务管理,安全控制。
3.事务管理:Spring提供了一套一致的事务管理接口,支持声明式事务和编程式事务。
4.MVC框架:Spring MVC是一个基于Servlet API构建的Web框架,支持灵活的URL到页面的映射。
2.介绍一下IOC和AOP
IOC:控制反转,主要作用是解决了创建对象和依赖管理的高耦合,主要是通过依赖注入(DI)实现的,使用了IOC之后,我们不用再去使用new来创建对象了,而是通过IOC容器实例化对象。简单的说就是需要什么对象,从这个容器里面取就可以了。
AOP:面向切面编程,将那些与业务代码无关的,但是许多业务模块公用的部分封装起来,降低代码耦合度,基于动态代理的思想实现。
3.IOC和AOP都是如何实现的
IOC:
1.反射:通过Java的反射机制动态地加载类,实例对象,调用方法等操作,在程序运行的时候,检查类,方法,属性等信息,根据想要的内容动态地进行选择或组装
2.依赖注入:通过一些方式(构造方法注入,Setter注入,注解字段注入等)将组件之间的依赖属性管理好,将组件之间的依赖关系定义在注解中或者配置文件中
3.设计模式--工厂模式:采用工厂模式的方式来对对象的创建和使用进行管理,通过工厂模式对Bean的实例化,Bean生命周期进行管理
4.容器实现:通常使用BeanFactory或者ApplicationContext来管理Bean。BeanFactory是Spring容器的最基础实现,ApplicationContext是BeanFactory的扩展,提供了更多的功能
AOP:
动态代理的思想:AOP需要在不修改目标方法源码的前提下,再添加另外一套逻辑,这就要求程序在运行的时候,能够动态生成 一个"增强版"的目标对象,这个对象既保留了原方法的业务逻辑,又自动添加了要插入的逻辑。
Spring支持的两种动态代理:
1.基于JDK实现的动态代理(基于接口的代理):使用Proxy类和InvocationHandler接口实现。这种方式需要代理的类实现一个或多个接口。每一个动态代理类都必须实现InvocationHandler这个接口,每个代理类的实例都会关联一个handler,当代理对象调用一个方法的时候,这个方法会被转发为invocationHandler接口的invoke()方法来调用。
2.基于CGLIB的动态代理(基于类的代理):当被代理的类没有实现接口时,Spring会生成一个CGLIB库生成一个被代理的子类。
4.怎么实现依赖注入
1.构造方法注入:保证对象初始化时依赖已就绪
2.Setter方法注入:通过Setter方法设置依赖,灵活性高,依赖可能未完全初始化
3.注解注入:不利于进行扩展
5.为什么AOP不用静态代理
要实现是可以进行实现的,只不过由于
1.代码爆炸,有100个Service就得写100个,不好维护
2.僵化:一旦有地方更改了方法名,全部都得更改
3.无法动态筛选:不能指定要为哪个注解加一些逻辑
6.介绍一下反射
反射是指程序在运行的时候,对于任意一个类,都能够获取到这个类的属性和方法,对于任意一个对象,都能够取调用它的属性和方法。
特性:
1.运行时类信息访问
2.动态对象创建:通过Class类的newInstance()方法或Constructor()方法创建动态地创建对象
3.动态方法的调用:包括私有方法,通过Method类的invoke()方法实现
4.访问和修改字段值:在程序运行的期间,即使是私有的,也能通过Field类的get()和set()方法完成对对象的字段修改
7.Spring如何解决循环依赖问题
Spring只解决了通过Setter方法进行依赖注入且Bean是单例模式下的循环依赖。
通过三级缓存实现
1.一级缓存:存放的是完全初始化好的、可用的Bean实例,getBean()返回的是这里面的Bean(初始化好,依赖已注入,初始化方法已执行)
2.二级缓存:存放的是提前暴露的Bean的原始对象引用,专门用来处理循环依赖,此时的Bean只是被实例化了,但是还没有进行初始化,初始化方法也没有得到执行。
3.三级缓存:存放的是Bean的ObjectFactory工厂对象。解决循环依赖的关键:Bean被实例化之后,Spring会创建一个ObjectFactory并将其放入三级缓存中,当检测循环依赖需要注入一个尚未完全初始化的Bean时,就会调用这个工厂来获取早期引用
Spring通过三级缓存和提前暴露未完全初始化的对象引用来解决其中一个循环依赖问题
举例:A依赖B,B也依赖于A(A和B都必须是单例的Bean)
1.Spring会先创建A的构造函数进行实例化,之后Spring会将一个特殊的工厂对象存入到第三级缓存中
2.填充A的属性发现其依赖B,于是就按照1的方法将B的工厂对象也存入到第三级缓存中
3.填充B的时候又发现其依赖于A,在一级缓存,二级缓存中都没找到A,只有第三级缓存中定位到了A的工厂对象,调用工厂的getObject()方法之后,得到A的早期引用,放入到二级缓存,并清理A的第三级缓存
4.B获得了所有的依赖后,执行初始化方法,之后将B对象放入到一级缓存,清除其在二、三级缓存的临时条目
5.回溯完成A的构建
B创建完毕之后,为A进行属性注入,从二级缓存中获得Bean,并执行初始化方法,填充到一级缓存中,清除在二级缓存的临时条目
三级缓存工厂:负责实例化后立刻暴露对象生成能力,兼顾AOP代理的提前生成
二级缓存:临时存储已确定的早期引用,避免重复生成代理
一级缓存:最终交付完整的Bean
不能使用二级缓存:
因为要正确处理被AOP代理的Bean,第三季缓存不是直接缓存对象,而是一个工厂去判断是否需要被代理,如果要,就生成代理对象放入二级缓存,如果不要直接返回原始对象。
三级缓存的本质是:按需延迟生成正确引用。二级缓存缺乏这种动态决策能力。
8.Spring常用注解
1.MVC相关:RestController,RequestMapping,RequestBody,
2.Service层:bean,service,mapper,component,resitory,configuration,autowired
3.AOP相关:aspectJ(找切面)before,after,around,afterthrowing,
4.事务相关:Transacational
9.Spring事务什么情况会失效
1.未捕获异常:一个事务方法中发生了未捕获的异常,并且异常未被处理,那么事务会失效,所有数据库操作都会回滚。
2.某些异常不会被事务检查
3.多个事务之间存在嵌套,且事务传播属性配置不正确也会导致事务失效
4.跨方法调用事务问题:一个事务方法内部调用另外一个方法,而这个被调用方法没有事务注解,那么可能导致外部的这层事务失效
5.在非public修饰的方法中使用
10.Bean的生命周期
1.实例化:SpringFactory调用getBean方法实例化Bean
2.设置属性:通过构造方法,Setter方法,注解等方式将属性注入
三、初始化:3.检测一些Aware接口(BeanNameAware,BeanFactoryAware,ApplicationContextAware)并设置相关依赖
4.BeanPostProcessor接口的前置处理:调用接口的postBeforeInitialization()方法
5.是否实现了Initialization接口
6.是否配置了自定义的init-method声明了初始化方法
7.BeanPostProcessor的后置处理,调用postAfterInitialization()方法
8.注册Destruction相关回调接口
四、销毁
9.是否实现了DisposableBean接口,调用其destroy()方法。
10.是否配置了自定义的destroy-method声明销毁方法
11.Bean是单例的吗
Spring中Bean默认是单例的,但是可以通过设置scope属性为prototype来设计成多例。
注意:Spring只会帮我们管理单例的Bean,而不会管理prototype的Bean,多例的Bean,Spring创建好了之后就交给了用户。
|-------|------------------|-------------------------|
| 阶段 | 单例 | 非单例 |
| 创建时机 | 容器启动时创建 | 每次请求时创建实例 |
| 初始化流程 | 完整执行生命周期流程 | 每次创建实例时才执行生命周期流程(只到初始化) |
| 销毁时机 | 容器关闭销毁 | 用户进行手动释放资源 |
| 内存占用 | 内存占用小,但需考虑线程安全问题 | 内存开销较大,需手动管理资源释放 |
| 使用场景 | 无状态服务 | 有状态对象(用户会话,临时计算对象) |
三、Spring MVC
1.MVC是什么,介绍一下
M(Model):模型,代表一个存取数据的对象,对对象进行一些核心数据处理
V(View):视图,为用户提供使用界面,与用户直接进行交互
C(Controller):用于将用户的请求转发给对应的Model进行处理,并根据用户的计算结果向用户提供相应响应
2.SpringMVC 处理流程
这里比较复杂,小编在这里贴一张图,大家自行观看吧!
1.用户发送一个request请求,到达dispatchServlet前置处理器
2.前置处理器从HandlerMapping处理器映射器中查找对应的处理器
3.处理器映射器返回一条执行链
4.前置处理器根据执行链请求HandlerAdapter适配器执行
5.适配器调用ControllerHandler处理请求
6.ControllerHandler执行完毕之后返回model and view给适配器
7.适配器返回model and view给前置处理器
8.前置处理器将这个发给view resolver解析
9.view resolver返回view
10.将view返回给用户
四、Spring Boot
1.Spring Boot相比于Sprig的优点
1.Spring Boot提供了自动化配置:根据项目的依赖关系和一些规则来配置应用程序,很多配置都自动完成了,开发者只需要关注业务逻辑
2.Spring Boot提供了快速启动器:通过引入不同的starter,快速集成了常用的框架和库(数据库,消息队列,Web开发等)
3.Spring Boot默认集成了多种内嵌服务器:无需额外配置,即可将应用打包成JAR包直接部署运行。
2.Spring Boot用了哪些设计模式
1.代理模式:AOP
2.策略模式:JDK和CGLIB两种代理模式,根据类有没有实现接口自行去进行选择
3.装饰器模式:Spring用Transaction解决缓存和数据库事务问题对事务的支持
4.单例模式:Spring Boot中的Bean默认是单例的
5.工厂模式
6.适配器模式:MVC中体现,HandlerAdapter
3.Spring Boot的约定大于配置
1.自动化配置:Spring Boot会根据项目自动配置相应的依赖和环境,例如添加了Spring-Boot-starter-web这个依赖就会自动内嵌Tomcat和Spring MVC,无需手动编写XML文件
2.默认配置:存在许多默认配置:连接数据库,设置Web服务器等
3.约定的项目结构:Spring Boot提供了一套项目结构,主应用程序类置于根包,控制器类,服务类置于子包
4.Spring Boot的自动装配原理是什么
关键是@EnableAutoConfiguration注解实现,开发者引入了一些依赖之后,这个注解会根据这些依赖自动进行一些配置程序上下文和功能
Spring Boot在启动的时候回去扫描外部jar包下的spring.factories文件,将文件的配置信息加载到Spring容器中
简单来说:自动装配就是通过一些注解或者配置文件通过Spring Boot的帮助下开启和配置各种功能。
5.Spring Boot怎么做到导入就直接使用的
1.起步依赖:在创建Spring Boot项目的时候就会直接通过maven或gradle依赖,创建的时候就构建了一些操作
2.自动配置:根据EnableAutoConfiguration注解对文件进行扫描,获取其中的配置程序的上下文和功能
3.条件注解:只有满足相应的条件才会创建相应的Bean,常见的注解有:ConditionalOnClass等
6.拦截器和过滤器的区别
过滤器:对要进入Servlet容器的请求和响应进行预处理和后处理,通过实现javax.servlet.Filter接口,重写其中的init,doFilter,destroy方法来执行相应逻辑,按照配置的顺序依次经过各个过滤器,然后才会到达servlet,返回结果也是同理,也得经过各个过滤器
拦截器:对控制器的方法进行拦截,通过实现HandlerInterceptor接口的preHandler,postHandler的方法来完成相应的逻辑,当请求到达拦截器,会经过拦截器的preHandle方法,返回true才能继续执行后续的拦截器或者方法。控制器方法执行完之后,会调用拦截器的postHandle方法,最后在请求处理完成后,调用拦截器的afterCompletion方法。
区别:
所属规范不同: 一个是Java Servlet规范的一部分,一个是Spring框架提供的机制
执行顺序:一个是在进入执行器之前,一个是进入了执行器,在调用执行器方法前后
使用范围:一个是对所有请求,一个是对Spring MVC控制器的请求进行拦截
功能特性:过滤器主要用于对请求进行预处理,如字符编码,请求日志记录等
拦截器主要用于控制器方法的执行,如权限验证,性能监控等
五、MyBatis
1.与传统的JDBC相比,MyBatis的优势在哪里
1.基于SQL语句编程,比较灵活,同时又与应用程序和数据库分离,极大地解耦合了,便于统一管理,同时支持写动态的SQL
2.减少了代码量,不用一直去手动地对数据库进行开关
3.本质还是JDBC来连接数据库,所以JDBC支持的,MyBatis都支持
4.提供映射标签,支持对象和数据库的ORM字段关系映射,提供对象关系映射标签
2.MyBatis的特殊功能
1.插件扩展机制:可编写插件去拦截SQL执行过程,实现分页、性能监控、SQL改写的逻辑
2.与Spring生态无缝衔接:通过@MapperScan快速扫描Mapper接口,配置简洁高效
3.如何使用MyBatis
1.配置MyBatis:配置MyBatis的数据源,SQL映射文件等
2.创建实体类:用于映射数据库表的实体类
3.编写SQL映射文件:创建XML文件,定义SQL语句和映射关系
4.编写DAO接口:创建DAO接口,定义数据库操作的方法
5.编写具体的查询语句:编写具体语句
4.MyBatis中的#和$的区别是什么
#会预编译SQL语句,将#{}替换成?,执行SQL的时候给预编译SQL中的?赋值,可以有效地防止SQL注入这样的安全问题
$是直接将参数拼接进SQL语句中,参数没有得到校验,过滤,可能会出现SQL注入的问题
5.MyBatisPlus和MyBatis的区别
1.代码生成器:根据表结构自动生成实体类
2.CRUD操作:提供了快捷方法
3.通用方法封装:封装了许多方法:条件构造器,排序,分页查询等
4.分页插件:内置了许多插件,用来实现各项功能
5.注解的支持
六、总结
本篇文章简单介绍了一些面试中常见的关于Spring的八股,内容肯定不是很完善,也可能存在错误,欢迎大家指正!最后,强调以上大部分内容都来自于:小林coding:Spring面试题 | 小林coding,大家想看更完整的,可以去小林coding官网阅读,谢谢观看!