一文带你理解@RefreshScope注解实现动态刷新原理 |
您所在的位置:网站首页 › 自动刷新java › 一文带你理解@RefreshScope注解实现动态刷新原理 |
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情 概述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的源码,该注解被@Scope注解使用,@Scope用来比较Spring Bean的作用域,具体使用参考相关文章。 注解的属性proxyMode默认使用TARGET_CLASS作为代理。 实例 controller中添加@RefreshScope代码地址: github.com/alvinlkk/aw… 原理解析为了实现动态刷新配置,主要就是想办法达成以下两个核心目标: 让Spring容器重新加载Environment环境配置变量 Spring Bean重新创建生成@RefreshScope主要就是基于@Scope注解的作用域代理的基础上进行扩展实现的,加了@RefreshScope注解的类,在被Bean工厂创建后会加入自己的refresh scope 这个Bean缓存中,后续会优先从Bean缓存中获取,当配置中心发生了变更,会把变更的配置更新到spring容器的Environment中,并且同事bean缓存就会被清空,从而就会从bean工厂中创建bean实例了,而这次创建bean实例的时候就会继续经历这个bean的生命周期,使得@Value属性值能够从Environment中获取到最新的属性值,这样整个过程就达到了动态刷新配置的效果。 如果对Scope注解不清楚的可以阅读这篇文章:【Spring注解必知必会】@Scope注解源码解析 通过打上断点查看堆栈可知: 因为Class被加上了@RefreshScope注解,那么这个BeanDefinition信息中的scope为refresh,在getBean的的时候会单独处理逻辑。 public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { protected T doGetBean( String name, @Nullable Class 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的流程。 RefeshScope Bean的创建上面的在getBean的时候依赖到RefreshScope这个Bean,那么这个Bean是在什么时候加入到Spring Bean中的呢?答案就是RefreshAutoConfiguration。 上面是这个RefreshScope实现动态刷新大致的原理,其中里面还有很多细节,可能需要留给大家自己debug去深入理解。 参考 blog.csdn.net/qq_29310729… blog.csdn.net/weixin_3768… |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |