SpringBoot
SpringBoot
Spring Boot 的自动配置是如何实现的?
使用Springboot时,我们需要先引入对应starter,Springboot启动时会自动加载相关依赖,配置相应的初始化参数;
自动装配的流程:SpringBoot通过**@EnableAutoConfiguration注解开启自动配置**,Spring Boot会自动根据项目中的依赖配置相关的Bean,例如,如果项目中引入了spring-boot-starter-web依赖
它使用 Spring 框架提供的 @Import 注解通过AutoConfigurationImportSelector类(选择器)给容器中加载所有自动配置类的jar包的META-INF/spring.factorie文件下面指定的自动配置类
如果是条件配置,满足@Conditional注解的条件时,就实例化该AutoConfiguration类中定义的Bean,并注入Spring容器,即可完成Springboot的自动装配
比如spring.factories定义的AutoConfiguration类是MyConditionalAutoConfiguration
1 |
|
什么是spring.factories?
通常将 spring.factories
文件放置在项目的 META-INF
目录下。在大多数情况下,这个目录位于 src/main/resources
目录下。
spring.factories 文件可以将 spring-boot 项目包以外的 bean(即在 pom 文件中添加依赖中的 bean)注册到 spring-boot 项目的 spring 容器。
由于@ComponentScan 注解只能扫描 spring-boot 项目包内的 bean 并注册到 spring 容器中,因此需要@EnableAutoConfiguration 注解来注册项目包外的bean。而 spring.factories 文件,则是用来记录项目包外需要注册的bean类名
@Configuration
的作用是什么
使用 @Configuration
注解进行标记,然后在类上添加 @ConditionalOnProperty
注解可以指定条件。例如:
1 |
|
只有当配置文件中的 myapp.enabled
属性值为 true
时,才会加载这个自动配置类。
条件注解有哪些?
- **
@ConditionalOnBean
**:当容器中存在指定的 Bean 时,才创建当前的 Bean。 - **
@ConditionalOnMissingBean
**:当容器中不存在指定的 Bean 时,才创建当前的 Bean。 - **
@ConditionalOnClass
**:当类路径下存在指定的类时,才创建当前的 Bean。 - **
@ConditionalOnMissingClass
**:当类路径下不存在指定的类时,才创建当前的 Bean。 - **
@ConditionalOnProperty
**:当指定的属性值符合条件时,才创建当前的 Bean。可以指定属性的键和值。 - **
@ConditionalOnResource
**:当类路径下存在指定的资源文件时,才创建当前的 Bean。 - **
@ConditionalOnWebApplication
**:根据应用程序的类型来决定是否创建当前的 Bean。例如,可以指定只有当应用程序是 Web 应用程序时才创建。 - **
@ConditionalOnNotWebApplication
**:与@ConditionalOnWebApplication
相反,当应用程序不是 Web 应用程序时才创建 Bean。 - **
@ConditionalOnExpression
**:当 SpEL 表达式为 true 时,才创建当前的 Bean。 - **
@ConditionalOnJava
**:当运行在特定版本的 Java 环境下时,才创建当前的 Bean。 - **
@ConditionalOnSingleCandidate
**:当容器中只有一个指定的 Bean,或者这些 Bean 可以被自动装配时,才创建当前的 Bean。 - **
@ConditionalOnCloudPlatform
**:当应用程序运行在特定的云平台上时,才创建当前的 Bean。 - **
@ConditionalOnRibbon
**:当 Ribbon 客户端存在时,才创建当前的 Bean。 - **
@ConditionalOnEureka
**:当 Eureka 客户端存在时,才创建当前的 Bean。 - **
@ConditionalOnZuul
**:当 Zuul 客户端存在时,才创建当前的 Bean。
@SpringBootApplication注解工作流程
以下是Spring Boot启动类注解的工作流程:
- 初始化SpringApplication:在Spring Boot应用启动时,首先会进行SpringApplication的初始化。这个过程包括设置源(即设置Spring容器启动时依赖的初始配置类),设置WEB应用程序类型(例如SERVLET或REACTIVE),以及配置一些基本的环境变量、资源、构造器和监听器。
- 加载配置环境:在初始化之后,Spring Boot会根据项目的配置(如application.properties或application.yml文件)加载配置环境。这些配置信息将被用来设置Spring应用的各种参数,如数据库连接、服务器端口等。
- 创建上下文环境:接着,Spring Boot会创建应用上下文环境。在这个过程中,Spring Boot会根据项目中定义的Beans和自动配置项来构建Spring容器。
- 自动化配置:Spring Boot的一个核心特性是自动化配置,它会根据项目的依赖和配置自动配置Spring应用。例如,如果项目中包含了spring-boot-starter-web依赖,Spring Boot会自动配置嵌入式的Tomcat服务器和Spring MVC相关的组件。
- 启动应用:最后,通过调用
SpringApplication.run(SpringBoot.class, args)
方法,Spring Boot会启动内嵌的Web服务器(默认为Tomcat),并开始监听指定的端口,等待接收请求。
Spring有哪些设计模式?
单例模式
在我们的系统中,有一些对象其实我们只需要一个,比如说:线程池、缓存、对话框、注册表、日志对象、充当打印机、显卡等设备驱动程序的对象。事实上,这一类对象只能有一个实例,如果制造出多个实例就可能会导致一些问题的产生,比如:程序的行为异常、资源使用过量、或者不一致性的结果。
使用单例模式的好处 :
- 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
- 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
Spring 中 bean 的默认作用域就是 singleton(单例)的
工厂模式
Spring 使用工厂模式可以通过 BeanFactory
或 ApplicationContext
创建 bean 对象。
两者对比:
BeanFactory
:延迟注入(使用到某个 bean 的时候才会注入),相比于ApplicationContext
来说会占用更少的内存,程序启动速度更快。ApplicationContext
:容器启动的时候,不管你用没用到,一次性创建所有 bean 。BeanFactory
仅提供了最基本的依赖注入支持,ApplicationContext
扩展了BeanFactory
,除了有BeanFactory
的功能还有额外更多功能,所以一般开发人员使用ApplicationContext
会更多。
ApplicationContext
的三个实现类:
ClassPathXmlApplication
:把上下文文件当成类路径资源。FileSystemXmlApplication
:从文件系统中的 XML 文件载入上下文定义信息。XmlWebApplicationContext
:从 Web 系统中的 XML 文件载入上下文定义信息。
1 |
|
代理模式
AOP(Aspect-Oriented Programming,面向切面编程) 能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy 去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理
适配器模式
适配器模式(Adapter Pattern) 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作。
Spring AOP 中的适配器
我们知道 Spring AOP 的实现是基于代理模式,但是 Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是AdvisorAdapter
。
Advice 常用的类型有:BeforeAdvice
(目标方法调用前,前置通知)、AfterAdvice
(目标方法调用后,后置通知)、AfterReturningAdvice
(目标方法执行结束后,return 之前)等等。每个类型 Advice(通知)都有对应的拦截器:MethodBeforeAdviceInterceptor
、AfterReturningAdviceInterceptor
、ThrowsAdviceInterceptor
等等。
Spring 预定义的通知要通过对应的适配器,适配成 MethodInterceptor
接口(方法拦截器)类型的对象(如:MethodBeforeAdviceAdapter
通过调用 getInterceptor
方法,将 MethodBeforeAdvice
适配成 MethodBeforeAdviceInterceptor
)。
Spring MVC 中的适配器模式
在 Spring MVC 中,DispatcherServlet
根据请求信息调用 HandlerMapping
,解析请求对应的 Handler
。解析到对应的 Handler
(也就是我们平常说的 Controller
控制器)后,开始由HandlerAdapter
适配器处理。HandlerAdapter
作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller
作为需要适配的类。
模板方法模式
Spring 中 JdbcTemplate
、HibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。一般情况下,我们都是使用继承的方式来实现模板模式,但是 Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。
装饰者模式
装饰者模式可以动态地给对象添加一些额外的属性或行为。相比于使用继承,装饰者模式更加灵活。简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个 Decorator 套在原有代码外面。其实在 JDK 中就有很多地方用到了装饰者模式,比如 InputStream
家族,InputStream
类下有 FileInputStream
(读取文件)、BufferedInputStream
(增加缓存,使读取文件速度大大提升)等子类都在不修改InputStream
代码的情况下扩展了它的功能。
Spring 中配置 DataSource 的时候,DataSource 可能是不同的数据库和数据源。我们能否根据客户的需求在少修改原有类的代码下动态切换不同的数据源?这个时候就要用到装饰者模式(这一点我自己还没太理解具体原理)。Spring 中用到的包装器模式在类名上含有 Wrapper
或者 Decorator
。这些类基本上都是动态地给一个对象添加一些额外的职责
SpringBoot常用注解有哪些
@SpringBootApplication
这里先单独拎出@SpringBootApplication
注解说一下,虽然我们一般不会主动去使用它。
Guide:这个注解是 Spring Boot 项目的基石,创建 SpringBoot 项目之后会默认在主类加上。
1 |
|
我们可以把 @SpringBootApplication
看作是 @SpringBootConfiguration
、@EnableAutoConfiguration
、@ComponentScan
注解的集合。
@EnableAutoconfiguration :启用SpringBoot的自动配置机制
@ComponentScan :扫描被 @Component(@Service,@controller )注解的 bean,注解默认会扫描该类所在的包下所有的类。
@SpringBootConfiguration: 组合了@Configuration注解,实现配置文件的功能
1 |
|
工作原理如下:
- 当Spring Boot应用启动时,会扫描主程序类上的@SpringBootApplication注解。
- 根据@SpringBootConfiguration注解,将当前类作为配置类。
- 根据@ComponentScan注解,扫描指定包下的类,并将其注册为Spring容器中的Bean。
- 根据@EnableAutoConfiguration注解,Spring Boot会自动根据项目中的依赖配置相关的Bean。例如,如果项目中引入了spring-boot-starter-web依赖,那么Spring Boot会自动配置Tomcat和Spring MVC等相关的Bean。
- Spring Boot会将所有配置好的Bean注入到Spring容器中,完成自动配置。
- 最后,Spring Boot会调用主程序类的main方法,启动应用。
@RestController
@RestController
注解是@Controller
和@ResponseBody
的合集,表示这是个控制器 bean,并且是将函数的返回值直接填入 HTTP 响应体中,是 REST 风格的控制器。
单独使用 @Controller
不加 @ResponseBody
的话一般是用在要返回一个视图的情况,这种情况属于比较传统的 Spring MVC 的应用,对应于前后端不分离的情况。@Controller
+@ResponseBody
返回 JSON 或 XML 形式数据
@RequestMapping
@RequestMapping
是 Spring MVC 中的一个注解,用于映射 HTTP 请求到控制器的处理方法。它可以应用于类级别或方法级别,提供灵活的配置选项来处理不同的请求路径、HTTP 方法、请求参数等。
1 |
|
@Scope
声明 Spring Bean 的作用域,使用方法:
1 |
|
四种常见的 Spring Bean 的作用域:
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
- session : 每一个 HTTP Session 会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
@Configuration
一般用来声明配置类,可以使用 @Component
注解替代,不过使用@Configuration
注解声明配置类更加语义化。
1 |
|
处理常见的 HTTP 请求类型
5 种常见的请求类型:
- GET:请求从服务器获取特定资源。举个例子:
GET /users
(获取所有学生) - POST:在服务器上创建一个新的资源。举个例子:
POST /users
(创建学生) - PUT:更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:
PUT /users/12
(更新编号为 12 的学生) - DELETE:从服务器删除特定的资源。举个例子:
DELETE /users/12
(删除编号为 12 的学生) - PATCH:更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少,这里就不举例子了。
GET 请求
1 |
|
1 |
|
POST 请求
1 |
|
关于@RequestBody
注解的使用,在下面的“前后端传值”这块会讲到。
1 |
|
PUT 请求
1 |
|
1 |
|
DELETE 请求
1 |
|
1 |
|
PATCH 请求
一般实际项目中,我们都是 PUT 不够用了之后才用 PATCH 请求去更新数据。
1 |
|
前后端传值
@PathVariable 和 @RequestParam
@PathVariable
用于获取路径参数,即路径中的占位符部分,@RequestParam
用于从请求参数中获取参数值,即 URL 中的查询参数或者表单参数。
举个简单的例子:
1 |
|
如果我们请求的 url 是:/klasses/123456/teachers?type=web
那么我们服务获取到的数据就是:klassId=123456,type=web
。
@RequestBody
用于读取 Request 请求(可能是 POST,PUT,DELETE,GET 请求)的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用HttpMessageConverter
或者自定义的HttpMessageConverter
将请求的 body 中的 json 字符串转换为 java 对象。
我用一个简单的例子来给演示一下基本使用!
我们有一个注册的接口:
1 |
|
UserRegisterRequest
对象:
1 |
|
我们发送 post 请求到这个接口,并且 body 携带 JSON 数据:
1 |
|
这样我们的后端就可以直接把 json 格式的数据映射到我们的 UserRegisterRequest
类上。

需要注意的是:**一个请求方法只可以有一个@RequestBody
,但是可以有多个@RequestParam
和@PathVariable
**。 如果你的方法必须要用两个 @RequestBody
来接受数据的话,大概率是你的数据库设计或者系统设计出问题了!
读取配置信息
很多时候我们需要将一些常用的配置信息比如阿里云 oss、发送短信、微信认证的相关配置信息等等放到配置文件中。
下面我们来看一下 Spring 为我们提供了哪些方式帮助我们从配置文件中读取这些配置信息。
我们的数据源application.yml
内容如下:
1 |
|
@Value(常用)
使用 @Value("${property}")
读取比较简单的配置信息:
1 |
|
@ConfigurationProperties(常用)
通过@ConfigurationProperties
读取配置信息并与 bean 绑定。其中prefix为配置信息的前缀
1 |
|
@PropertySource(不常用)
@PropertySource
读取指定 properties 文件
1 |
|
参数校验
数据的校验的重要性就不用说了,即使在前端对数据进行校验的情况下,我们还是要对传入后端的数据再进行一遍校验,避免用户绕过浏览器直接通过一些 HTTP 工具直接向后端请求一些违法数据。
JSR(Java Specification Requests) 是一套 JavaBean 参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们 JavaBean 的属性上面,这样就可以在需要校验的时候进行校验了,非常方便!
校验的时候我们实际用的是 Hibernate Validator 框架。Hibernate Validator 是 Hibernate 团队最初的数据校验框架,Hibernate Validator 4.x 是 Bean Validation 1.0(JSR 303)的参考实现,Hibernate Validator 5.x 是 Bean Validation 1.1(JSR 349)的参考实现,目前最新版的 Hibernate Validator 6.x 是 Bean Validation 2.0(JSR 380)的参考实现。
SpringBoot 项目的 spring-boot-starter-web 依赖中已经有 hibernate-validator 包,不需要引用相关依赖。如下图所示(通过 idea 插件—Maven Helper 生成):
注:更新版本的 spring-boot-starter-web 依赖中不再有 hibernate-validator 包(如 2.3.11.RELEASE),需要自己引入 spring-boot-starter-validation
依赖。

需要注意的是:所有的注解,推荐使用 JSR 注解,即javax.validation.constraints
,而不是org.hibernate.validator.constraints
一些常用的字段验证的注解
@NotEmpty
被注释的字符串的不能为 null 也不能为空@NotBlank
被注释的字符串非 null,并且必须包含一个非空白字符@Null
被注释的元素必须为 null@NotNull
被注释的元素必须不为 null@AssertTrue
被注释的元素必须为 true@AssertFalse
被注释的元素必须为 false@Pattern(regex=,flag=)
被注释的元素必须符合指定的正则表达式@Email
被注释的元素必须是 Email 格式。@Min(value)
被注释的元素必须是一个数字,其值必须大于等于指定的最小值@Max(value)
被注释的元素必须是一个数字,其值必须小于等于指定的最大值@DecimalMin(value)
被注释的元素必须是一个数字,其值必须大于等于指定的最小值@DecimalMax(value)
被注释的元素必须是一个数字,其值必须小于等于指定的最大值@Size(max=, min=)
被注释的元素的大小必须在指定的范围内@Digits(integer, fraction)
被注释的元素必须是一个数字,其值必须在可接受的范围内@Past
被注释的元素必须是一个过去的日期@Future
被注释的元素必须是一个将来的日期
1 |
|
我们在需要验证的参数上加上了@Valid
注解,如果验证失败,它将抛出MethodArgumentNotValidException
。
1 |
|
验证请求参数@Validated
使用PathVariables 和 RequestParameters一定不要忘记在类上加上 @Validated
注解了,这个参数可以告诉 Spring 去校验方法参数。
1 |
|
全局处理 Controller 层异常
介绍一下我们 Spring 项目必备的全局处理 Controller 层异常。
相关注解:
@ControllerAdvice
:注解定义全局异常处理类@ExceptionHandler
:注解声明异常处理方法
1. 新建异常信息实体类
非必要的类,主要用于包装异常信息。
1 |
|
2. 自定义异常类型
一般我们处理的都是 RuntimeException
,所以如果你需要自定义异常类型的话直接集成这个类
1 |
|
3. 新建异常处理类
我们只需要在类上加上@ControllerAdvice
注解这个类就成为了全局异常处理类,当然你也可以通过 assignableTypes
指定特定的 Controller 类,让异常处理类只处理特定类抛出的异常。
1 |
|
4. controller模拟抛出异常
1 |
|
使用 Get 请求 localhost:8080/api/resourceNotFoundException,服务端返回的 JSON 数据如下:
1 |
|
5. 编写测试类
MockMvc 由org.springframework.boot.test
包提供,实现了对Http请求的模拟,一般用于我们测试 controller 层。
1 |
|
@ResponseStatus
@ResponseStatus
是Spring框架中的一个注解,用于在控制器方法或自定义异常类上指定默认的HTTP响应状态码。
1 |
|
1 |
|
使用 Get 请求 localhost:8080/api/resourceNotFoundException2,服务端返回的 JSON 数据如下:
1 |
|
ResponseStatusException类
ResponseStatusException
类是位于org.springframework.web.server.ResponseStatusException
包中的类。它是Spring框架中用于表示特定HTTP状态码的异常类。
这种通过 ResponseStatus
注解简单处理异常的方法是的好处是比较简单,但是一般我们不会这样做,通过ResponseStatusException
会更加方便,可以避免我们额外的异常类。
1 |
|
使用 Get 请求 localhost:8080/api/resourceNotFoundException2 ,服务端返回的 JSON 数据和使用 ResponseStatus
实现的效果一样
ResponseStatusException
提供了三个构造方法:
1 |
|
构造函数中的参数解释如下:
status :http status
reason :response 的消息内容
cause :抛出的异常
格式化 json 数据
@JsonFormat
一般用来格式化 json 数据。
比如:
1 |
|
扁平化对象
1 |
|
未扁平化之前:
1 |
|
使用@JsonUnwrapped
扁平对象之后:
1 |
|
1 |
|
测试相关
@ActiveProfiles
一般作用于测试类上, 用于声明生效的 Spring 配置文件。
1 |
|
@Test
声明一个方法为测试方法
@Transactional
被声明的测试方法的数据会回滚,避免污染测试数据。
@WithMockUser
Spring Security 提供的,用来模拟一个真实用户,并且可以赋予权限。
1 |
|