springboot-actuator
| SpringBoot监控神器-Actuator保姆级教程 | 手把手教你实现SpringBoot的监控! | Spring Cloud应用的优雅下线与灰度发布 |
|---|
springboot-admin
| Spring Boot Admin横空出世! | Spring Boot Admin服务监控利器 |
|---|
springboot-aop
| 类别 | 函数 | 入参 | 说明 |
|---|---|---|---|
| 方法切点函数 | execution() | 方法匹配模式串 | 表示满足某一匹配模式的所有目标类方法连接点。如execution(* greetTo(..))表示所有目标类中的greetTo()方法。 * |
| @annotation() | 方法注解类名 | 表示标注了特定注解的目标方法连接点。如@annotation(com.baobaotao.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法。 | |
| 方法入参切点函数 | args() | 类名 | 通过判别目标类方法运行时入参对象的类型定义指定连接点。如args(com.baobaotao.Waiter)表示所有有且仅有一个按类型匹配于Waiter的入参的方法。 |
| @args() | 类型注解类名 | 通过判别目标方法的运行时入参对象的类是否标注特定注解来指定连接点。如@args(com.baobaotao.Monitorable)表示任何这样的一个目标方法:它有一个入参且入参对象的类标注@Monitorable注解。 | |
| 目标类切点函数 | within() | 类名匹配串 | 表示特定域下的所有连接点。如within(com.baobaotao.service.*)表示com.baobaotao.service包中的所有连接点,也即包中所有类的所有方法,而within(com.baobaotao.service.*Service)表示在com.baobaotao.service包中,所有以Service结尾的类的所有连接点。 |
| target() | 类名 | 假如目标类按类型匹配于指定类,则目标类的所有连接点匹配这个切点。如通过target(com.baobaotao.Waiter)定义的切点,Waiter、以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点。 | |
| @within() | 类型注解类名 | 假如目标类按类型匹配于某个类A,且类A标注了特定注解,则目标类的所有连接点匹配这个切点。 如@within(com.baobaotao.Monitorable)定义的切点,假如Waiter类标注了@Monitorable注解,则Waiter以及Waiter实现类NaiveWaiter类的所有连接点都匹配。 | |
| @target() | 类型注解类名 | 目标类标注了特定注解,则目标类所有连接点匹配该切点。如@target(com.baobaotao.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter所有连接点匹配切点。 | |
| 代理类切点函数 | this() | 类名 | 代理类按类型匹配于指定类,则被代理的目标类所有连接点匹配切点。这个函数比较难理解,这里暂不举例,留待后面详解。 |
相关文章
springboot-dynamicDB
springboot-feature
参数读取
| SpringBoot多种读取配置文件中参数的方式 | SpringBoot的@Value注解,高级特性,真心强大! | Spring Boot中yaml文件:定义list集合、数组及注意事项 |
|---|---|---|
| 6种方式读取SpringBoot的配置,老鸟都这么玩(原理+实战) | Spring Boot优雅加载配置文件的几种姿势! | 如何不重新编译让Spring Boot配置文件生效 |
druid
| Spring Boot如何统计、监控SQL运行情况? | 使用druid进行数据库加密 |
|---|
优化
| Spring Boot深度调优,6得飞起~ | Spring Boot这样优化,让你的项目飞起来! | 7种方式,教你提升Spring Boot项目的吞吐量 |
|---|---|---|
| SpringBoot内置Tomcat线程数优化配置,你学会了吗 | 这样优化,0.059s启动一个SpringBoot项目 | 7min到40s:Spring Boot启动优化实践 |
other
| 优雅的实现Spring Boot异步线程间数据传递 | Spring Boot请求路径可以定义成/**/**这种格式吗? | 你绝对不知道的SpringBoot的外部化配置特性! |
|---|---|---|
| Spring Boot如何实现插件化开发模式 | Spring Boot业务组件化开发,维护起来很香 | try-catch这样写才足够优雅 |
| 请不要自己写,Spring Boot非常实用的内置功能 |
模式
| 巧妙利用SpringBoot应用责任连模式,让编程事半功倍 | SpringBoot使用装饰器模式 |
|---|
业务
retry
| Spring中的重试机制,简单、实用!(Spring Retry) | 重试框架Spring-Retry和Guava-Retry,你知道该怎么选吗? | Java远程调用失败?如何优雅的进行重试? |
|---|---|---|
| Spring Boot中使用spring-retry轻松解决重试 | 接口请求重试的8种方法,你用哪种? |
Thymeleaf模板引擎
| Thymeleaf快速入门 | 细品Spring Boot+Thymeleaf,还有这么多好玩的细节! |
|---|
Spring-Boot-Devtools
Spring Boot应用支持热部署,无需手动重启Spring Boot应用,spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是修改代码后自动启动springboot服务,速度比手动停止后再启动要快,节省出来的并不是手工操作的时间,具体原理主要是使用了两个ClassLoader,一个Classloader加载不会改变的类(第三方Jar包),另一个ClassLoader加载会更改的类,称为restart ClassLoader,这样在有代码更改的时候,原来的restartClassLoader被丢弃,重新创建一个restartClassLoader,由于需要加载的类相比较少,所以实现了较快的重启时间
Spring Boot开发环境热部署(HotSwap)详解
springboot-flowable
springboot-freemarker
| freemarker语法 | poi-tl文档 |
|---|
springboot-i18n
| 一探究竟:深度解析Java国际化底层类ResourceBundle | Spring Boot国际化踩坑指南 |
|---|
springboot-jasypt
| 两种方式,实现SpringBoot中数据库密码加密 | Spring Boot保护敏感配置的4种方法 | Spring Boot加密配置中的敏感信息 |
|---|---|---|
| Springboot配置文件、隐私数据脱敏的最佳实践 | Spring Boot配置文件这样加密,才足够安全! | 仅需三步完成SpringBoot日志脱敏 |
springboot-kafka
consumer
# 消费者所属消费组的唯一标识
group.id
# 一次拉取请求的最大消息数,默认500条
max.poll.records
# 指定拉取消息线程最长空闲时间,默认300000ms
max.poll.interval.ms
# 检测消费者是否失效的超时时间,默认10000ms
session.timeout.ms
# 消费者心跳时间,默认3000ms
heartbeat.interval.ms
# 连接集群broker地址
bootstrap.servers
# 是否开启自动提交消费位移的功能,默认true
enable.auto.commit
# 自动提交消费位移的时间间隔,默认5000ms
auto.commit.interval.ms
# 消费者的分区配置策略,默认RangeAssignor
partition.assignment.strategy
# 如果分区没有初始偏移量,或者当前偏移量服务器上不存在时,将使用的偏移量设置,earliest从头开始消费,latest从最近的开始消费,none抛出异常,如果存在已经提交的offest时,不管设置为earliest或者latest都会从已经提交的offest处开始消费,如果不存在已经提交的offest时,earliest表示从头开始消费,latest表示从最新的数据消费,也就是新产生的数据.none topic各分区都存在已提交的offset时,从提交的offest处开始消费;只要有一个分区不存在已提交的offset,则抛出异常.kafka-0.10.1.X版本之前: auto.offset.reset的值为smallest,和,largest.(offest保存在zk中).kafka-0.10.1.X版本之后: auto.offset.reset的值更改为:earliest,latest,和none(offest保存在kafka的一个特殊的topic,名为:__consumer_offsets里面)
auto.offset.reset
# 消费者客户端一次请求从Kafka拉取消息的最小数据量,如果Kafka返回的数据量小于该值,会一直等待,直到满足这个配置大小,默认1b
fetch.min.bytes
# 消费者客户端一次请求从Kafka拉取消息的最大数据量,默认50MB
fetch.max.bytes
# 从Kafka拉取消息时,在不满足fetch.min.bytes条件时,等待的最大时间,默认500ms
fetch.max.wait.ms
# 强制刷新元数据时间,毫秒,默认300000,5分钟
metadata.max.age.ms
# 设置从每个分区里返回给消费者的最大数据量,区别于fetch.max.bytes,默认1MB
max.partition.fetch.bytes
# Socket发送缓冲区大小,默认128kb,-1将使用操作系统的设置
send.buffer.bytes
# Socket发送缓冲区大小,默认64kb,-1将使用操作系统的设置
receive.buffer.bytes
# 消费者客户端的id
client.id
# 连接失败后,尝试连接Kafka的时间间隔,默认50ms
reconnect.backoff.ms
# 尝试连接到Kafka,生产者客户端等待的最大时间,默认1000ms
reconnect.backoff.max.ms
# 消息发送失败重试时间间隔,默认100ms
retry.backoff.ms
# 样本计算时间窗口,默认30000ms
metrics.sample.window.ms
# 用于维护metrics的样本数量,默认2
metrics.num.samples
# metrics日志记录级别,默认info
metrics.log.level
# 类的列表,用于衡量指标,默认空list
metric.reporters
# 自动检查CRC32记录的消耗
check.crcs
# key反序列化方式
key.deserializer
# value反序列化方式
value.deserializer
# 设置多久之后关闭空闲连接,默认540000ms
connections.max.idle.ms
# 客户端将等待请求的响应的最大时间,如果在这个时间内没有收到响应,客户端将重发请求,超过重试次数将抛异常,默认30000ms
request.timeout.ms
# 设置消费者api超时时间,默认60000ms
default.api.timeout.ms
# 自定义拦截器
interceptor.classes
# 内部的主题:consumer_offsets和一transaction_state。该参数用来指定Kafka中的内部主题是否可以向消费者公开,默认值为true。如果设置为true,那么只能使用subscribe(Collection)的方式而不能使用subscribe(Pattern)的方式来订阅内部主题,设置为false则没有这个限制。
exclude.internal.topics
# 用来配置消费者的事务隔离级别。如果设置为“read committed”,那么消费者就会忽略事务未提交的消息,即只能消费到LSO(LastStableOffset)的位置,默认情况下为“read_uncommitted”,即可以消费到HW(High Watermark)处的位置
isolation.level
key.deserializer = org.apache.kafka.common.serialization.StringDeserializer
value.deserializer = org.apache.kafka.common.serialization.StringDeserializer
producer
# Snappy压缩技术是Google开发的,它可以在提供较好的压缩比的同时,减少对CPU的使用率并保证好的性能,所以建议在同时考虑性能和带宽的情况下使用。Gzip压缩技术通常会使用更多的CPU和时间,但会产生更好的压缩比,所以建议在网络带宽更受限制的情况下使用,默认不压缩,该参数可以设置成snappy、gzip或lz4对发送给broker的消息进行压缩
compression.type=Gzip
# 请求的最大字节数。这也是对最大消息大小的有效限制。注意:server具有自己对消息大小的限制,这些大小和这个设置不同。此项设置将会限制producer每次批量发送请求的数目,以防发出巨量的请求。
max.request.size=1048576
# TCP的接收缓存SO_RCVBUF空间大小,用于读取数据
receive.buffer.bytes=32768
# client等待请求响应的最大时间,如果在这个时间内没有收到响应,客户端将重发请求,超过重试次数发送失败
request.timeout.ms=30000
# TCP的发送缓存SO_SNDBUF空间大小,用于发送数据
send.buffer.bytes=131072
# 指定server等待来自followers的确认的最大时间,根据acks的设置,超时则返回error
timeout.ms=30000
# 在block前一个connection上允许最大未确认的requests数量。当设为1时,即是消息保证有序模式,注意:这里的消息保证有序是指对于单个Partition的消息有顺序,因此若要保证全局消息有序,可以只使用一个Partition,当然也会降低性能
max.in.flight.requests.per.connection=5
# 在第一次将数据发送到某topic时,需先fetch该topic的metadata,得知哪些服务器持有该topic的partition,该值为最长获取metadata时间
metadata.fetch.timeout.ms=60000
# 连接失败时,当我们重新连接时的等待时间
reconnect.backoff.ms=50
# 在重试发送失败的request前的等待时间,防止若目的Broker完全挂掉的情况下Producer一直陷入死循环发送,折中的方法
retry.backoff.ms=100
# metrics系统维护可配置的样本数量,在一个可修正的window size
metrics.sample.window.ms=30000
# 用于维护metrics的样本数
metrics.num.samples=2
# 类的列表,用于衡量指标。实现MetricReporter接口
metric.reporters=[]
# 强制刷新metadata的周期,即使leader没有变化
metadata.max.age.ms=300000
# 与broker会话协议,取值:LAINTEXT,SSL,SASL_PLAINTEXT,SASL_SSL
security.protocol=PLAINTEXT
# 分区类,实现Partitioner接口
partitioner.class=class org.apache.kafka.clients.producer.internals.DefaultPartitioner
# 控制block的时长,当buffer空间不够或者metadata丢失时产生block
max.block.ms=60000
# 关闭达到该时间的空闲连接
connections.max.idle.ms=540000
# 当向server发出请求时,这个字符串会发送给server,目的是能够追踪请求源
client.id=""
# acks=0配置适用于实现非常高的吞吐量,acks=all这是最安全的模式。Server完成producer request前需要确认的数量。acks=0时,producer不会等待确认,直接添加到socket等待发送;acks=1时,等待leader写到local log就行;acks=all或acks=-1时,等待isr中所有副本确认(注意:确认都是broker接收到消息放入内存就直接返回确认,不是需要等待数据写入磁盘后才返回确认,这也是kafka快的原因)
acks = all
# 发生错误时,重传次数。当开启重传时,需要将`max.in.flight.requests.per.connection`设置为1,否则可能导致失序
retries = 0
# 发送到同一个partition的消息会被先存储在batch中,该参数指定一个batch可以使用的内存大小,单位是byte。不一定需要等到batch被填满才能发送Producer可以将发往同一个Partition的数据做成一个Produce Request发送请求,即Batch批处理,以减少请求次数,该值即为每次批处理的大小。另外每个Request请求包含多个Batch,每个Batch对应一个Partition,且一个Request发送的目的Broker均为这些partition的leader副本。若将该值设为0,则不会进行批处理
batch.size = 16384
# 生产者在发送消息前等待linger.ms,从而等待更多的消息加入到batch中。如果batch被填满或者linger.ms达到上限,就把batch中的消息发送出去,Producer默认会把两次发送时间间隔内收集到的所有Requests进行一次聚合然后再发送,以此提高吞吐量,而linger.ms则更进一步,这个参数为每次发送增加一些delay,以此来聚合更多的Message。官网解释翻译:producer会将request传输之间到达的所有records聚合到一个批请求。通常这个值发生在欠负载情况下,record到达速度快于发送。但是在某些场景下,client即使在正常负载下也期望减少请求数量。这个设置就是如此,通过人工添加少量时延,而不是立马发送一个record,producer会等待所给的时延,以让其他records发送出去,这样就会被聚合在一起。这个类似于TCP的Nagle算法。该设置给了batch的时延上限:当我们获得一个partition的batch.size大小的records,就会立即发送出去,而不管该设置;但是如果对于这个partition没有累积到足够的record,会linger指定的时间等待更多的records出现。该设置的默认值为0(无时延)。例如,设置linger.ms=5,会减少request发送的数量,但是在无负载下会增加5ms的发送时延。
linger.ms = 1
# Producer可以用来缓存数据的内存大小。该值实际为RecordAccumulator类中的BufferPool,即Producer所管理的最大内存。如果数据产生速度大于向broker发送的速度,producer会阻塞max.block.ms,超时则抛出异常
buffer.memory = 33554432
key.serializer = org.apache.kafka.common.serialization.StringSerializer
value.serializer = org.apache.kafka.common.serialization.StringSerializer
| Spring Kafka之@KafkaListener单条或批量处理消息 | SpringBoot整合Kafka实现千万级数据异步处理,实战介绍! |
|---|
springboot-lifecycle
Aware接口
每一个Aware的作用如下:
- ApplicationEventPublisherAware:实现该接口的对象可以获取事件发布的能力。
- ServletContextAware:实现该接口的对象可以获取到ServletContext对象。
- MessageSourceAware:实现该接口的对象可以获取到MessageSource对象,MessageSource支持多消息源,主要用于主要用于国际化。
- ResourceLoaderAware:实现该接口的对象可以获取到一个ResourceLoader,Spring ResourceLoader则为我们提供了一个统一的getResource()方法来通过资源路径检索外部资源,例如文本文件、XML文件、属性文件或图像文件等。
- ApplicationStartupAware:实现该接口的对象可以获取到一个ApplicationStartup对象,这个比较新,是Spring5.3中新推出的,通过ApplicationStartup可以标记应用程序启动期间的步骤,并收集有关执行上下文或其处理时间的数据。
- NotificationPublisherAware:实现该接的对象可以获取到一个NotificationPublisher对象,通过该对象可以实现通知的发送。
- EnvironmentAware:实现该接口的对象可以获取到一个Environment对象,通过Environment可以获取到容器的环境信息。
- BeanFactoryAware:实现该接口的对象可以获取到一个BeanFactory对象,通过BeanFactory可以完成Bean的查询等操作。
- ImportAware:实现该接口的对象可以获取到一个AnnotationMetadata对象,ImportAware接口是需要和@Import注解一起使用的。在@Import作为元注解使用时,通过@Import导入的配置类如果实现了ImportAware接口就可以获取到导入该配置类接口的数据配置。
- EmbeddedValueResolverAware:实现该接口的对象可以获取到一个StringValueResolver对象,通过StringValueResolver对象,可以读取到Spring容器中的properties配置的值(YAML配置也可以)。
- ServletConfigAware:实现该接口的对象可以获取到一个ServletConfig对象,不过这个似乎没什么用,我们很少自己去配置ServletConfig。
- LoadTimeWeaverAware:实现该接口的对象可以获取到一个LoadTimeWeaver对象,通过该对象可以获取加载Spring Bean时织入的第三方模块,如AspectJ等。
- BeanClassLoaderAware:实现该接口的对象可以获取到一个ClassLoader对象。
- BeanNameAware:实现该接口的对象可以获取到一个当前Bean的名称。
- ApplicationContextAware:实现该接口的对象可以获取到一个ApplicationContext对象,通过-ApplicationContext可以获取容器中的Bean、环境等信息。
Spring Boot监听器
Spring Boot启动事件顺序ApplicationListener中的泛型的类型,可以在MyEvent里面实现某一个接口来监听不同的事件
- ApplicationStartingEvent
这个事件在Spring Boot应用运行开始时,且进行任何处理之前发送(除了监听器和初始化器注册之外)。 - ApplicationEnvironmentPreparedEvent
这个事件在当已知要在上下文中使用Spring环境(Environment)时,在Spring上下文(context)创建之前发送。 - ApplicationContextInitializedEvent
这个事件在当Spring应用上下文(ApplicationContext)准备好了,并且应用初始化器(ApplicationContextInitializers)已经被调用,在bean的定义(bean definitions)被加载之前发送。 - ApplicationPreparedEvent
这个事件是在Spring上下文(context)刷新之前,且在bean的定义(bean definitions)被加载之后发送。 - ApplicationStartedEvent
这个事件是在Spring上下文(context)刷新之后,且在application/command-line runners被调用之前发送。 - AvailabilityChangeEvent
这个事件紧随上个事件之后发送,状态:ReadinessState.CORRECT,表示应用已处于活动状态。 - ApplicationReadyEvent
这个事件在任何application/ command-line runners调用之后发送。 - AvailabilityChangeEvent
这个事件紧随上个事件之后发送,状态:ReadinessState.ACCEPTING_TRAFFIC,表示应用可以开始准备接收请求了。 - ApplicationFailedEvent
这个事件在应用启动异常时进行发送
注册监听器方式1:implements ApplicationListener<T>接口在类上增加@Component注册bean
自定义事件代码如下:
@Component
public class CustomListener implements ApplicationListener<MyEvent>{
@Override
public void onApplicationEvent(MyEvent myEvent) {
myEvent.printMsg();
}
}
@SuppressWarnings("serial")
public class MyEvent extends ApplicationEvent{
public MyEvent(Object source){super(source);}
}
注册监听器方式2:
使用监听器的时候如果不将监听器的类注册为spring bean则使用这种方式添加
ConfigurableApplicationContext context = SpringApplication.run(XmxeApplication.class, args);
// 装载监听
context.addApplicationListener(new com.xmxe.config.listen.CustomListener());
注册监听器方式3:
在application.properties中配置监听context.listener.classes=com.xmxe.config.listen.CustomListener
注册监听器方式4:
创建MyListener4类,该类无需实现ApplicationListener接口,使用@EventListener装饰具体方法
@Component
public class MyListener4{
Logger logger = Logger.getLogger(MyListener4.class);
@EventListener
public void listener(MyEvent event){
logger.info(String.format("%s监听到事件源:%s.", MyListener4.class.getName(), event.getSource()));}
}
// 进行测试(在启动类中加入发布事件的逻辑):
@SpringBootApplication
public class LisenterApplication{
public static void main(String[] args){
ConfigurableApplicationContext context = SpringApplication.run(LisenterApplication.class, args);
//装载事件
context.addApplicationListener(new MyListener1());
//发布事件
context.publishEvent(new MyEvent("测试事件."));}
}
ConfigurableApplicationContext方法
ConfigurableApplicationContext即为SpringApplication.run()返回值
static interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
/**
* 应用上下文配置时,这些符号用于分割多个配置路径
*/
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
/**
* BeanFactory中,ConversionService类所对应的bean的名字。如果没有此类的实例的话则使用默认的转换规则
*/
String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
/**
* LoadTimeWaver类所对应的Bean在容器中的名字。如果提供了该实例,上下文会使用临时的ClassLoader,这样,LoadTimeWaver就可以使用bean确切的类型了
*/
String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
/**
* Environment类在容器中实例的名字
*/
String ENVIRONMENT_BEAN_NAME = "environment";
/**
* System系统变量在容器中对应的Bean的名字
*/
String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
/**
* System环境变量在容器中对应的Bean的名字
*/
String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
/**
* 设置容器的唯一ID
*/
void setId(String id);
/**
* 设置此容器的父容器,需要注意的是,父容器一经设定就不应该修改。并且一般不会在构造方法中对其进行配置,因为很多时候其父容器还不可用。比如WebApplicationContext。
*/
void setParent(ApplicationContext parent);
/**
* 设置容器的Environment变量
*/
void setEnvironment(ConfigurableEnvironment environment);
/**
* 以COnfigurableEnvironment的形式返回此容器的环境变量。以使用户更好的进行配置
*/
@Override
ConfigurableEnvironment getEnvironment();
/**
* 此方法一般在读取应用上下文配置的时候调用,用以向此容器中增加BeanFactoryPostProcessor。增加的Processor会在容器refresh的时候使用。
*/
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
/**
* 向容器增加一个ApplicationListener,增加的Listener用于发布上下文事件如refresh和shutdown等,需要注意的是,如果此上下文还没有启动,那么在此注册的Listener将会在上下文refresh的时候,全部被调用,如果上下文已经是active状态的了,就会在multicaster中使用
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* 向容器中注入给定的Protocol resolver,允许多个实例同时存在。在此注册的每一个resolver都将会在上下的标准解析规则之前使用。因此,某种程度上来说这里注册的resolver可以覆盖上下文的resolver
*/
void addProtocolResolver(ProtocolResolver resolver);
/**
* 加载资源配置文件(XML、properties,Whatever)。由于此方法是一个初始化方法,因此如果调用此方法失败的情况下,要将其已经创建的Bean销毁。换句话说,调用此方法以后,要么所有的Bean都实例化好了,要么就一个都没有实例化
*/
void refresh() throws BeansException, IllegalStateException;
/**
* 向JVM注册一个回调函数,用以在JVM关闭时,销毁此应用上下文。
*/
void registerShutdownHook();
/**
* 关闭此应用上下文,释放其所占有的所有资源和锁。并销毁其所有创建好的singleton Beans,实现的时候,此方法不应该调用其父上下文的close方法,因为其父上下文具有自己独立的生命周期.多次调用此方法,除了第一次,后面的调用应该被忽略。
*/
@Override
void close();
/**
* 检测此FactoryBean是否被启动过。
*/
boolean isActive();
/**
* 返回此应用上下文的容器。千万不要使用此方法来对BeanFactory生成的Bean做后置处理,因为单例Bean在此之前已经生成,这种情况下应该使用BeanFactoryPostProcessor来在Bean生成之前对其进行处理。通常情况下,内容容器只有在上下文是激活的情况下才能使用。因此,在使用此方法前,可以调用isActive来判断容器是否可用
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
扩展接口
可扩展的接口启动调用顺序图
ApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer
这是整个spring容器在刷新之前初始化ConfigurableApplicationContext的回调接口,简单来说,就是在容器刷新之前调用此类的initialize方法。这个点允许被用户自己扩展。用户可以在整个spring容器还没被初始化之前做一些事情。可以想到的场景可能为,在最开始激活一些配置,或者利用这时候class还没被类加载器加载的时机,进行动态字节码注入等操作。
扩展方式为:
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("[ApplicationContextInitializer]");
}
}
因为这时候spring容器还没被初始化,所以想要自己的扩展的生效,有以下三种方式:
- 在启动类中用
springApplication.addInitializers(new TestApplicationContextInitializer())语句加入 - 配置文件配置
context.initializer.classes=com.example.demo.TestApplicationContextInitializer - Spring SPI扩展,在spring.factories中加入
org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer
BeanDefinitionRegistryPostProcessor
org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor
这个接口在读取项目中的beanDefinition之后执行,提供一个补充的扩展点
使用场景:你可以在这里动态注册自己的beanDefinition,可以加载classpath之外的bean
扩展方式为:
public class TestBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("[BeanDefinitionRegistryPostProcessor]postProcessBeanDefinitionRegistry");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("[BeanDefinitionRegistryPostProcessor] postProcessBeanFactory");
}
}
BeanFactoryPostProcessor
org.springframework.beans.factory.config.BeanFactoryPostProcessor
这个接口是beanFactory的扩展接口,调用时机在spring在读取beanDefinition信息之后,实例化bean之前。在这个时机,用户可以通过实现这个扩展接口来自行处理一些东西,比如修改已经注册的beanDefinition的元信息。
扩展方式为:
public class TestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("[BeanFactoryPostProcessor]");
}
}
InstantiationAwareBeanPostProcessor
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
该接口继承了BeanPostProcess接口,区别如下:
BeanPostProcess接口只在bean的初始化阶段进行扩展(注入spring上下文前后),而InstantiationAwareBeanPostProcessor接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段。
该类主要的扩展点有以下5个方法,主要在bean生命周期的两大阶段:实例化阶段和初始化阶段,下面一起进行说明,按调用顺序为:
postProcessBeforeInstantiation:实例化bean之前,相当于new这个bean之前postProcessAfterInstantiation:实例化bean之后,相当于new这个bean之后postProcessPropertyValues:bean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现postProcessBeforeInitialization:初始化bean之前,相当于把bean注入spring上下文之前postProcessAfterInitialization:初始化bean之后,相当于把bean注入spring上下文之后
使用场景:这个扩展点非常有用,无论是写中间件和业务中,都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行统一的设值等等。
扩展方式为:
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);
return bean;
}
@Override
public Object postProcessBeforeInstantiation(Class throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);
return true;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName);
return pvs;
}
SmartInstantiationAwareBeanPostProcessor
org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor
该扩展接口有3个触发点方法:
predictBeanType:该触发点发生在postProcessBeforeInstantiation之前(在图上并没有标明,因为一般不太需要扩展这个点),这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。determineCandidateConstructors:该触发点发生在postProcessBeforeInstantiation之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。getEarlyBeanReference:该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。
扩展方式为:
public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
@Override
public Class predictBeanType(Class<?> var1, String var2) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName);
return beanClass;
}
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> var1,String var2) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName);
return null;
}
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName);
return bean;
}
}
BeanFactoryAware
org.springframework.beans.factory.BeanFactoryAware
这个类只有一个触发点,发生在bean的实例化之后,注入属性之前,也就是Setter之前。这个类的扩展点方法为setBeanFactory,可以拿到BeanFactory这个属性。使用场景为,你可以在bean实例化之后,但还未初始化之前,拿到BeanFactory,在这个时候,可以对每个bean作特殊化的定制。也或者可以把BeanFactory拿到进行缓存,日后使用。
扩展方式为:
public class TestBeanFactoryAware implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());
}
}
ApplicationContextAwareProcessor
org.springframework.context.support.ApplicationContextAwareProcessor
该类本身并没有扩展点,但是该类内部却有6个扩展点可供实现,这些类触发的时机在bean实例化之后,初始化之前
可以看到,该类用于执行各种驱动接口,在bean实例化之后,属性填充之后,通过执行以上红框标出的扩展接口,来获取对应容器的变量。所以这里应该来说是有6个扩展点,这里就放一起来说了
EnvironmentAware:用于获取EnviromentAware的一个扩展类,这个变量非常有用,可以获得系统内的所有参数。当然个人认为这个Aware没必要去扩展,因为spring内部都可以通过注入的方式来直接获得。EmbeddedValueResolverAware:用于获取StringValueResolver的一个扩展类,StringValueResolver用于获取基于String类型的properties的变量,一般我们都用@Value的方式去获取,如果实现了这个Aware接口,把StringValueResolver缓存起来,通过这个类去获取String类型的变量,效果是一样的。ResourceLoaderAware:用于获取ResourceLoader的一个扩展类,ResourceLoader可以用于获取classpath内所有的资源对象,可以扩展此类来拿到ResourceLoader对象。ApplicationEventPublisherAware:用于获取ApplicationEventPublisher的一个扩展类,ApplicationEventPublisher可以用来发布事件,结合ApplicationListener来共同使用,下文在介绍ApplicationListener时会详细提到。这个对象也可以通过spring注入的方式来获得。MessageSourceAware:用于获取MessageSource的一个扩展类,MessageSource主要用来做国际化。ApplicationContextAware:用来获取ApplicationContext的一个扩展类,ApplicationContext应该是很多人非常熟悉的一个类了,就是spring上下文管理器,可以手动的获取任何在spring上下文注册的bean,我们经常扩展这个接口来缓存spring上下文,包装成静态方法。同时ApplicationContext也实现了BeanFactory,MessageSource,ApplicationEventPublisher等接口,也可以用来做相关接口的事情。
BeanNameAware
org.springframework.beans.factory.BeanNameAware
可以看到,这个类也是Aware扩展的一种,触发点在bean的初始化之前,也就是postProcessBeforeInitialization之前,这个类的触发点方法只有一个:setBeanName
使用场景为:用户可以扩展这个点,在初始化bean之前拿到spring容器中注册的的beanName,来自行修改这个beanName的值。
扩展方式为:
public class NormalBeanA implements BeanNameAware{
public NormalBeanA() {
System.out.println("NormalBean constructor");
}
@Override
public void setBeanName(String name) {
System.out.println("[BeanNameAware] " + name);
}
}
@PostConstruct
javax.annotation.PostConstruct
这个并不算一个扩展点,其实就是一个标注。其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是在postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前。
使用场景:用户可以对某一方法进行标注,来进行初始化某一个属性
扩展方式为:
public class NormalBeanA {
public NormalBeanA() {
System.out.println("NormalBean constructor");
}
@PostConstruct
public void init(){
System.out.println("[PostConstruct] NormalBeanA");
}
}
InitializingBean
org.springframework.beans.factory.InitializingBean
这个类,顾名思义,也是用来初始化bean的。InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。这个扩展点的触发时机在postProcessAfterInitialization之前。
使用场景:用户实现此接口,来进行系统启动的时候一些业务指标的初始化工作。
扩展方式为:
public class NormalBeanA implements InitializingBean{
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("[InitializingBean] NormalBeanA");
}
}
FactoryBean
org.springframework.beans.factory.FactoryBean
一般情况下,Spring通过反射机制利用bean的class属性指定支线类去实例化bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在bean中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占有重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式
使用场景:用户可以扩展这个类,来为要实例化的bean作一个代理,比如为该对象的所有的方法作一个拦截,在调用前后输出一行log,模仿ProxyFactoryBean的功能。
扩展方式为:
public class TestFactoryBean implements FactoryBean<TestFactoryBean.TestFactoryInnerBean> {
@Override
public TestFactoryBean.TestFactoryInnerBean getObject() throws Exception {
System.out.println("[FactoryBean] getObject");
return new TestFactoryBean.TestFactoryInnerBean();
}
@Override
public Class getObjectType() {
return TestFactoryBean.TestFactoryInnerBean.class;
}
@Override
public boolean isSingleton() {
return true;
}
public static class TestFactoryInnerBean{
}
}
SmartInitializingSingleton
org.springframework.beans.factory.SmartInitializingSingleton
这个接口中只有一个方法afterSingletonsInstantiated,其作用是在spring容器管理的所有单例对象(非懒加载对象)初始化完成之后调用的回调接口。其触发时机为postProcessAfterInitialization之后。
使用场景:用户可以扩展此接口在对所有单例对象初始化完毕后,做一些后置的业务处理。
扩展方式为:
public class TestSmartInitializingSingleton implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.out.println("[TestSmartInitializingSingleton]");
}
}
CommandLineRunner
org.springframework.boot.CommandLineRunner
这个接口也只有一个方法:run(String... args),触发时机为整个项目启动完毕后,自动执行。如果有多个CommandLineRunner,可以利用@Order来进行排序。
使用场景:用户扩展此接口,进行启动项目之后一些业务的预处理。
扩展方式为:
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("[TestCommandLineRunner]");
}
}
DisposableBean
org.springframework.beans.factory.DisposableBean
这个扩展点也只有一个方法:destroy(),其触发时机为当此对象销毁时,会自动执行这个方法。比如说运行applicationContext.registerShutdownHook时,就会触发这个方法。
扩展方式为:
public class NormalBeanA implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("[DisposableBean] NormalBeanA");
}
}
ApplicationListener
org.springframework.context.ApplicationListener
准确的说,这个应该不算spring&springboot当中的一个扩展点,ApplicationListener可以监听某个事件的event,触发时机可以穿插在业务方法执行过程中,用户可以自定义某个业务事件。但是spring内部也有一些内置事件,这种事件,可以穿插在启动调用中。我们也可以利用这个特性,来自己做一些内置事件的监听器来达到和前面一些触发点大致相同的事情。
接下来罗列下spring主要的内置事件:
ContextRefreshedEvent
ApplicationContext被初始化或刷新时,该事件被发布。这也可以在
ConfigurableApplicationContext接口中使用refresh()方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean被预实例化,ApplicationContext容器已就绪可用。ContextStartedEvent
当使用
ConfigurableApplicationContext(ApplicationContext子接口)接口中的start()方法启动ApplicationContext时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。ContextStoppedEvent
当使用
ConfigurableApplicationContext接口中的stop()停止ApplicationContext时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作ContextClosedEvent
当使用
ConfigurableApplicationContext接口中的close()方法关闭ApplicationContext时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启RequestHandledEvent
这是一个web-specific事件,告诉所有bean HTTP请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件
牢记这16个SpringBoot扩展接口,写出更加漂亮的代码
相关文章
springboot-starter
spring.factory在spring boot3.0移除的解决办法
如果你有探索过这些Starter的原理,那你一定知道Spring Boot并没有消灭这些原本你要配置的Bean,而是将这些Bean做成了一些默认的配置类,同时利用/META-INF/spring.factories这个文件来指定要加载的默认配置。这样当Spring Boot应用启动的时候,就会根据引入的各种Starter中的/META-INF/spring.factories文件所指定的配置类去加载Bean。而Spring Boot 2.7中,有一个不推荐使用的内容就是关于这个/META-INF/spring.factories文件的,在Spring Boot 3开始将移除对/META-INF/spring.factories的支持。
那么具体怎么改呢?下面以之前我们编写的一个swagger的starter为例,它的/META-INF/spring.factories内容是这样的:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.spring4all.swagger.SwaggerAutoConfiguration
我们只需要创建一个新的文件:/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,内容的话只需要直接放配置类就可以了,比如这样:
com.spring4all.swagger.SwaggerAutoConfiguration
注意:这里多了一级spring目录。
如果你觉得维护这个太麻烦的话,还可以使用mica-auto来让他自动生成,具体怎么用可以看之前发的这篇文章。
文章
springboot-state-machine
| 项目终于用上了Spring状态机,非常优雅! | mybatis-plus文档 | mybatis-plus-samples |
|---|
springboot-upload
| Spring Boot项目超大文件上传时,如何实现秒传? | 大文件上传下载实现思路,分片、断点续传代码实现 | Spring Boot多线程异步上传图片、处理水印、缩略图 |
|---|---|---|
| Spring Boot分片上传、断点续传、大文件极速秒传 | Spring Boot实现文件断点下载,实战来了! |
springboot-websocket
SpringBoot
SpringApplication.run()(3.0版本)
// SpringApplication构造方法
new SpringApplication(Application.class){
this((ResourceLoader)null, primarySources);
}
// 创建一个新的实例,这个应用程序的上下文将要从指定的来源加载Bean
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 资源初始化资源加载器,默认为null
this.resourceLoader = resourceLoader;
// 断言主要加载资源类不能为null,否则报错
Assert.notNull(primarySources, "PrimarySources must not be null");
// 初始化主要加载资源类集合并去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断当前 WEB 应用类型,一共有三种:NONE(非web项目),SERVLET(servlet web项目),REACTIVE(响应式web项目)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 设置应用上线文初始化器,从"META-INF/spring.factories"读取ApplicationContextInitializer类的实例名称集合并去重,并进行set去重。(一共7个)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置监听器,从"META-INF/spring.factories"读取ApplicationListener类的实例名称集合并去重,并进行set去重。(一共11个)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断主入口应用类,通过当前调用栈,获取Main方法所在类,并赋值给mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass();
}
// run()方法
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
this.configureHeadlessProperty();
// 创建所有spring运行监听器并发布应用启动事件,加载所有SpringApplicationRunListener的实现类
SpringApplicationRunListeners listeners = getRunListeners(args);
// 调用了starting
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 创建ApplicationArguments对象,获取应用程序启动参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 根据运行监听器和应用参数来准备spring环境,自定义监听器加载配置信息和系统环境变量,调用了environmentPrepared
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
Banner printedBanner = printBanner(environment);
// 创建Spring上下文并加载Bean
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 准备ApplicationContext,该步骤包含一个非常关键的操作,将启动类注入容器,为后续开启自动化提供基础,内部调用了contextPrepared、contextLoaded
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新应用上下文(自动装配,初始化IOC容器)
this.refreshContext(context);
// 应用上下文刷新后置处理,做一些扩展功能
this.afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
// 执行所有的Runner运行器,调用了started
listeners.started(context, timeTakenToStartup);
// 执行所有的Runner运行器
callRunners(context, applicationArguments);
} catch (Throwable var12) {
if (var12 instanceof AbandonedRunException) {
throw var12;
}
// 内部调用了failed
this.handleRunFailure(context, var12, listeners);
throw new IllegalStateException(var12);
}
try {
if (context.isRunning()) {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 发布应用上下文就绪事件,调用了reday
listeners.ready(context, timeTakenToReady);
}
// 返回应用上下文
return context;
} catch (Throwable var11) {
if (var11 instanceof AbandonedRunException) {
throw var11;
} else {
this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var11);
}
}
}
public interface SpringApplicationRunListener {
// run方法第一次被执行时调用,早期初始化工作
void starting();
// environment创建后,ApplicationContext创建前
void environmentPrepared(ConfigurableEnvironment environment);
// ApplicationContext实例创建,部分属性设置了
void contextPrepared(ConfigurableApplicationContext context);
// ApplicationContext加载后,refresh前
void contextLoaded(ConfigurableApplicationContext context);
// refresh后
void started(ConfigurableApplicationContext context);
// 所有初始化完成后,run结束前
void running(ConfigurableApplicationContext context);
// 初始化失败后
void failed(ConfigurableApplicationContext context, Throwable exception);
}
Spring MVC配置相关
Spring Boot中,SpringMVC相关的自动化配置是在WebMvcAutoConfiguration配置类中实现的,它的生效条件有一条,就是当不存在WebMvcConfigurationSupport的实例时,这个自动化配置才会生生效。因此,如果我们在Spring Boot中自定义SpringMVC配置时选择了继承WebMvcConfigurationSupport,就会导致Spring Boot中SpringMVC的自动化配置失效。Spring Boot给我们提供了很多自动化配置,很多时候当我们修改这些配置的时候,并不是要全盘否定Spring Boot提供的自动化配置,我们可能只是针对某一个配置做出修改,其他的配置还是按照Spring Boot默认的自动化配置来,而继承WebMvcConfigurationSupport来实现对SpringMVC的配置会导致所有的SpringMVC自动化配置失效,因此,一般情况下我们不选择这种方案。
若直接继承WebMvcConfigurationSupport,会导致application.properties配置文件不生效,需要增加以下等相关配置代替配置文件
例:@Bean public InternalResourceViewResolver resourceViewResolver(){ InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); // 请求视图文件的前缀地址 internalResourceViewResolver.setPrefix("/WEB-INF/views/"); // 请求视图文件的后缀 internalResourceViewResolver.setSuffix(".jsp"); return internalResourceViewResolver; } @Override public void configureViewResolvers(ViewResolverRegistry registry) { super.configureViewResolvers(registry); registry.viewResolver(resourceViewResolver()); registry.jsp("/WEB-INF/views/",".jsp"); }
在SpringBoot2.0及Spring 5.0WebMvcConfigurerAdapter已被废弃,标记为过时。使用方式:
- 直接实现WebMvcConfigurer(官方推荐)WebMvcConfigurer详解
- 直接继承WebMvcConfigurationSupport(继承此方法会导致application.properties不生效)
// WebMvcConfigurer常用的方法
// 解决跨域问题
public void addCorsMappings(CorsRegistry registry) ;
// 添加拦截器
void addInterceptors(InterceptorRegistry registry);
// 这里配置视图解析器
void configureViewResolvers(ViewResolverRegistry registry);
// 配置内容裁决的一些选项
void configureContentNegotiation(ContentNegotiationConfigurer configurer);
// 视图跳转控制器
void addViewControllers(ViewControllerRegistry registry);
// 静态资源处理
void addResourceHandlers(ResourceHandlerRegistry registry);
// 默认静态资源处理器
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
跟自定义SpringMVC相关的类和注解主要有如下四个:
- WebMvcConfigurerAdapter
- WebMvcConfigurer
- WebMvcConfigurationSupport
- @EnableWebMvc
Spring Boot1.x中,自定义SpringMVC配置可以通过继承WebMvcConfigurerAdapter来实现。
Spring Boot2.x中,自定义SpringMVC配置可以通过实现WebMvcConfigurer接口来完成。
如果在Spring Boot中使用继承WebMvcConfigurationSupport来实现自定义SpringMVC配置,或者在Spring Boot中使用了@EnableWebMvc注解,都会导致Spring Boot中默认的SpringMVC自动化配置失效。在纯Java配置的SSM环境中,如果我们要自定义SpringMVC配置,有两种办法,第一种就是直接继承自WebMvcConfigurationSupport来完成SpringMVC配置,还有一种方案就是实现WebMvcConfigurer接口来完成自定义SpringMVC配置,如果使用第二种方式,则需要给SpringMVC的配置类上额外添加@EnableWebMvc注解,表示启用WebMvcConfigurationSupport,这样配置才会生效。换句话说,在纯Java配置的SSM中,如果你需要自定义SpringMVC配置,你离不开WebMvcConfigurationSupport,所以在这种情况下建议通过继承WebMvcConfigurationSupport来实现自动化配置。
扫描注解的几种方式
- implements SmartInitializingSingleton
String[] beanDefinitionsNames=applicationContext.getBeanNamesForType(LifeCycleController.class,false,true);
for (String beanDefinitionName : beanDefinitionsNames){
Object bean = applicationContext.getBean(beanDefinitionName);
// map = 注解及注解所在的方法
Map<Method, InvokeMethod> annotatedMethods = null;
try{
// 查找bean里指定注解的方法
annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(), new MethodIntrospector.MetadataLookup<InvokeMethod>() {
@Override
public InvokeMethod inspect(Method method) {
return AnnotatedElementUtils.findMergedAnnotation(method,InvokeMethod.class);
}
});
}catch (Exception e){
e.printStackTrace();
}
- implements BeanPostProcessor
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
for (Method method : methods) {
MsgEvent myMsgEvent = AnnotationUtils.findAnnotation(method, MsgEvent.class)
}
HandlerMethod.getMethodAnnotation(Class<A> annotationType),底层AnnotatedElementUtils.findMergedAnnotation(method,InvokeMethod.class)实现HandlerMethodSelector
在spring5.0中废弃
Class<?> handlerType = applicationContext.getType(beanName);
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> = HandlerMethodSelector.selectMethods(userType, new ReflectionUtils.MethodFilter() {
public boolean matches(Method method) {
OpanApi methodAnnotation = AnnotationUtils.findAnnotation(method, OpanApi.class);
if (methodAnnotation != null) {
return true;
}
return false;
}
});
相关文章
springcloud-config
springcloud-config-server
客户端通过直接调用配置中心的server端来获取配置文件信息。但是存在了一个问题,客户端和服务端的耦合性太高,如果server端要做集群,客户端只能通过原始的方式来路由,server端改变IP地址的时候,客户端也需要修改配置,不符合springcloud服务治理的理念。
springcloud提供了这样的解决方案,我们只需要将server端当做一个服务注册到eureka中,client端去eureka中去获取配置中心server端的服务既可。与之前配置相比主要是去掉了spring.cloud.config.uri直接指向server端地址的配置,增加了最后的三个配置:
- spring.cloud.config.discovery.enabled :开启Config服务发现支持
- spring.cloud.config.discovery.serviceId :指定server端的name,也就是server端spring.application.name的值
- eureka.client.serviceUrl.defaultZone :指向注册中心的地址
config server集成spring-cloud-starter-bus-kafka和spring-boot-starter-actuator
在远程仓库修改配置文件的时候通过 http://config-server:port/actuator/bus-refresh 来刷新所有springcloud-config-client,无须重启springcloud-config-client,也无须在springcloud-config-client上一个一个单独通过 http://configclientID:port/actuator/refresh 来刷新config-client的数据。
借助Git仓库的WebHook,我们就可轻松实现配置的自动刷新
springcloud-consul
| Consul下载 | Consul文档 | Consul Github |
|---|
springcloud-gateway
微服务为什么要使用网关
不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:
- 客户端会多次请求不同的微服务,增加了客户端的复杂性
- 存在跨域请求,在一定场景下处理相对复杂
- 认证复杂,每个服务都需要独立认证
- 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将会很难实施
- 某些微服务可能使用了防火墙 / 浏览器不友好的协议,直接访问会有一定的困难
以上这些问题可以借助网关解决。
微服务网关的优点
- 安全 ,只有网关系统对外进行暴露,微服务可以隐藏在内网,通过防火墙保护。
- 易于监控。可以在网关收集监控数据并将其推送到外部系统进行分析。
- 易于认证。可以在网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
- 减少了客户端与各个微服务之间的交互次数
- 易于统一授权。
- 网关的作用:转发功能,熔断功能,限流功能
文章
| 网关系统就该这么设计(万能通用) | 为什么我们的微服务中需要网关? | Spring Cloud 微服务网关Gateway使用详解 |
|---|---|---|
| Spring Cloud Gateway在微服务架构下的最佳实践 |
springcloud-openfeign
| Feign调用常见问题避坑指南! |
|---|
springcloud-shenyu
| GitHub ShenYu | Apache ShenYu文档 | ShenYu Admin\BootStrap二进制包下载地址(不使用源码启动的话直接下载包启动) |
|---|
springcloud-tencent
| 北极星官网 | 北极星下载地址 |
|---|