RefreshScope源码分析

简介: springcloud的git配置中可以使用@RefreshScope + POST [/actuator/refresh]调用actuator的refresh来实现配置的热更新。 @RefreshScope springcloud描述: A Spring @Bean that is marked.

springcloud的git配置中可以使用@RefreshScope + POST [/actuator/refresh]调用actuator的refresh来实现配置的热更新。

@RefreshScope

springcloud描述:

A Spring @Bean that is marked as @RefreshScope will get special treatment when there is a configuration change. This addresses 
the problem of stateful beans that only get their configuration injected when they are initialized. For instance if a DataSource has open connections when the 
database URL is changed via the Environment, we probably want the holders of those connections to be able to complete what they are doing. Then the next time someone borrows a 
connection from the pool he gets one with the new URL.

Refresh scope beans are lazy proxies that initialize when they are used (i.e. when a method is called), and the scope acts as a cache of initialized values. To force a 
bean to re-initialize on the next method call you just need to invalidate its cache entry.

@RefreshScope标记的Bean是延迟加载的,每次访问时都会被强制初始化,这一点在RefreshScope类的doc中也有说明:

A Scope implementation that allows for beans to be refreshed dynamically at runtime
 * (see {@link #refresh(String)} and {@link #refreshAll()}). If a bean is refreshed then
 * the next time the bean is accessed (i.e. a method is executed) a new instance is
 * created. 

refresh scope bean则是在使用时(即调用方法时)初始化的惰性代理,充当已初始化值的缓存。如果要强制被标记bean在下一个方法调用上重新初始化,只需使其缓存项无效即可。

RefreshScope类:

/**
 * <p>
 * A Scope implementation that allows for beans to be refreshed dynamically at runtime
 * (see {@link #refresh(String)} and {@link #refreshAll()}). If a bean is refreshed then
 * the next time the bean is accessed (i.e. a method is executed) a new instance is
 * created. All lifecycle methods are applied to the bean instances, so any destruction
 * callbacks that were registered in the bean factory are called when it is refreshed, and
 * then the initialization callbacks are invoked as normal when the new instance is
 * created. A new bean instance is created from the original bean definition, so any
 * externalized content (property placeholders or expressions in string literals) is
 * re-evaluated when it is created.
 * </p>
 *
 * <p>
 * Note that all beans in this scope are <em>only</em> initialized when first accessed, so
 * the scope forces lazy initialization semantics. The implementation involves creating a
 * proxy for every bean in the scope, so there is a flag
 * {@link #setProxyTargetClass(boolean) proxyTargetClass} which controls the proxy
 * creation, defaulting to JDK dynamic proxies and therefore only exposing the interfaces
 * implemented by a bean. If callers need access to other methods then the flag needs to
 * be set (and CGLib present on the classpath). Because this scope automatically proxies
 * all its beans, there is no need to add <code>&lt;aop:auto-proxy/&gt;</code> to any bean
 * definitions.
 * </p>
 *
 * <p>
 * The scoped proxy approach adopted here has a side benefit that bean instances are
 * automatically {@link Serializable}, and can be sent across the wire as long as the
 * receiver has an identical application context on the other side. To ensure that the two
 * contexts agree that they are identical they have to have the same serialization id. One
 * will be generated automatically by default from the bean names, so two contexts with
 * the same bean names are by default able to exchange beans by name. If you need to
 * override the default id then provide an explicit {@link #setId(String) id} when the
 * Scope is declared.
 * </p>
 *
 * @author Dave Syer
 *
 * @since 3.1
 *
 */
@ManagedResource
public class RefreshScope extends GenericScope
        implements ApplicationContextAware, Ordered {

    private ApplicationContext context;
    private BeanDefinitionRegistry registry;
    private boolean eager = true;
    private int order = Ordered.LOWEST_PRECEDENCE - 100;

    /**
     * Create a scope instance and give it the default name: "refresh".
     */
    public RefreshScope() {
        super.setName("refresh");
    }

    @ManagedOperation(description = "Dispose of the current instance of bean name provided and force a refresh on next method execution.")
    public boolean refresh(String name) {
        if (!name.startsWith(SCOPED_TARGET_PREFIX)) {
            // User wants to refresh the bean with this name but that isn't the one in the
            // cache...
            name = SCOPED_TARGET_PREFIX + name;
        }
        // Ensure lifecycle is finished if bean was disposable
        if (super.destroy(name)) {
            this.context.publishEvent(new RefreshScopeRefreshedEvent(name));
            return true;
        }
        return false;
    }

    @ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.")
    public void refreshAll() {
        super.destroy();
        this.context.publishEvent(new RefreshScopeRefreshedEvent());
    }
}

当调用refresh方法时会调用super.destroy(String name)方法,父类实现如下:

public class GenericScope implements Scope, BeanFactoryPostProcessor,
        BeanDefinitionRegistryPostProcessor, DisposableBean {
        
    private BeanLifecycleWrapperCache cache = new BeanLifecycleWrapperCache(
            new StandardScopeCache());
            
    /**
     * Destroy the named bean (i.e. flush it from the cache by default).
     *
     * @param name the bean name to flush
     * @return true if the bean was already cached, false otherwise
     */
    protected boolean destroy(String name) {
        BeanLifecycleWrapper wrapper = this.cache.remove(name);
        if (wrapper != null) {
            Lock lock = locks.get(wrapper.getName()).writeLock();
            lock.lock();
            try {
                wrapper.destroy();
            }
            finally {
                lock.unlock();
            }
            this.errors.remove(name);
            return true;
        }
        return false;
    }
    
    @Override
    public void destroy() {
        List<Throwable> errors = new ArrayList<Throwable>();
        Collection<BeanLifecycleWrapper> wrappers = this.cache.clear();
        for (BeanLifecycleWrapper wrapper : wrappers) {
            try {
                Lock lock = locks.get(wrapper.getName()).writeLock();
                lock.lock();
                try {
                    wrapper.destroy();
                }
                finally {
                    lock.unlock();
                }
            }
            catch (RuntimeException e) {
                errors.add(e);
            }
        }
        if (!errors.isEmpty()) {
            throw wrapIfNecessary(errors.get(0));
        }
        this.errors.clear();
    }
}

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        BeanLifecycleWrapper value = this.cache.put(name,
                new BeanLifecycleWrapper(name, objectFactory));
        locks.putIfAbsent(name, new ReentrantReadWriteLock());
        try {
            return value.getBean();
        }
        catch (RuntimeException e) {
            this.errors.put(name, e);
            throw e;
        }
    }

    @Override
    public Object remove(String name) {
        BeanLifecycleWrapper value = this.cache.remove(name);
        if (value == null) {
            return null;
        }
        // Someone might have added another object with the same key, but we
        // keep the method contract by removing the
        // value we found anyway
        return value.getBean();
    }

BeanLifecycleWrapperCache用于缓存被标记的bean,当destroy执行时,会从BeanLifecycleWrapperCache中删除或全部清除bean。在调用get方法时会重新new一个对象放入缓存中。

跟踪代码,可得到调用refresh时:

/**
 * @author Dave Syer
 * @author Venil Noronha
 */
@Endpoint(id = "refresh")
public class RefreshEndpoint {

    private ContextRefresher contextRefresher;

    public RefreshEndpoint(ContextRefresher contextRefresher) {
        this.contextRefresher = contextRefresher;
    }

    @WriteOperation
    public Collection<String> refresh() {
        Set<String> keys = contextRefresher.refresh();
        return keys;
    }
}

得到:

public class ContextRefresher {
    public synchronized Set<String> refresh() {
        Map<String, Object> before = extract(
                this.context.getEnvironment().getPropertySources());
        addConfigFilesToEnvironment();
        Set<String> keys = changes(before,
                extract(this.context.getEnvironment().getPropertySources())).keySet();
        this.context.publishEvent(new EnvironmentChangeEvent(context, keys));
        this.scope.refreshAll();
        return keys;
    }
}

是此处调用了RefreshScope的refreshAll方法。

Endpoint

@Endpoint用于标记为actuator终端,提供应用运行信息:

Identifies a type as being an actuator endpoint that provides information about the
 * running application. Endpoints can be exposed over a variety of technologies including
 * JMX and HTTP.

例如:

/**
 * @author Dave Syer
 * @author Venil Noronha
 */
@Endpoint(id = "refresh")
public class RefreshEndpoint {

    private ContextRefresher contextRefresher;

    public RefreshEndpoint(ContextRefresher contextRefresher) {
        this.contextRefresher = contextRefresher;
    }

    @WriteOperation
    public Collection<String> refresh() {
        Set<String> keys = contextRefresher.refresh();
        return keys;
    }

}

再看@Endpoint注解:

/**
 * Identifies a type as being an actuator endpoint that provides information about the
 * running application. Endpoints can be exposed over a variety of technologies including
 * JMX and HTTP.
 * <p>
 * Most {@code @Endpoint} classes will declare one or more
 * {@link ReadOperation @ReadOperation}, {@link WriteOperation @WriteOperation},
 * {@link DeleteOperation @DeleteOperation} annotated methods which will be automatically
 * adapted to the exposing technology (JMX, Spring MVC, Spring WebFlux, Jersey etc.).
 * <p>
 * {@code @Endpoint} represents the lowest common denominator for endpoints and
 * intentionally limits the sorts of operation methods that may be defined in order to
 * support the broadest possible range of exposure technologies. If you need deeper
 * support for a specific technology you can either write an endpoint that is
 * {@link FilteredEndpoint filtered} to a certain technology, or provide
 * {@link EndpointExtension extension} for the broader endpoint.
 *
 * @author Andy Wilkinson
 * @author Phillip Webb
 * @since 2.0.0
 * @see EndpointExtension
 * @see FilteredEndpoint
 * @see EndpointDiscoverer
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Endpoint {

    /**
     * The id of the endpoint.
     * @return the id
     */
    String id() default "";

    /**
     * If the endpoint should be enabled or disabled by default.
     * @return {@code true} if the endpoint is enabled by default
     */
    boolean enableByDefault() default true;

}
目录
相关文章
|
2月前
|
安全 Java Spring
SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六)
SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六)
47 0
|
缓存 Java Nacos
一文带你理解@RefreshScope注解实现动态刷新原理
一文带你理解@RefreshScope注解实现动态刷新原理
644 0
一文带你理解@RefreshScope注解实现动态刷新原理
|
3月前
|
缓存 Java Spring
Spring 源码阅读 75:@EnableAsync 分析
【1月更文挑战第10天】本文以 @EnableAsync 作为切入点,分析了 Spring 开启基于注解的异步任务特性的原理。
35 0
|
4月前
|
JSON 前端开发 Java
[Spring~源码] ControllerAdvice揭秘
[Spring~源码] ControllerAdvice揭秘
|
11月前
|
缓存 Java Go
Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析
Spring5源码 - 13 Spring事件监听机制_@EventListener源码解析
98 0
|
11月前
|
缓存 Java C++
Spring5源码 - 01 BeanDefination源码分析
Spring5源码 - 01 BeanDefination源码分析
71 0
|
缓存 Java Spring
Spring - @PostConstruct 源码解析
@PostConstruct 源码解析
229 1
Spring - @PostConstruct 源码解析
|
XML 缓存 Java
Spring FactoryBean 源码分析
什么是FactoryBean? 根据FactoryBean源码的注释,我们可以对FactoryBean有如下大概的认识。
232 0
|
缓存 Java Spring
Spring 源码阅读 45:@PostConstruct 和 @PreDestroy 注解的处理
本文介绍了 InitDestroyAnnotationBeanPostProcessor 后处理器对`@PostConstruct`和`@PreDestroy`注解方法的解析和处理原理。
108 0
|
Java Spring 容器
简明易理解的@SpringBootApplication注解源码分析
springboot项目的启动类上,都会有个注解@SpringBootApplication,这个注解起到了什么作用?
137 0

热门文章

最新文章