前言
共25
题。
项目中为什么选择SpringBoot?
SpringBoot
简化了Spring
,开发效率更高。
它的主要优点如下:
-
版本锁定:在父工程中进行了大量常见依赖的版本锁定,我们就不用去找了;
-
起步依赖 :将依赖进行组装,并且允许程序员以
starter
的方式进行引入; -
默认配置:实现了大量依赖框架的默认配置项,不需要自己配置;
-
内置
Tomcat
。
缺点:SpringBoot
可能会装载大量用不到的对象,会浪费资源空间。
SpringBoot的核心注解是哪个?
SpringBoot
的核心注解是@SpringBootApplication
(自动装配),将其标记在启动类上,表示这是一个SpringBoot
应用。
该注解主要包含了以下3
个注解:
@SpringBootConfiguration
:实现配置文件的功能;@EnableAutoConfiguration
:打开或关闭某个自动配置;@ComponentScan
:默认扫描启动类所在的包。
SpringBoot中的starter是干什么的?
每个starter
都可以为我们提供某个服务场景所需要的一系列依赖。在导入starter
之后,SpringBoot
主要帮我们完成了两件事情:
-
相关组件的自动导入;
-
相关组件的自动配置。
SpringBoot可以有哪些方式加载配置?
常见的有三种:
-
配置文件 :比如
properties
、yaml
、yml
(常用); -
系统环境变量:但不推荐;
-
命令行参数:一般用于临时修改配置。
bootstrap.yml和application.yml有何区别?
都是SpringBoot
支持的核心配置文件,区别在于:
-
boostrap
比applicaton
优先加载,在应用程序上下文的引导阶段生效,且里面的属性不能被覆盖。一般来说我们在SpringCloud Config
中会用到它(Nacos
),一般用于多环境配置; -
application
用于SpringBoot
项目的自动化配置,一般来说我们会将自己项目的业务配置项写在这里面。
SpringBoot读取配置的方式有几种?
常见的有两种:
-
使用
@Value
配合EL
表达式(@Value("${name}")
) 直接注入对应的值; -
使用
@ConfigurationProperties(prefix=" ")
把对应的值绑定到一个配置对象,然后将配置对象注入到需要的地方。
推荐使用使用第二种方式,在配置比较多的情况下,操作简单,可读性好。
SpringBoot项目如何热部署?
使用Spring Boot
的开发工具(DevTools
)模块实现。当开发人员更改文件后,该模块就会自动部署修改到服务器并自动重启服务器。
SpringBoot项目如何实现方法的异步调用?
异步调用指的是两个方法a
与b
,a
调用b
的时候,不用等b
执行完毕就可以继续向下执行,一般用在a
方法不需要使用b
方法返回结果的场景,可提高运行效率。
在SpringBoot
只需要做两个操作就可以实现异步调用:
-
在启动类上添加
@EnableAsync
注解,开启异步调用支持; -
在被调用的方法上添加
@Async
注解。
SpringBoot中如何实现定时任务?
主要有两种方式:
-
使用第三方框架
Quartz
; -
使用
SpringTask
:主要是通过@Scheduled
注解来实现定时任务触发。
@Scheduled
注解主要属性如下:
fixedRate
:按一定的频率执行任务,参数类型为long
,单位ms
;fixedDelay
:上一次任务执行完后多久再执行,参数类型为long
,单位ms
;initialDelay
:延迟多久再第一次执行任务,参数类型为long
,单位ms
;cron
:使用cron
表达式指定任务在特定时间执行。
集群部署用
xxl-job
来实现定时任务。涉及到集群部署,SpringTask
定时任务不支持,会造成同一个任务重复执行多份,Redis
分布式锁需要手动修改比较麻烦。xxl-job
可以在后台随时调整时间规则,统一管理所有定时任务。
cron表达式?
cron
表达式其实就是一个字符串,通过cron
表达式可以定义任务的触发时间。SpringTask
支持的cron
表达式分为6
个域,由空格分隔开,每个域代表一个含义:秒 分 时 日 月 周。每个域都支持精准数值的写法,也支持一些具有特殊意义的字符,主要的有:
(1) *
:表示任意
(2) ?
:表示忽略,只能用在日和周两个域
(3) -
:表示区间,
(4) /
:表示起始时间开始触发,然后每隔固定时间触发一次
(5) ,
:表示列出枚举值,例如在分域使用5,20
则意味着在5
和20
分触发一次
(6) #
: 用于确定每个月第几个星期几
SpringBoot中如何解决跨域问题?
跨域问题指的是,前后端出现域名、端口、协议任意一个不同,都属于跨域。
解决方法 :添加一个配置类,实现WebMvcConfigurer
接口,然后重写addCorsMappings
方法即可,最后都是可以采用跨域资源共享CORS
来解决跨域问题。
如何理解拦截器?
拦截器是Spring
提供的一种拦截机制,实现对指定请求路径进行拦截:
-
单体项目中拦截器校验
token
,拦截用户信息存入ThreadLocal
; -
微服务在网关校验,获取用户
ID
放到请求头中传入后面微服务,微服务中拦截器拦截请求获取用户信息,将其放入ThreadLocal
中。
自定义一个拦截器,需要实现
HandlerInterceptor
接口,并重写3
个方法:
preHandle
: 这个方法在Controller
处理请求之前被调用,通过方法的返回值可以确定是否放行请求;postHandle
:这个方法在Controller
处理请求之后被调用;afterCompletion
:在整个请求结束之后被调用,此方法主要用于进行资源清理。
拦截器和过滤器的区别是什么?
都可以实现请求的拦截处理,不同点有4
个:
-
技术栈所属 不同:过滤器属于
JavaWeb
技术,依赖Servlet
容器;而拦截器是属于Spring
的技术; -
实现原理 不同:拦截器是基于
Java
的反射机制,而过滤器是基于函数回调; -
拦截范围 不同:过滤器可以拦截所有请求,而拦截器主要是针对发往
controller
请求; -
拦截位置不同:过滤器在前端控制器前拦截,而拦截器在前端控制器后拦截。
SpringBoot中properties文件如何配置文件上传上限?
ini
# 设置单个文件上传大小限制为10MB
spring.servlet.multipart.max-file-size=10MB
# 设置总上传文件大小限制为20MB
spring.servlet.multipart.max-request-size=20MB
SpringBoot如何实现缓存?
SpringCache
是Spring
提供的一个缓存框架,它可以通过简单的注解实现缓存的操作,常用的注解有下面几个:
-
@EnableCaching
: 开启基于注解的缓存; -
@CachePut
: 一般用在查询方法上,表示将方法的返回值放到缓存中; -
@Cacheable
: 一般用在查询方法上,表示在方法执行前先查看缓存中是否有数据,如果有直接返回;如果没有,再调用方法体查询数据并将返回结果放到缓存中;他有两个关键属性:value
: 缓存的名称,每个缓存名称下面可以有多个key
key
: 缓存的key
,支持Spring
的表达式语言SPEL
语法
-
@CacheEvict
: 一般用在增删改方法上 ,用于清理指定缓存,可以根据key
清理,也可以清理整个value
下的缓存。
SpringCache
还有一个优点:就是可以随意切换底层的缓存软件,比如:Redis
、内存等等。
项目中是如何进行异常处理的?
项目中是使用全局异常处理器来实现异常处理的,核心是两个注解:
- @RestControllerAdvice:标注在类上,声明该类是处理异常的类;
- @ExceptionHandler:标注在方法上,声明该方法是处理什么异常。
在全局异常处理器中一般定义三种异常:
-
指定异常 :指定异常指的是用户操作产生的与程序设计相关的异常,比如字段重复异常、
Validation
校验异常等等,这类异常捕获之后,我们会根据异常的消息提示,给前端一个确定的返回结果; -
业务异常:业务异常是由于用户不正当操作产生的与业务相关的的异常,这种异常往往需要我们自定义,指定异常提示信息,然后在程序的相关位置手动抛出。然后异常处理器捕获之后,直接将异常提示消息返回给前端;
-
异常时兜底异常:此处主要捕获的是不属于上面两种异常的异常,一般是一些程序员代码不够严谨引发的运行时异常,对于这些异常,我们处理方案是首先要把错误记录到日志系统中,然后给前端一个类似于服务器开小差了之类的统一提示。
项目中是如何存储文件的?
主要有三类存储方式:
-
第一种是直接将文件保存到服务到硬盘,这种方式操作方便,但是扩容困难,而且安全保障不高,现在基本不再使用;
-
分布式文件存储系统:如果文件是隐私性比较高,建议使用自己搭建的分布式文件存储系统,安全性也比较高;
-
第三方服务:如果文件隐私性不高,可以考虑使用第三方服务,比如阿里云或者七牛云。
项目中是如何进行参数校验的?
项目中的参数校验是使用validation
来实现的,它有一些特定的注解,这些注解主要标注在请求参数或者是参数对象对应类的属性上,每个注解都有自己的校验规则。
如果我们输入的请求参数不符合对应的校验规则,系统就会抛出异常,此时我们只需要在全局异常处理器中捕获异常,然后给前端提示即可。
常用的注解有下面这些:
-
@Null
:可以标注在任意类型元素上,被标注的元素必须为null
-
@NotEmpty
:可以标注在字符串,集合,数组,map
上,被标注的元素必须不能为null
,也不能是空串 -
@Range
:标注在数值类型上,数值的大小必须在指定的范围内,对于null
无效 -
@Digits(integer(数值的位数) =3 , fraction(小数的位数)=2)
,被注释的元素必须是一个数字,其值必须在可接受的范围内 -
@size(min=,max=)
:可标注在字符串,数组,集合,map
用于控制长度 -
@Email
:邮箱 -
@URL
:合法的地址
如何理解分组校验?
比如我们在新增和修改一个用户对象时,都会接收User
对象作为请求参数,但是新增要求对象的id
为空,而修改则要求id
字段不能为空。这个时候就需要使用到分组校验。分组校验其实就是定义多套校验规则,对于指定的功能,我们按照要求指定它使用哪套规则即可。
SpringBoot自动装配的过程是可以被干预的吗?
可以的。在实践之中,如果我想要排除一个自动装配类,就会利用@SpringBootApplication
注解的exclude
属性,比如:exclude={DataSourceAutoConfiguration.class}
,即可。
或者在yml
配置文件中使用spring.autoconfigure.exclude=DataSourceAutoConfiguration.class
来排除自动配置。
比如SpringBoot内置tomcat,现在我想使用netty,怎么办?
SpringBoot
内置tomcat
作为web
服务器,如果想要更换为netty
,需要如下步骤:
- 更换依赖,在
pom.xml
中引入netty
,然后在spring-boot-starter-web
依赖除移除tomcat
。 - 如果有需要的话,可以创建一个配置类修改
netty
配置,然后启动应用即可
@Controller和@Service交换会发生什么?
完整问题:现有控制层类StudentController和业务层类StudentService,将这两个类的@Controller注解和@Service注解进行交换,应用程序还能正常启动吗,StudentController类还可以处理请求吗?
可以正常启动。因为@Controller
注解和@Service
注解都继承了@Component
注解,在Spring
容器眼里都是一样的,只是方便程序员区分。真正决定能否匹配url
、处理请求的是@RequestMapping
等注解。
SpringBoot中都有哪些场景会导致事务失效?
我知道的有6
种场景:
- 事务方法不是public修饰的 ,因为
Transaction
注解基于SpringAOP
的动态代理,而SpringAOP
的动态代理只适用于public
方法 - 当前类没有交给IOC容器管理
- 抛出异常类型不是运行时异常
- 方法执行失败时,抛出的异常被捕获,导致事务没有发现异常无法回滚
- 同一个类中有事务方法也有非事务方法,非事务方法调用事务方法,因为非事务方法没有
@transaction
注解,相当于直接调用普通方法 - 事务传播行为使用有误 :如一个事务方法
A
内部调用另一个事务方法B
,A
的传播机制是默认的,B
是必须新事务。进入A
时,A
使用默认事务,调用B
时,B
又创建了一个新事务,A
与B
不是一个事务了。当A
抛出异常时,A
就会回滚,因为B
与A
不是一个事务,就不会回滚。
如何自定义一个注解?
比如:
Java
public @interface MyAnnotation {
public String name();
int age();
String sex() default "女";
}
- 访问修饰符必须为
public
,不写默认为public
; - 该元素的类型只能是基本数据类型、
String
、Class
、枚举类型、注解类型以及一维数组; - 该元素的名称一般定义为名词,如果注解中只有一个元素,名字起为
value
最好; ()
不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;default
代表默认值,值必须定义的类型一致;- 如果没有默认值,代表后续使用注解时必须给该类型元素赋值。
什么是元注解?
配置注解需使用元注解,元注解是专门修饰注解的注解,常见的有@Target
、@Retention
、@Documented
和@Inherited
。
@Target
@Target
是专门用来限定某个自定义注解能够被应用在哪些Java
元素上面的,包含一个ElementType[] value
,ElementType
常见的有:
TYPE
:类、接口、枚举等METHOD
:方法CONSTRUCTOR
:构造方法
@Retention
@Retention
注解,用来修饰自定义注解的生命力。
- 如果一个注解被定义为
RetentionPolicy.SOURCE
,则它将被限定在Java
源文件中,那么这个注解既不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java
文件的人看到; - 如果一个注解被定义为
RetentionPolicy.CLASS
,则它将被编译到Class
文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVM
(Java
虚拟机)会忽略它,我们在运行期也不能读取到,是默认的; - 如果一个注解被定义为
RetentionPolicy.RUNTIME
,那么这个注解可以在运行期的加载阶段被加载到Class
对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发 中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME
。
@Documented
@Documented
注解,是被用来指定自定义注解是否能随着被定义的java
文件生成到JavaDoc
文档当中
@Inherited
@Inherited
注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类(继承关系)的声明部分也能自动拥有该注解。该注解只对@Target
被定义为ElementType.TYPE
的自定义注解起作用。
SpringBoot常用的starter?
spring-boot-starter-web
:用于构建web
应用spring-boot-starter-test
:用于测试spring-boot-starter-data-redis
:redis
相关spring-boot-starter-validation
:数据验证