一、@RefreshScope动态刷新原理
在SpringIOC中,BeanScope(Bean的作用域)影响了Bean的管理方式。
Bean的作用域:
例如创建Scope=singleton
的Bean时,IOC会保存实例在一个Map中,保证这个Bean在一个IOC上下文有且仅有一个实例。
SpringCloud新增了一个自定义的作用域:refresh(可以理解为“动态刷新”),同样用了一种独特的方式改变了Bean的管理方式,使得其可以通过外部化配置(.properties
)的刷新,在应用不需要重启的情况下热加载新的外部化配置的值。
这个scope是如何做到热加载的呢?RefreshScope
主要做了以下动作:
单独管理Bean生命周期
创建Bean的时候如果是RefreshScope
就缓存在一个专门管理的ScopeMap
中,这样就可以管理Scope是Refresh的Bean的生命周期了(所以含RefreshScope
的其实一共创建了两个bean)。
重新创建Bean
外部化配置刷新之后,会触发一个动作,这个动作将上面的ScopeMap
中的Bean清空,这样这些Bean就会重新被IOC容器创建一次,使用最新的外部化配置的值注入类中,达到热加载新值的效果。
spring cloud config
或sprring cloud alibaba nacos
作为配置中心,其实现原理就是通过@RefreshScope
来实现对象属性的的动态更新。
@RefreshScope
实现配置的动态刷新需要满足一下几点条件:
-
@Scope注解
-
@RefreshScope注解
-
RefreshScope类
-
GenericScope类
-
Scope接口
-
ContextRefresher类
@RefreshScope 能实现动态刷新全仰仗着@Scope 这个注解。
1. @Scope注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
/**
* Alias for {@link #scopeName}.
* @see #scopeName
*/
@AliasFor("scopeName")
String value() default "";
/**
* singleton 表示该bean是单例的。(默认)
* prototype 表示该bean是多例的,即每次使用该bean时都会新建一个对象。
* request 在一次http请求中,一个bean对应一个实例。
* session 在一个httpSession中,一个bean对应一个实例
*/
@AliasFor("value")
String scopeName() default "";
/**
* DEFAULT 不使用代理。(默认)
* NO 不使用代理,等价于DEFAULT。
* INTERFACES 使用基于接口的代理(jdk dynamic proxy)。
* TARGET_CLASS 使用基于类的代理(cglib)。
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
@Scope
有两个主要属性value 和 proxyMode
,其中proxyMode
就是@RefreshScope
实现的本质了。
proxyMode
属性是一个ScopedProxyMode
类型的枚举对象。
public enum ScopedProxyMode {
DEFAULT,
NO,
INTERFACES,// JDK 动态代理
TARGET_CLASS;// CGLIB 动态代理
private ScopedProxyMode() {
}
}
当proxyMode
属性的值为ScopedProxyMode.TARGET_CLASS
时,会给当前创建的bean 生成一个代理对象,会通过代理对象来访问,每次访问都会创建一个新的对象。
2. @RefreshScope注解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Scope("refresh")
@Documented
public @interface RefreshScope {
/**
* @see Scope#proxyMode()
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
它使用就是 @Scope
,一个scopeName="refresh"
的@Scope
。
proxyMode
值为ScopedProxyMode.TARGET_CLASS
,通过CGLIB
动态代理的方式生成Bean。
使用 @RefreshScope
注解的 bean,不仅会生成一个beanName
的bean,默认情况下同时会生成 scopedTarget.beanName
的 bean。