导图社区 Spring学习
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。
编辑于2021-08-26 21:52:50Spring
JavaEE 应用程序框架,提供底层容器与基础设施,可以快速与大量常用开源框架集成
Spring Core
IoC
Inversion of Control,控制反转 组件的创建、配置等转移到 IoC 容器中,应用程序只需要直接使用已经装配到 IoC 容器的组件 ---依赖注入--- Dependency Injection,直接持有依赖的对象,而不是重新 new 一个该对象 ---无侵入容器--- 不需要事先准备特定接口 ---@Configuration--- 注解配置类 ---@ComponentScan--- 注解扫描包及其子包,将其中 @Component 的 Bean 创建出来 ---@Conponent--- 注解为 JavaBean 并交由 IoC 容器托管 ---@Autowired--- 注入 JavaBean
底层原理
XML/注解解析 + 工厂模式 + 反射
IoC 接口
ApplicationContext
IoC 容器实例,一次性创建所有 Bean,继承自 BeanFactory,支持国际化,事件,通知机制等
BeanFactory
另一种 IoC 容器,工厂模式,按需创建
Bean 管理
创建对象
创建
@Component @Service @Controller @Repository
条件装配
@Profile("!native")
指定环境创建
Condition
Conditional - 满足自定义 Condition 时创建 ConditionalOnProperty - 配置中指定项的值为 true 或 false 时创建 ConditionalOnClass - 当前 classpath 中存在指定类时创建 ConditionalOnMissingBean - 当前 classpath 中不存在指定类时创建 ConditionalOnWebApplication - 在 Web 环境中创建 ConditionalOnExpression - 根据表达式判断是否创建
FactoryBean
工厂模式构造 Bean
初始化与销毁
---@PostConstruct--- 构造器之后执行 ---@PreDestroy--- 回收对象前执行
配置
@Configuration @ComponentScan @PropertySource("app.properties") public class AppProperties{ @Value("${app.item:defValue}") String item; } @Component public class Config{ @Value("#{appProperties.item}") private String item; }
别名
@Bean("name") @Bean + @Qualifier("name") ------ @Autowired("name") @Autowired + @Qualifier("name") ------ @Primary
注入属性
List 注入
@Autowired List<Validator> validators; @Component @Order(1) public class ValidatorA implements Validator{...}
可选注入
@Autowired(required = false)
Resource
注入配置文件、资源文件 @Value("classpath:/logo.json") //@Value("file:/path/logo.json") private Resource resource;
第三方Bean 注入
@Configuration @ComponentScan public class AppConfig{ @Bean // 只会调用一次,Bean 对象单例 ClassA createClassA(){...} }
scope
作用域
singleton
默认类型,当 IOC 容器一创建就会创建 bean 的实例,而且是单例的,每次得到的都是同一个实例
prototype
原型的,当 IOC 容器创建时不会创建 bean 实例,每次调用 getBean 方法时实例化该 bean,而且每次得到的都是不同的实例
request
每次请求实例化一个 bean,适用于 WebApplicationContext 环境
session
在一次会话中共享一个 bean,适用于 WebApplicationContext 环境
Bean 生命周期
1. 调用无参构造器 - 创建 bean 实例 2. 调用 set 方法 - 为 bean 的属性设置,设置对其他 bean 的引用 后置处理器 - 初始化之前执行 3. 调用 bean 的 PostConstruct - 初始化 bean 后置处理器 -初始化之后执行 4. 使用 bean 5. 调用 bean 的 PreDestroy - 关闭容器前,销毁 bean
循环依赖
1. 循环依赖的双方都使用构造器注入时,IoC 容器会抛出 BeanCurrentlyCreationException,可以使用 setter 注入方式避免这个问题 2. singleton 支持循环依赖,prototype 不支持循环依赖,同样会抛异常
getSingleton
doCreateBean
populateBean
addSingleton
三级缓存
1. A创建过程中需要B,于是A将自己放到三级缓里面,去实例化B。 2. B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。 3. B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。 ------------------------------- 1. 调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA 2. 在getSingleton()方法中,从一级缓存中查找,没有,返回null 3. doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的) 4. 在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的creatBean方法 5. 进入AbstractAutowireCapableBeanFactory#ndoCreateBean,先反射调用构造器创建出beanA的实例,然后判断:是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否在第四步的集合中)。判断为true则将beanA添加到【三级缓存】中 6. 对beanA进行属性填充,此时检测到beanA依赖于beanB,于是开始查找beanB 7. 调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性 8. 此时 beanB依赖于beanA,调用getSingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的beanA 9. 这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中 10. 随后beanA继续他的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getsingleton()方法中继续向下执行,将beanA从二级缓存移动到一级缓存中
singletonObjects
earlySingletonObjects
singletonFactories
AOP
Aspect Oriented Programming,面向切面编程 ---AOP织入方式--- 1. 编译期,由编译器把切面调用编译进字节码,需要定义新的关键字并扩展编译器(AspectJ) 2. 类加载期,在目标类装载到 JVM 时,由特殊的类加载器对目标字节码重新“增强” 3. 运行期,通过 JVM 的动态代理或者第三方库实现运行期动态织入(CGLIB、Javassist) ---概念--- Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点; Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行; Pointcut:切入点,即一组连接点的集合; Advice:增强,指特定连接点上执行的动作; Introduction:引介,指为一个已有的Java对象动态地增加新的接口; Weaving:织入,指将切面整合到程序的执行流程中; Interceptor:拦截器,是一种实现增强的方式; Target Object:目标对象,即真正执行业务的核心逻辑对象; AOP Proxy:AOP代理,是客户端持有的增强后的对象引用。
实现原理
动态代理,在运行期创建一个代理实例,实际被执行的是代理对象的方法,代理对象的方法可以执行被代理对象的方法
JDK 动态代理
1. 创建接口,定义方法 2. 创建接口实现类,实现方法 3. 创建代理对象,Proxy.newProxyInstance( ClassLoader, // 类加载器 Class<?>[], // 要实现的接口 InvocationHandler // 调用方法的 InvocationHandler ) 4. 覆写 InvocationHandler 的 invoke( Ojbect proxy, // 被代理的对象 Method method,// 要执行的方法 Ojbect[] args // 方法的参数列表 )
CGLIB 动态代理
概念
连接点
可以被增强的方法
切入点
实际被增强的方法
通知(增强)
切入点被执行的增强逻辑
@Before
前置通知,目标方法之前执行
@AfterReturning
返回后通知,执行方法结束前执行(异常不执行)
@AfterThrowing
异常通知,出现异常时执行
@After
后置通知,目标方法之后执行(始终执行)
@Around
环绕通知,环绕目标方法执行
顺序
V4
正常情况:@AroundStart -> @Before -> Invoke -> @AroundEnd -> @After -> @AfterRunning 异常情况:@AroundStart -> @Before -> Invoke -> @After -> @AfterThrowing
V5
正常情况:@AroundStart -> @Before -> Invoke -> @AfterRunning -> @After -> @AroundEnd 异常情况:@AroundStart -> @Before -> Invoke -> @AfterThrowing -> @After
切面
把通知(增强)应用到切入点的过程
操作
1. 定义用于标记连接点的注解,并在连接点标记上注解 2. 定义 Aspect 逻辑,@Aspect, @Component 1. @Before("execution(public * *(..))") 2. @Before("@annotation(metricTime)") 3. 在 @Configuration 类启用 AOP,@EnableAspectJAutoProxy
避坑指南
1. 访问被注入的 Bean 时,总是调用方法而非直接访问字段 2. 编写 Bean 时,如果可能会被代理,就不要编写 public final 方法 ------ Java 编译器在发现构造方法中没有 super() 时,会自动加上; 如果直接构造字节码,一个类的构造方法中不一定要调用 super(); Spring 通过 CGLIB 构造 Proxy 类就是如此; 在 $$EnhanceBySpringCGLIB 类中不会执行变量的赋值语句; proxy 的目的是代理方法,成员变量的初始化在构造函数中完成
Database
Jdbc JdbcTemplate,提供 Jdbc 的各类常用操作 DAO,Data Access Object,使用 JdbcTemplate + BeanPropertyRowMapper<T> 实现数据行与实体映射 Hibernate,全自动(自动填充 JavaBean,自动生成 SQL) ORM JPA,Java Persistence API,ORM 标准,只是接口,需要引入 ORM 框架实现,如 Hiberanate MyBatis,半自动(自动填充 JavaBean,手写 SQL) ORM
Transaction
1. 创建 PlatformTransactionManager 组件 2. @EnableTransactionManagement 启用声明式事务 3. @Transactional 添加对目标方法的事务支持 ---回滚--- RuntimeException | IOException
propagation
事务传播级别
REQUIRED
如果当前没有事务,就创建一个新事务,如果当前有事务,就加入到当前事务中执行
SUPPORTS
表示如果有事务,就加入到当前事务,如果没有,那也不开启事务执行。这种传播级别可用于查询方法,因为 SELECT 语句既可以在事务内执行,也可以不需要事务;
MANDATORY
表示必须要存在当前事务并加入执行,否则将抛出异常。这种传播级别可用于核心更新逻辑,比如用户余额变更,它总是被其他事务方法调用,不能直接由非事务方法调用;
REQUIRES_NEW
表示不管当前有没有事务,都必须开启一个新的事务执行。如果当前已经有事务,那么当前事务会挂起,等新事务完成后,再恢复执行;
NOT_SUPPORTED
表示不支持事务,如果当前有事务,那么当前事务会挂起,等这个方法执行完成后,再恢复执行;
NEVER
和 NOT_SUPPORTED 相比,它不但不支持事务,而且在监测到当前有事务时,会抛出异常拒绝执行;
NESTED
表示如果当前有事务,则开启一个嵌套级别事务,如果当前没有事务,则开启一个新事务。
isolation
事务隔离级别 目的是解决事务的并发一致性问题: 1. 修改丢失 - 修改了未提交的修改 - 写隔离 2. 脏读 - 读取了未提交的修改 - 读写隔离 3. 不可重复读 - 读取了已提交的修改 4. 幻读 - 读取了已提交的增删行数
读未提交
读已提交
可重复读
串行化
ORM
1. 实现 ORMTemplate,持有 Class<T> 实例 - RowMapper<T> 实例的映射表,并通过扫描 entities 包填充映射表 2. 持有一个 CriteriaQuery,用于解释生成 SQL
Web
Servlet 三大组件:Servlet、Filter、Listener
Filter
在 Spring MVC 中,Filter 托管给 Spring 容器,Spring 提供一个 DelegatingFilterProxy 托管给 Servlet 容器,当 DelegatingFilterProxy 生效后,DelegatingFilterProxy 将请求代理给 Filter
Interceptor
preHandle postHandle afterCompletion ---注册--- WebMvcConfigurer
CORS
Cross-Origin Resource Sharing Access-Control-Allow-Origin 响应头 1. @CrossOrigin 注解目标 @RestController 2. WebMvnConfigurer 中注册全局 CorsRegistry 3. CorsFilter
国际化
MessageSource <- _zh_CN.properties LocaleResolver -> Locale
异步
1. 返回一个 Callable<T>,在 callable 内部写入响应 2. 返回一个 DeferredResult 实例,在另一个线程将响应写入 deferredResult ------ 推荐 java.nio -> Netty 针对调用者: 同步:发出请求后需要等待结果才能做其他事情 异步:发出请求后,可以做其他事情,结果的处理交给回调或轮训 针对被调用者: 阻塞:接收到请求后执行完成才返回结果 非阻塞:接收到请求后立刻返回,真实结果等待执行完成后另行返回
Webflux
Reacotr,Sevlet3.1 支持 ---Publisher--- Flux - 返回 N 个元素 Mono - 返回 1/0 个元素 ---声明数据流--- just - 只是声明数据流,不会发出数据流,订阅之后才会触发数据流 ---三种信号--- 元素值 错误信号 - 终止型号 完成信号 - 终止型号 1. 终止信号不能共存 2. 错误信号的数据流是空的 3. 没有终止信号表示无限数据流
WebSocket
JMS
Java Message Service
JMX
Java Management Extensions 1. 编写 MBean(@ManagedResource, @ManagedAttribute, @ManagedOperation, @ManagedOperationParameter) 2. 注册 MBean ---控制 MBean--- jconsole
Spring Boot
Configuration
@SpringBootApplication
@SpringBootApplication // 标注主配置类 @SpringBootConfiguration // Spring Boot 的配置类 @Configuration // 标注配置类 @Component // 组件 @EnableAutoConfiguration // 开启自动配置功能 @AutoConfigurationPackage // 自动配置包 @Import(AutoConfigurationImportSelector.class) // 扫描主配置类所在包及其子包的所有组件,并导入 IoC 容器
自动配置原理
@EnableAutoConfiguration @Import(AutoConfigurationImportSelector.class) getCandidateConfigurations // 获取配置 SpringFactoriesLoader.loadFactoryNames() // 扫描所有 jar 包类路径下 META-INF/spring.factories 的 EnableAutoConfiguration 类清单 // 把扫描到的类名清单转换成 properties 对象 // 从 properties 获取类名,创建组件并添加到容器中
YAML
---格式--- key:(空格)value 空格不能少 key 左对齐的一列都是同一层级,以空格的缩进控制层级 属性和值的大小写敏感 ---引号处理--- "" - 转义字符以转义后的效果显示 '' - 转义字符以字面字符显示(自动添加转义符处理) ---行内写法--- key:{k1:v1,k2:v2} key:[v1,v2,v3]
配置文件注入
@ConfigurationProperties
@Component @ConfigurationProperties(prefix="k1.k2.k3")
@Value
SpEL
@PropertySource
@PropertySource(value={"classpath:aaa.yml"}) // 使用指定配置文件 @Component @ConfigurationProperties(prefix="k1.k2.k3")
@ImportSource
@ImportSource(locations = {"classpath:beans.xml"}) // 导入配置文件,让其生效
占位符
k1=v1 k2=${random.int} k3=${random.long} k4=${random.int[1024,65536]} k5=${K1:default_value}_suffix
Profile
---激活 profile--- spring.profile.active=native java -jar aaa.jar --spring.profiles.active=test java -jar aaa.jar -Dspring.profiles.active=dev
加载顺序
优先级递增 ---内部配置--- -classpath:/ -classpath:/config/ -file:./ -file:./config/ ---外部配置--- SpringApplication.setDefaultProperties 指定属性值 @Configuration + @PropertySource 指定配置文件 jar 包内部的 application.properties 和 application.yml jar 包外部的 application.properties 和 application.yml jar 包内部的 application-{profile}.properties 和 application-{profile}.yml jar 包外部的 application-{profile}.properties 和 application-{profile}.yml RandomValuePropertySource 配置的 random.* 属性值 操作系统环境变量 Java 系统属性 java:comp/env 的 JNDI 属性 命令行参数:java -jar aaa.jar --k1=v1 --k2=v2 ---改变内部配置位置--- spring.config.location=D:/aaa.yml
条件配置
@ConditionalOnJava - 系统的 Java 版本是否符合要求 @ConditionalOnBean - 容器中存在指定 Bean @ConditionalOnMissingBean - 容器中不存在指定 Bean @ConditionalOnExpression - 满足 SpEL 表达式条件 @ConditionalOnClass - 系统中存在指定类 @ConditionalOnMissingClass - 系统中不存在指定类 @ConditionalOnSingleCandidate - 系统中只有一个指定 Bean 或者这个 Bean 是首选 Bean @ConditionalOnProperty - 系统中指定属性是否有指定值 @ConditionalOnResource - classpath 下是否存在指定资源文件 @ConditionalOnWebApplication - 当前是 Web 环境 @ConditionalOnNotWebApplication - 当前不是 Web 环境 @ConditionalOnJndi - JNDI 存在指定项
Log
默认使用 Slf4j + LogBack
输出格式
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n ------ %d 日期时间 %thread 线程名 %-5level 级别,总宽度 5 个字符 %logger(50) logger 名字,最长 50 个字符,否则按照句点分割缩写 %msg 日志内容 %n 换行符
配置
logging.level.packagename=trace # logging.path=logs # 默认文件名 springboot.log logging.file.name=logs/app.log logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} -%msg%n
Web
1. WebMvcAutoConfiguration 2. WebMvcProperties 3. ViewResolver自动配置 4. 静态资源自动映射 5. Formatter与Converter自动配置 6. HttpMessageConverter自动配置 7. 静态首页 8. favicon 9. 错误处理
嵌入式 Servlet 容器
Tomcat
Jetty
长连接
Undertow
不支持 JSP
Servlet 容器自动装配原理
1. SpringBoot 根据容器的依赖,注入相应 EmbeddedServletContainerFacotry 2. 创建 Servlet 容器,并调用后置处理器 EmbeddedServletContainerCustomizerBeanProcessor 3. 后置处理器调用所有定制器方法 EmbeddedServletContainerCustomizer
Servlet 容器启动原理
执行 run 方法,SpringBoot 应用启动 执行 refreshContext(context),SpringBoot 创建 IoC 容器,初始化容器,并创建容器中所有组件 Web 应用 - AnnotationConfigEmbeddedWebApplicationContext 非 Web 应用 - AnnotationConfigApplicationContext - refresh(context) // 刷新 IoC 容器 - onRefresh() // Web IoC 重写 onRefresh // 创建嵌入式 Servlet 容器 // 启动嵌入式 Servlet 容器
Database
Druid MyBatis SpringData JPA
Starts
@Configuration //指定这个类是一个配置类 @ConditionalOnXXX //在指定条件成立的情况下自动配置类生效 @AutoConfigureAfter //指定自动配置类的顺序 @Bean //给容器中添加组件 @ConfigurationProperties // 结合相关xxxProperties类来绑定相关的配置 @EnableConfigurationProperties //让xxxProperties生效加入到容器中 // 将需要启动就加载的自动配置类,配置在META‐INF/spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
Actuator
autoconfig 所有自动配置信息 auditevents 审计事件 beans 所有Bean的信息 configprops 所有配置属性 dump 线程状态信息 env 当前环境信息 health 应用健康状况 info 当前应用信息 metrics 应用的各项指标 mappings 应用@RequestMapping映射路径 shutdown 关闭当前应用(默认关闭) trace 追踪信息(最新的http请求)
Devtools
Spring Cloud
服务注册
Eureka
Zookeeper
Consul
Nacos
动态服务发现、配置管理和服务管理平台
注册中心
服务注册
curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'
服务发现
curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'
配置中心
发布配置
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"
获取配置
curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"
持久化
使用 MySQL 持久化
准备 db
create database `nacos.db` default charset utf8mb4 collate utf8mb4_romanian_ci; 使用 nacos/conf/nacos-mysql.sql 初始化 `nacos.db`
修改配置
spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://localhost:3306/nacos.db?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true db.user=nacos_devtest db.password=youdontknow
重启
sh bin/shutdown.sh sh bin/startup.sh -m standalone
服务调用
Ribbon
LoadRalancer
Feign
OpenFeign
服务降级
Hystrix
Sentinel
资源(服务,方法,代码段等)保护
定义资源
1. 主流框架,默认适配(Web Servlet, Dubbo, Spring Cloud, gRPC, Spring WebFlus, Reactor 等) 2. 抛异常 3. 返回布尔值 4. 注解 5. 异步调用
@SentinelResource
value
资源名称
entryType
blockHandler / blockHandlerClass
针对 BlockException
fallback
针对所有类型的异常
defaultFallback
exceptionsToIgnore
定义规则
流量控制
QPS 线程数
熔断降级
慢调用比例 异常比例 异常数
系统保护
触发值,用于触发自适应控制阶段 所有入口流量的平均响应时间 入口流量的最大并发数 所有入口资源的 QPS 当前系统的 CPU 使用率
来源访问控制
黑白名单
热点参数
热点 key 限流,对接口的指定参数限流
检验效果
HTTP 接口 日志 block 事件
规则持久化
服务网关
Zuul
gateway
服务配置 & 总线
Config
Bus
分布式事务
Seata
分布式事务解决方案
Transaction Coordinator (TC)
事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚;
Transaction Manager (TM)
控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议;
Resource Manager (RM)
控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚