Spring框架 - SpringBoot

前言

25题。

项目中为什么选择SpringBoot?

SpringBoot简化了Spring,开发效率更高。

它的主要优点如下:

  1. 版本锁定:在父工程中进行了大量常见依赖的版本锁定,我们就不用去找了;

  2. 起步依赖 :将依赖进行组装,并且允许程序员以starter的方式进行引入;

  3. 默认配置:实现了大量依赖框架的默认配置项,不需要自己配置;

  4. 内置 Tomcat

缺点:SpringBoot可能会装载大量用不到的对象,会浪费资源空间。

SpringBoot的核心注解是哪个?

SpringBoot的核心注解是@SpringBootApplication(自动装配),将其标记在启动类上,表示这是一个SpringBoot应用。

该注解主要包含了以下3个注解:

  1. @SpringBootConfiguration:实现配置文件的功能;
  2. @EnableAutoConfiguration:打开或关闭某个自动配置;
  3. @ComponentScan:默认扫描启动类所在的包。

SpringBoot中的starter是干什么的?

每个starter都可以为我们提供某个服务场景所需要的一系列依赖。在导入starter之后,SpringBoot主要帮我们完成了两件事情:

  1. 相关组件的自动导入;

  2. 相关组件的自动配置。

SpringBoot可以有哪些方式加载配置?

常见的有三种:

  1. 配置文件 :比如propertiesyamlyml(常用);

  2. 系统环境变量:但不推荐;

  3. 命令行参数:一般用于临时修改配置。

bootstrap.yml和application.yml有何区别?

都是SpringBoot支持的核心配置文件,区别在于:

  1. boostrapapplicaton优先加载,在应用程序上下文的引导阶段生效,且里面的属性不能被覆盖。一般来说我们在SpringCloud Config中会用到它(Nacos),一般用于多环境配置;

  2. application用于SpringBoot项目的自动化配置,一般来说我们会将自己项目的业务配置项写在这里面。

SpringBoot读取配置的方式有几种?

常见的有两种:

  1. 使用@Value配合EL表达式(@Value("${name}")) 直接注入对应的值;

  2. 使用@ConfigurationProperties(prefix=" ")把对应的值绑定到一个配置对象,然后将配置对象注入到需要的地方。

推荐使用使用第二种方式,在配置比较多的情况下,操作简单,可读性好。

SpringBoot项目如何热部署?

使用Spring Boot的开发工具(DevTools)模块实现。当开发人员更改文件后,该模块就会自动部署修改到服务器并自动重启服务器。

SpringBoot项目如何实现方法的异步调用?

异步调用指的是两个方法aba调用b的时候,不用等b执行完毕就可以继续向下执行,一般用在a方法不需要使用b方法返回结果的场景,可提高运行效率。

SpringBoot只需要做两个操作就可以实现异步调用:

  1. 在启动类上添加@EnableAsync注解,开启异步调用支持;

  2. 在被调用的方法上添加@Async注解。

SpringBoot中如何实现定时任务?

主要有两种方式:

  1. 使用第三方框架Quartz

  2. 使用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则意味着在520分触发一次

(6) #: 用于确定每个月第几个星期几

SpringBoot中如何解决跨域问题?

跨域问题指的是,前后端出现域名、端口、协议任意一个不同,都属于跨域。

解决方法 :添加一个配置类,实现WebMvcConfigurer接口,然后重写addCorsMappings方法即可,最后都是可以采用跨域资源共享CORS来解决跨域问题。

如何理解拦截器?

拦截器是Spring提供的一种拦截机制,实现对指定请求路径进行拦截:

  1. 单体项目中拦截器校验token,拦截用户信息存入ThreadLocal

  2. 微服务在网关校验,获取用户ID放到请求头中传入后面微服务,微服务中拦截器拦截请求获取用户信息,将其放入ThreadLocal中。

自定义一个拦截器,需要实现HandlerInterceptor接口,并重写3个方法:

  1. preHandle: 这个方法在Controller处理请求之前被调用,通过方法的返回值可以确定是否放行请求;
  2. postHandle:这个方法在Controller处理请求之后被调用;
  3. afterCompletion:在整个请求结束之后被调用,此方法主要用于进行资源清理。

拦截器和过滤器的区别是什么?

都可以实现请求的拦截处理,不同点有4个:

  1. 技术栈所属 不同:过滤器属于JavaWeb技术,依赖Servlet容器;而拦截器是属于Spring的技术;

  2. 实现原理 不同:拦截器是基于Java的反射机制,而过滤器是基于函数回调;

  3. 拦截范围 不同:过滤器可以拦截所有请求,而拦截器主要是针对发往controller请求;

  4. 拦截位置不同:过滤器在前端控制器前拦截,而拦截器在前端控制器后拦截。

SpringBoot中properties文件如何配置文件上传上限?

ini 复制代码
# 设置单个文件上传大小限制为10MB

spring.servlet.multipart.max-file-size=10MB

# 设置总上传文件大小限制为20MB

spring.servlet.multipart.max-request-size=20MB

SpringBoot如何实现缓存?

SpringCacheSpring提供的一个缓存框架,它可以通过简单的注解实现缓存的操作,常用的注解有下面几个:

  • @EnableCaching: 开启基于注解的缓存;

  • @CachePut: 一般用在查询方法上,表示将方法的返回值放到缓存中;

  • @Cacheable: 一般用在查询方法上,表示在方法执行前先查看缓存中是否有数据,如果有直接返回;如果没有,再调用方法体查询数据并将返回结果放到缓存中;他有两个关键属性:

    • value: 缓存的名称,每个缓存名称下面可以有多个key
    • key: 缓存的key,支持Spring的表达式语言SPEL语法
  • @CacheEvict: 一般用在增删改方法上 ,用于清理指定缓存,可以根据key清理,也可以清理整个value下的缓存。

SpringCache还有一个优点:就是可以随意切换底层的缓存软件,比如:Redis、内存等等。

项目中是如何进行异常处理的?

项目中是使用全局异常处理器来实现异常处理的,核心是两个注解:

  1. @RestControllerAdvice:标注在类上,声明该类是处理异常的类;
  2. @ExceptionHandler:标注在方法上,声明该方法是处理什么异常。

在全局异常处理器中一般定义三种异常:

  1. 指定异常 :指定异常指的是用户操作产生的与程序设计相关的异常,比如字段重复异常、Validation校验异常等等,这类异常捕获之后,我们会根据异常的消息提示,给前端一个确定的返回结果;

  2. 业务异常:业务异常是由于用户不正当操作产生的与业务相关的的异常,这种异常往往需要我们自定义,指定异常提示信息,然后在程序的相关位置手动抛出。然后异常处理器捕获之后,直接将异常提示消息返回给前端;

  3. 异常时兜底异常:此处主要捕获的是不属于上面两种异常的异常,一般是一些程序员代码不够严谨引发的运行时异常,对于这些异常,我们处理方案是首先要把错误记录到日志系统中,然后给前端一个类似于服务器开小差了之类的统一提示。

项目中是如何存储文件的?

主要有三类存储方式:

  1. 第一种是直接将文件保存到服务到硬盘,这种方式操作方便,但是扩容困难,而且安全保障不高,现在基本不再使用;

  2. 分布式文件存储系统:如果文件是隐私性比较高,建议使用自己搭建的分布式文件存储系统,安全性也比较高;

  3. 第三方服务:如果文件隐私性不高,可以考虑使用第三方服务,比如阿里云或者七牛云。

项目中是如何进行参数校验的?

项目中的参数校验是使用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种场景:

  1. 事务方法不是public修饰的 ,因为Transaction注解基于SpringAOP的动态代理,而SpringAOP的动态代理只适用于public方法
  2. 当前类没有交给IOC容器管理
  3. 抛出异常类型不是运行时异常
  4. 方法执行失败时,抛出的异常被捕获,导致事务没有发现异常无法回滚
  5. 同一个类中有事务方法也有非事务方法,非事务方法调用事务方法,因为非事务方法没有@transaction注解,相当于直接调用普通方法
  6. 事务传播行为使用有误 :如一个事务方法A内部调用另一个事务方法BA的传播机制是默认的,B是必须新事务。进入A时,A使用默认事务,调用B时,B又创建了一个新事务,AB不是一个事务了。当A抛出异常时,A就会回滚,因为BA不是一个事务,就不会回滚。

如何自定义一个注解?

比如:

Java 复制代码
public @interface MyAnnotation {
    public String name();
    int age();
    String sex() default "女";
}
  • 访问修饰符必须为public,不写默认为public
  • 该元素的类型只能是基本数据类型、StringClass、枚举类型、注解类型以及一维数组;
  • 该元素的名称一般定义为名词,如果注解中只有一个元素,名字起为value最好;
  • ()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
  • default代表默认值,值必须定义的类型一致;
  • 如果没有默认值,代表后续使用注解时必须给该类型元素赋值。

什么是元注解?

配置注解需使用元注解,元注解是专门修饰注解的注解,常见的有@Target@Retention@Documented@Inherited

@Target

@Target是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的,包含一个ElementType[] valueElementType常见的有:

  1. TYPE:类、接口、枚举等
  2. METHOD:方法
  3. CONSTRUCTOR:构造方法

@Retention

@Retention注解,用来修饰自定义注解的生命力。

  1. 如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解既不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到;
  2. 如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVMJava虚拟机)会忽略它,我们在运行期也不能读取到,是默认的;
  3. 如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发 中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME

@Documented

@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中

@Inherited

@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类(继承关系)的声明部分也能自动拥有该注解。该注解只对@Target被定义为ElementType.TYPE的自定义注解起作用。

SpringBoot常用的starter?

  1. spring-boot-starter-web:用于构建web应用
  2. spring-boot-starter-test:用于测试
  3. spring-boot-starter-data-redisredis相关
  4. spring-boot-starter-validation:数据验证
相关推荐
uhakadotcom18 分钟前
入门:如何使用Python预测广告点击率
算法·面试·github
uhakadotcom26 分钟前
实时语音转文字(RealtimeSTT)简介与应用
后端·算法·面试
uhakadotcom27 分钟前
使用 ONNX Runtime 进行深度学习模型推理和优化
算法·面试·github
江城开朗的豌豆34 分钟前
CSS篇:前端开发者必须掌握的CSS核心知识:选择器与优先级详解
前端·css·面试
和和和39 分钟前
关于webpack我们应该知道的知识
前端·javascript·面试
江城开朗的豌豆40 分钟前
CSS篇:前端布局新思路:左侧宽度智能调节,右侧自适应填充的5种实现
前端·css·面试
uhakadotcom40 分钟前
ContextGem:简化文档数据提取的利器
后端·面试·github
uhakadotcom42 分钟前
使用MCP服务器控制浏览器的AI代理
后端·面试·github