Spring相关
什么是 Spring 和 SpringBoot
Spring 是什么
Spring Framework 是很多模块的集合,使用这些模块可以很方便地协助我们进行开发;核心功能是 IoC 和 AOP。
下图是 Spring 中各个模块之间的依赖关系。需要大概知道一些模块是干嘛的。
- Core Container:基础模块,包含spring-core、spring-beans等
- Data Access/Integration:数据访问模块,包含spring-jdbc、spring-orm等
- Spring Web:网络部分,包含spring-webmvc、spring-websocket等等

Spring Boot
SpringBoot是用来快速开发Spring应用的一个脚手架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程;
SpringBoot提供了很多内置的Starter(⼀系列依赖关系的集合)结合自动配置,对主流框架如 Redis MySQL无需配置或开箱即用;
SpringBoot简化了开发,采用JavaConfig的方式可以使用零xml的方式进行开发,配置即自动装配;
SpringBoot内置Web容器无需依赖外部Web服务器,省略了Web.xml,直接运行jar文件就可以启动web应用;
SpringBoot帮我管理了常用的第三方的依赖版本,减少出现版本冲突的问题; SpringBoot自带了监控功能,可以监控应用程序的运行状况,或者内存,线程池,Http请求统计等,同时还提供了优雅关闭应用程序等功能。
什么是Spring IoC
IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。这样就把应用从复杂的依赖关系中解放出来,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
@Bean 和 @Component 和 @Configuration
- Spring会自动检测被Component注解的类,并将其作为bean注册到IoC容器中。但是不能用于第三方库,因为不能修改他们的源码,这时候要用到bean
- bean是方法注解,一般会在配置类Configuration中编写方法,返回值将被注册为 Spring IoC 容器中的一个 bean;不推荐在Component类中对方法添加@Bean注解,这样会导致非预期的Bean实例化问题
1 |
|
如果方法被 @Bean 注解,并且方法的参数是 Spring IoC 容器中已存在的 Bean 对象,那么 Spring 会自动将这些 Bean 注入到方法中。
@Autowired 和 @Resource
@Bean 和 @Component 是告诉Spring IoC,某个类需要IoC进行管理;而@Autowired 和 @Resource则是告诉 Spring 框架,我需要使用某个Bean,需要进行依赖注入(DI)
@Autowired 用于自动注入 Bean 依赖项,它默认按照类型进行注入。它可以标注在构造器、字段、Setter 方法或配置方法上,Spring 容器会自动查找匹配类型的 Bean 并将其注入。
当存在多个相同类型的 Bean 时,@Autowired 默认按类型注入可能产生歧义。此时,可以与 @Qualifier 结合使用,通过指定 Bean 的名称来精确选择需要注入的实例;或者使用 @Primary 方法将某个实现设为首选注入对象。
@Resource(name="beanName") 默认按名称 (by Name) 查找 Bean 进行注入,
依赖注入的常见方式
以下三种方式的初始化时间依次推迟:
- 构造函数注入:通过类的构造函数来注入依赖项(最佳实践);
- Field(字段) 注入:直接在类的字段上使用注解(如
@Autowired或@Resource)来注入依赖项; - Setter 注入:通过类的 Setter 方法来注入依赖项(官方更推荐使用构造函数注入);
Spring 循环依赖
循环依赖是指 Bean 对象循环引用,是两个或多个 Bean 之间相互持有对方的引用。Spring 框架通过使用三级缓存来解决这个问题,确保即使在循环依赖的情况下也能正确创建 Bean。
Bean 的生命周期

Bean的作用域和线程安全
Spring 中 Bean 的作用域通常有下面几种:
- singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
- prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续
getBean()两次,得到的是不同的 Bean 实例。 - request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
- session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
- application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
- websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
详细解释见:https://g.co/gemini/share/e923f73db565
AOP 相关
面向切面编程,是OOP的一种延续,OOP 不能很好地处理一些分散在多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流、接口幂等等),而 AOP 可以将横切关注点(如日志记录、事务管理、权限控制、接口限流、接口幂等等)从 核心业务逻辑 中分离出来,实现关注点的分离。
主要通过注解的方式实现,且以方法为单位,可以在方法运行前和结束后添加额外逻辑,为代理模式,会在bean生命周期中生成一个代理对象。主要有如下通知类型:
- 前置通知(Before):在目标方法执行之前执行。
- 后置通知(After):在目标方法执行之后执行,无论方法是否成功完成。
- 返回通知(AfterReturning):在目标方法成功执行并返回结果后执行。
- 异常通知(AfterThrowing):在目标方法抛出异常后执行。
- 环绕通知(Around):在目标方法执行前后都执行,甚至可以控制目标方法是否执行。
类限定:Pointcut表达式
- within()和target()
- @within()和@target()
- execute()
@SpringBootApplication 注解
用于标注主启动类,如下:
1 |
|
可以看出⼤概可以把 @SpringBootApplication 看作是@Configuration、@EnableAutoConfiguration、@ComponentScan注解的集合。根据 SpringBoot 官⽹,这三个注解的作⽤分别是:
@EnableAutoConfiguration:启⽤ SpringBoot 的⾃动配置机制@ComponentScan:扫描被@Component(@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类(启动类在src最外层)@SpringBootConfiguration:也可能是@Configuration,表示启动类也是一个配置类,允许在上下文中注册额外的 bean 或导⼊其他配置类,支持JavaConfig的方式进行配置(?)
SpringBoot 自动装配的原理
理解得还不够透彻
@EnableAutoConfiguration是启动⾃动配置的关键- 里面
@Import了AutoConfigurationImportSelector,用于选择出所有需要自动配置的类,同时也会让SpringBoot的自动配置类的注入顺序在最后 - 这里面会去读取一个文件
spring.factories,并根据@Condition的相关注解进行筛选,最后完成注入
Http 请求的相关注解
GET请求:
@PathVariable和@RequestParam若请求 URL 为
/glasses/123/teachers?type=web,则glassId = 123为 PathVariable,type = web为 RequestParam。
1 |
|
- POST请求:
@RequestBody,接收请求体中的JSON或XML等数据,自动绑定到Java对象上;Spring实际上会使用HttpMessageConverter,它会根据请求头中的Content-Type字段来决定如何解析请求体,例如application/json或application/xml
此外,还可以直接将HttpServletRequest request作为控制器方法的参数,来手动获取请求相关的所有信息,更灵活但更繁琐。
注意使用request.getInputStream()读取请求体时只能读取一次,无法和@RequestBody共同使用,因此如果在切面中读取参数并打印后,业务控制器中无法再正确获得参数;可以使用HttpServletRequestWrapper对请求体进行缓存。
如果使用request.getParameter(String name)来获取参数,它和@RequestParam一样,肯定能得到请求参数,而对于请求体却只能获取到表单提交的参数,即Content-Type: application/x-www-form-urlencoded的情况;如果是Content-Type: application/json的话,则只能通过@RequestBody得到。
定时任务
使用@Scheduled(cron = "")注解修饰方法即可。
cron表达式:共6个坑要填,即Seconds Minutes Hours DayofMonth Month DayofWeek,其中:
*表示该域的任意值,例如每分钟每小时每天5/20表示经过5后触发一次,在此之后每20分钟触发一次,分割的数表示枚举,单个值也是枚举,例如10,14,16表示24小时制下的3个小时点
多个定时任务之间是串行执行的,没有优先级之分;可以将其配置为并发执行,通过:1. 在启动类上添加@EnableAsync并在定时任务上添加@Async即可;建议通过线程池来管理并发执行的任务,因为默认配置下会不断创建新线程来执行:
只要新建Spring的ThreadPoolTaskExecutor类型并将其放在@Bean中即可,Spring会自动寻找线程池来执行定时任务。




