RefeshScope这个注解想必大家都用过,在微服务配置中心的场景下经常出现,他可以用来刷新Bean中的属性配置,那大家对他的实现原理了解吗?它为什么可以做到动态刷新呢?
@RefreshScope
注解是Spring Cloud中的一个注解,用来实现Bean中属性的动态刷新。
- /**
- * Convenience annotation to put a
@Bean
definition in - * {@link org.springframework.cloud.context.scope.refresh.RefreshScope refresh scope}.
- * Beans annotated this way can be refreshed at runtime and any components that are using
- * them will get a new instance on the next method call, fully initialized and injected
- * with all dependencies.
- *
- * @author Dave Syer
- *
- */
- @Target({ ElementType.TYPE, ElementType.METHOD })
- @Retention(RetentionPolicy.RUNTIME)
- @Scope("refresh")
- @Documented
- public @interface RefreshScope {
-
- /**
- * @see Scope#proxyMode()
- * @return proxy mode
- */
- ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
-
- }
- 复制代码
@RefreshScope
@RefreshScope
就不会自动刷新。代码地址: github.com/alvinlkk/aw…
为了实现动态刷新配置,主要就是想办法达成以下两个核心目标:
@RefreshScope
主要就是基于@Scope
注解的作用域代理的基础上进行扩展实现的,加了@RefreshScope
注解的类,在被Bean工厂创建后会加入自己的refresh scope 这个Bean缓存中,后续会优先从Bean缓存中获取,当配置中心发生了变更,会把变更的配置更新到spring容器的Environment中,并且同事bean缓存就会被清空,从而就会从bean工厂中创建bean实例了,而这次创建bean实例的时候就会继续经历这个bean的生命周期,使得@Value属性值能够从Environment中获取到最新的属性值,这样整个过程就达到了动态刷新配置的效果。
如果对Scope注解不清楚的可以阅读这篇文章:【Spring注解必知必会】@Scope注解源码解析
通过打上断点查看堆栈可知:
@RefreshScope
注解,那么这个BeanDefinition信息中的scope为refresh,在getBean的的时候会单独处理逻辑。- public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
-
- protected <T> T doGetBean(
- String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
- throws BeansException {
-
- // 如果scope是单例的情况, 这里不进行分析
- if (mbd.isSingleton()) {
- .....
- }
- // 如果scope是prototype的情况, 这里不进行分析
- else if (mbd.isPrototype()) {
- ......
- }
- // 如果scope是其他的情况,本例中是reresh
- else {
- String scopeName = mbd.getScope();
- if (!StringUtils.hasLength(scopeName)) {
- throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
- }
- // 获取refresh scope的实现类RefreshScope,这个类在哪里注入,我们后面讲
- Scope scope = this.scopes.get(scopeName);
- if (scope == null) {
- throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
- }
- try {
- // 这边是获取bean,调用的是RefreshScope中的的方法
- Object scopedInstance = scope.get(beanName, () -> {
- beforePrototypeCreation(beanName);
- try {
- return createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- });
- beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
- }
- catch (IllegalStateException ex) {
- throw new ScopeNotActiveException(beanName, scopeName, ex);
- }
- }
- }
- catch (BeansException ex) {
- beanCreation.tag("exception", ex.getClass().toString());
- beanCreation.tag("message", String.valueOf(ex.getMessage()));
- cleanupAfterBeanCreationFailure(beanName);
- throw ex;
- }
- finally {
- beanCreation.end();
- }
- }
-
- return adaptBeanInstance(name, beanInstance, requiredType);
- }
-
- }
- 复制代码
2.RefreshScope继承成了GenericScope类,最终调用的的是GenericScope的get方法
- public class GenericScope
- implements Scope, BeanFactoryPostProcessor, BeanDefinitionRegistryPostProcessor, DisposableBean {
- @Override
-
- public Object get(String name, ObjectFactory> objectFactory) {
- // 将bean添加到缓存cache中
- BeanLifecycleWrapper value = this.cache.put(name, new BeanLifecycleWrapper(name, objectFactory));
- this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
- try {
- // 调用下面的getBean方法
- return value.getBean();
- }
- catch (RuntimeException e) {
- this.errors.put(name, e);
- throw e;
- }
- }
-
- private static class BeanLifecycleWrapper {
-
- public Object getBean() {
- // 如果bean为空,则创建bean
- if (this.bean == null) {
- synchronized (this.name) {
- if (this.bean == null) {
- this.bean = this.objectFactory.getObject();
- }
- }
- }
- // 否则返回之前创建好的bean
- return this.bean;
- }
- }
- }
- 复制代码
小结:
从这边的代码中可以印证了上面的说法,创建后的Bean会缓存到scope的cache中,优先从缓存中获取,如果缓存中是null, 则重新走一遍create bean的流程。
上面的在getBean的时候依赖到RefreshScope这个Bean,那么这个Bean是在什么时候加入到Spring Bean中的呢?答案就是RefreshAutoConfiguration
。
RefreshEvent
事件,RefreshEventListner
监听器会监听到这个事件。- public class RefreshEventListener implements SmartApplicationListener {
-
-
- ........
-
- public void handle(RefreshEvent event) {
- if (this.ready.get()) { // don't handle events before app is ready
- log.debug("Event received " + event.getEventDesc());
- // 会调用refresh方法,进行刷新
- Set<String> keys = this.refresh.refresh();
- log.info("Refresh keys changed: " + keys);
- }
- }
-
- }
-
- // 这个是ContextRefresher类中的刷新方法
- public synchronized Set<String> refresh() {
- // 刷新spring的envirionment 变量配置
- Set<String> keys = refreshEnvironment();
- // 刷新其他scope
- this.scope.refreshAll();
- return keys;
- }
- 复制代码
- public class RefreshScope extends GenericScope
- implements ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, Ordered {
-
- @ManagedOperation(description = "Dispose of the current instance of all beans "
- + "in this scope and force a refresh on next method execution.")
- public void refreshAll() {
- // 调用父类的destroy
- super.destroy();
- this.context.publishEvent(new RefreshScopeRefreshedEvent());
- }
- }
-
-
- @Override
- public void destroy() {
- List
errors = new ArrayList(); - Collection
wrappers = this.cache.clear(); - for (BeanLifecycleWrapper wrapper : wrappers) {
- try {
- Lock lock = this.locks.get(wrapper.getName()).writeLock();
- lock.lock();
- try {
- // 这里主要就是把之前的bean设置为null, 就会重新走createBean的流程了
- wrapper.destroy();
- }
- finally {
- lock.unlock();
- }
- }
- catch (RuntimeException e) {
- errors.add(e);
- }
- }
- if (!errors.isEmpty()) {
- throw wrapIfNecessary(errors.get(0));
- }
- this.errors.clear();
- }
- 复制代码
上面是这个RefreshScope实现动态刷新大致的原理,其中里面还有很多细节,可能需要留给大家自己debug去深入理解。