• nacos动态配置刷新机制原理


    nacos动态配置刷新机制原理

    项目里面许多业务场景以及灵活配置的需求经常要用到动态配置。一般就是apollo和nacos两种选型。

    nacos动态刷新导致的bug

    nacos一般为了实现动态配置一般会加入@RefreshScope注解进行实现,例如下面的代码加入了@RefreshScope想要实现跨域的动态配置,例如跨域白名单等。

    @slf4j
    @configuration
    @RefreshScope
    public class FilterConfiguration {
    @value("${jlpay.business.filter.allowCredentials:true}")
    private boolean allowCredentials;
    @value("${jlpay.business.filter.allowedHeader:}")
    private String allowedHeader;
    @value("${jlpay.business.filter.allowedMethod:}")
    private String allowedMethod;
    @value("${jlpay.business.filter.allowedOrigin:*}")
    private String allowedOrigin;
    @value("${jlpay.business.filter.corsPath:/**}")
    private String corsPath;
    
    /**
     * 跨域请求配置
     * @return
     */
    private CorsConfiguration buildCorsConfig() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.setAllowCredentials(allowCredentials);
        corsConfig.addAllowedHeader(allowedHeader);
        corsConfig.addAllowedMethod(allowedMethod);
        //指定域名拦截配置
        if (!StringUtils.isEmpty(allowedOrigin) && !CorsConfiguration.ALL.equals(allowedOrigin)) {
            String[] originArr = allowedOrigin.split(",");
            for (String origin : originArr) {
                corsConfig.addAllowedOrigin(origin);
            }
        } else {
            corsConfig.addAllowedOrigin(CorsConfiguration.ALL);
        }
    
        return corsConfig;
    }
    
    /**
     * 跨域请求过滤器配置
     */
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        //注:暂定所有接口均允许跨域,建议针对具体接口配置允许跨域
        configSource.registerCorsConfiguration(corsPath, buildCorsConfig());
    
        log.info("跨域请求过滤器配置:{}", JSONObject.toJSONString(configSource.getCorsConfigurations()));
        return new CorsFilter(configSource);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    但是发现这个注解后直接导致了跨域配置失效,前端访问我接口的时候报了跨域错误。问题的根因在于serverletContext没有注入生成的filterbean,为什么没有注入,是serverletContext在初始化的时候去找符合的filer类,是通过getBeanNamesForType获取的注入的filter类名称,按道理根据filter实现统一接口类是可以获取到的,但是这个方法isFactoryBean 判定了CorsFilter是factorybean,因此,CorsFilter没有被获取到加入到filter链中。所以@RefreshScope注解影响了CorsFilter bean的生成 方式。下面仔细看下@RefreshScope的原理。

    @Override
    public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
       String beanName = transformedBeanName(name);
       Object beanInstance = getSingleton(beanName, false);
       if (beanInstance != null) {
          return (beanInstance instanceof FactoryBean);
       }
       // No singleton instance found -> check bean definition.
       if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory cbf) {
          // No bean definition found in this factory -> delegate to parent.
          return cbf.isFactoryBean(name);
       }
       return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    因为@RefreshScope 导致FilterConfiguration 变成了一个facotorybean, 导致无法加载了serverletContext的filter链中。 所以解决防范是建议使用ConfigurationProperties bean 方式使用,@RefreshScope并不是一个很好的使用方式。

    RefreshScope动态刷新原理

    RefreshScope注解的Bean可以在运行时刷新,并且使用它们的任何组件都将在下一个方法调用前获得一个新实例,该实例将完全初始化并注入所有依赖项。

    public @interface RefreshScope {
    
      /**
    	 * @see Scope#proxyMode()
    	 * @return proxy mode
    	 */
      ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    RefreshScope用到的是spring bean的scope模式能力。scope模式在获取bean时和单例的Bean以及多例的bean不太一样。scope 里自定义了一个scope范围来隔离不同的scope,在获取bean时会优先从实现了Scope接口的类中获取,简单来说,scope模式的bean的获取方式通过scope接口实现类获取,是一种代理模式。

    //org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 
    // Create bean instance.
       if (mbd.isSingleton()) {
          sharedInstance = getSingleton(beanName, () -> {
             try {
                return createBean(beanName, mbd, args);
             }
              //......
       }
    
       else if (mbd.isPrototype()) {
          // It's a prototype -> create a new instance.
          Object prototypeInstance = null;
          try {
             beforePrototypeCreation(beanName);
             prototypeInstance = createBean(beanName, mbd, args);
          }
           //......
       }
    
       else {
          String scopeName = mbd.getScope();
          if (!StringUtils.hasLength(scopeName)) {
             throw new IllegalStateException("No scope name defined for bean '" + beanName + "'");
          }
          Scope scope = this.scopes.get(scopeName);
          if (scope == null) {
             throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
          }
          try {
             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) {
    	//......
    }
    finally {
    	//......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    RefreshScope 就是其中一个代理类实现,scope.get由genericScope实现。

    在这里插入图片描述
    GenericScope是一个beanFactoryPostprocess,在bean实列化前就会执行postProcessBeanFactory将自己注册放scopes中去,这样前面的Bean获取单例就会从对应scopeName中获取scope,而scope是一个保存了代理类的map,最终执行的代理类的Invoke方法。

    //postProcessBeanFactory会在实例化执行
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    		this.beanFactory = beanFactory;
    		beanFactory.registerScope(this.name, this);
    		setSerializationId(beanFactory);
    	}
    
     
    public void registerScope(String scopeName, Scope scope) {
    		Assert.notNull(scopeName, "Scope identifier must not be null");
    		Assert.notNull(scope, "Scope must not be null");
    		if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
    			throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
    		}
    		Scope previous = this.scopes.put(scopeName, scope);
    		if (previous != null && previous != scope) {
    			if (logger.isDebugEnabled()) {
    				logger.debug("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
    			}
    		}
    		else {
    			if (logger.isTraceEnabled()) {
    				logger.trace("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    这个代理类RefreshScope 注解生成的是LockedScopedProxyFactoryBean,下面看看代理类是怎么生成的以及invoke代理的方法。

    RefreshScope 注解类代理类注册容器

    RefreshScope 注解的类 在生成Bean 定义时会生成两份定义,一份是原来的类的定义,另一份时代理类LockedScopedProxyFactoryBean的定义,且LockedScopedProxyFactoryBean时一个factorybean,通过getObject方法获取实际的proxy对象,实际的proxy对象通过getBeanFactory().getBean(getTargetBeanName()),即ioc容器中获取原始bean。

    代理类定义

    springboot bean 加载过程是利用@ComponentScan会扫描并注册包下带有@Componet注解的类为Bean(@Configuration 实际上也是一个@Componet),达到一个注解驱动注册Bean的效果。扫描Bean并注册为BeanDefinition这一过程是由ClassPathBeanDefinitionScanner类的doScan方法去做的。

    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
      Assert.notEmpty(basePackages, "At least one base package must be specified");
      Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
      // 扫描basePackages所在的包下的所有的类,带@Componet的类都会被注册为BeanDefinition
      for (String basePackage : basePackages) {
        	//...
          if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            // 这里就是Scope的Bean的统一处理,是一个改变BeanDefinition的回调机会
              // 调用createScopedProxy创建一个scope 代理
            definitionHolder =
              AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            // 注册BeanDefinition到IOC容器中
            registerBeanDefinition(definitionHolder, this.registry);
          }
        }
      }
      return beanDefinitions;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    注意AnnotationConfigUtils.applyScopedProxyMode方法,这里提供了一个代理bean定义的方式,若具体是在 创建bean时会判断类是否带@refreshScope注解,然后创建ScopedProxyFactoryBean bean工厂用来创建代理bean。

    public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
                                                         BeanDefinitionRegistry registry, boolean proxyTargetClass) {
    
      // ...
    
      // Create a scoped proxy definition for the original bean name,
      // "hiding" the target bean in an internal target definition.
      // 重点,这里构造函数中将beanClass设置为了ScopedProxyFactoryBean.class
      RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
      // targetDefinition是被代理的原生Bean
      proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
      
      // ...
    
      // Return the scoped proxy definition as primary bean definition
      // (potentially an inner bean).
      return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    postProcessBeanDefinitionRegistry回调方法中会针对刚刚那个beanClass为ScopedProxyFactoryBean.class的BeanDefinition进行一个增强处理,最终生成的代理类定义的是LockedScopedProxyFactoryBean。

    // GenericScope.class
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
      throws BeansException {
      // 获取所有BeanDefinition的名称
      for (String name : registry.getBeanDefinitionNames()) {
        BeanDefinition definition = registry.getBeanDefinition(name);
        // 针对RootBeanDefinition这个BeanDefinition来做,这和上面的逻辑吻合
        if (definition instanceof RootBeanDefinition) {
          RootBeanDefinition root = (RootBeanDefinition) definition;
          // 判断BeanClass == ScopedProxyFactoryBean.class
          if (root.getDecoratedDefinition() != null && root.hasBeanClass()
              && root.getBeanClass() == ScopedProxyFactoryBean.class) {
            if (getName().equals(root.getDecoratedDefinition().getBeanDefinition()
                                 .getScope())) {
              // 将BeanClass换为LockedScopedProxyFactoryBean
              root.setBeanClass(LockedScopedProxyFactoryBean.class);
              root.getConstructorArgumentValues().addGenericArgumentValue(this);
              // surprising that a scoped proxy bean definition is not already
              // marked as synthetic?
              root.setSynthetic(true);
            }
          }
        }
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    代理类创建

    LockedScopedProxyFactoryBean的父类ScopedProxyFactoryBean 实现了FactoryBean,FactoryBean在Spring中最为典型的一个应用就是用来创建AOP的代理对象。proxy就是生成的实际代理对象。

    public Object getObject() {
      if (this.proxy == null) {
        throw new FactoryBeanNotInitializedException();
      }
      return this.proxy;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    proxy是通过 ScopedProxyFactoryBean 生成的,该类实现了 BeanFactoryAware(该接口用于知道创建Bean的beanfactory,即ioc容器),因此setBeanFactory会在比较早的时机被回调用来生成proxy.

    public void setBeanFactory(BeanFactory beanFactory) {
      
      // ...
      
    	// 这里是一个比较关键的点,scopedTargetSource变量是一个SimpleBeanTargetSource
      // scopedTargetSource中保存了IOC容器
      this.scopedTargetSource.setBeanFactory(beanFactory);
    
      // 创建动态代理前,将动态代理的信息都保存到ProxyFactory中
      ProxyFactory pf = new ProxyFactory();
      pf.copyFrom(this);
      // 注意,这里的TargetSource就是刚刚说的scopedTargetSource
      pf.setTargetSource(this.scopedTargetSource);
      
    	// ...
    
      this.proxy = pf.getProxy(cbf.getBeanClassLoader());
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    proxy的代理方法如下, 代理实现是CGLib动态代理都会实现一个MethodInterceptor,被代理的类的每一个方法调用实质上都是在调用MethodInterceptor的intercept方法。

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
      // 反复强调的TargetSource
      TargetSource targetSource = this.advised.getTargetSource();
      try {
        // ...
        // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
        // 重点,这里调用了targetSource的getTarget
        // target变量就是被代理的类,调用实际方法的时候反射调用其对应方法
        target = targetSource.getTarget();
       // ...
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
          // ...
          // target变量就是被代理的类,调用实际方法的时候反射调用其对应方法
          retVal = methodProxy.invoke(target, argsToUse);
        }
        else {
          // We need to create a method invocation...
          // target变量就是被代理的类,调用实际方法的时候反射调用其对应方法
          retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        // target变量就是被代理的类,调用实际方法的时候反射调用其对应方法
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
      }
      // ...
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    前面讲到TargetSource的实现类是SimpleBeanTargetSource:通过bean容器获取代理的bean.

    public class SimpleBeanTargetSource extends AbstractBeanFactoryBasedTargetSource {
    
      @Override
      public Object getTarget() throws Exception {
        // 从IOC中getBean
        return getBeanFactory().getBean(getTargetBeanName());
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    bean注册

    this.registry 的类是实现了BeanDefinitionRegistry 是一个接口,它定义了关于 BeanDefinition 的注册、移除、查询等一系列的操作。该接口有三个实现类:DefaultListableBeanFactory、GenericApplicationContext、SimpleBeanDefinitionRegistry。BeanDefinitionRegistry 集成了SingletonBeanRegistry ,接口的核心实现类是 DefaultSingletonBeanRegistry(该类存储 Bean 之间的依赖关系、存储 Bean 的包含关系(外部类包含内部类)、获取 Bean 所处的状态(正在创建、创建完毕等)、回调销毁 Bean 时触发的 destroy 方法等。)用来注册bean到ioc容器。

    执行完 registerBeanDefinition 方法后,Bean 的名称和对应的 BeanDefinition 就被放入了容器中,后续获取 Bean 也是从这个容器中获取。

    在这里插入图片描述

    	/**
    	 * 注册Bean基于给定的bean factory.
    	 * @param definitionHolder bean定义类
    	 * @param registry bean factory注册
    	 * @throws BeanDefinitionStoreException if registration failed
    	 */
    	public static void registerBeanDefinition(
    			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    			throws BeanDefinitionStoreException {
    
    		// 注册bean 定义
    		String beanName = definitionHolder.getBeanName();
    		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
    		// 证据Bean 别名
    		String[] aliases = definitionHolder.getAliases();
    		if (aliases != null) {
    			for (String alias : aliases) {
    				registry.registerAlias(beanName, alias);
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    	@Override
    	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    			throws BeanDefinitionStoreException {
            //......
    
    			if (hasBeanCreationStarted()) {
    				// 双重判断,将bean定义放入beanDefinitionMap (ioc容器)
    				synchronized (this.beanDefinitionMap) {
    					this.beanDefinitionMap.put(beanName, beanDefinition);
    					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
    					updatedDefinitions.addAll(this.beanDefinitionNames);
    					updatedDefinitions.add(beanName);
    					this.beanDefinitionNames = updatedDefinitions;
    					removeManualSingletonName(beanName);
    				}
    			}
                   //......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    动态配置刷新

    前面讲到通过beanfactory会创建以及获取bean, 这里scope.get获取的Scope对象为RefreshScope,创建Bean还是由IOC来做(createBean方法),但是获取Bean,都由RefreshScope对象的get方法去获取,其get方法在父类GenericScope中实现。scop利用hash map管理的ioc创建的bean

    Object scopedInstance = scope.get(beanName, () -> {
    							beforePrototypeCreation(beanName);
    							try {
    								return createBean(beanName, mbd, args);
    							}
    							finally {
    								afterPrototypeCreation(beanName);
    							}
    						});
    
    public Object get(String name, ObjectFactory<?> objectFactory) {
      // 将原始Bean缓存下来
      BeanLifecycleWrapper value = this.cache.put(name,
                                                  new BeanLifecycleWrapper(name, objectFactory));
      this.locks.putIfAbsent(name, new ReentrantReadWriteLock());
      try {
        // 获取原始类bean
        return value.getBean();
      }
      catch (RuntimeException e) {
        this.errors.put(name, e);
        throw e;
      }
    }
    #BeanLifecycleWrapper getbean
    public Object getBean() {
      // 因爲是新的BeanLifecycleWrapper實例,這裏必定爲null
      if (this.bean == null) {
        synchronized (this.name) {
          if (this.bean == null) {
            // 調用IOC容器的createBean,再建立一個Bean出來
            this.bean = this.objectFactory.getObject();
          }
        }
      }
      return this.bean;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    BeanLifecycleWrapper生成了一个原始的包装类,其中getBean是返回前面说到的原始类的bean。那么原始类的beab和代理类的bean的关系就出来了。

    代理类bean会被其他bean引用,代理原始bean进行执行,并且原始bean可以看出是由GenericScope来管理生命周期。所以当配置发生变化时,只需要将scop缓存的bean移除且将原始bean值设置为null,既可以让代理类重新生成新的原始bean,此时获取的配置也是最新的。

    // 配置发生变化时调用

    public synchronized Set<String> refresh() {
    	Set<String> keys = refreshEnvironment();
    	this.scope.refreshAll();
    	return keys;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    @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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    //进行对象获取,如果没有就创建并放入缓存
    @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 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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    总结

    @refreshScope注解用了两种代理的模式去实现动态刷新bean,一个是scope代理原始bean的生成和销毁,另一个是aop模式代理原始bean,去获取修改配置后新配置值生成的bean。值得注意的是@RefreshScope会使得bean自动销毁和生成,可能会导致一些莫名其妙的问题,例如常见的@scheduled注解失效、线程池bean不断被创建等等,因此实际生产中最好控制动态变量用ConfigurationProperties 注解生产,该注解是通过设置变量值的方式进行配置的修改,更加的安全。

  • 相关阅读:
    d的is表达式
    pgbackrest归档目录满,清理后写入仍报错,分析及处理
    【不积跬步无以至千里】Linux批量新建分区
    ChinaSoft 论坛巡礼|开源软件供应链论坛
    英语学习笔记
    基于STM32和ESP8266的WIFI信号检测仪
    让.NET 8 支持 Windows Vista RTM
    LeetCode每日一题(188. Best Time to Buy and Sell Stock IV)
    药品研发--质理研究人员绩效办法
    python文件的读取
  • 原文地址:https://blog.csdn.net/qq_17236715/article/details/132920853