Nacos源码分析九、配置动态刷新(2)

x33g5p2x  于2021-12-20 转载在 其他  
字(6.3k)|赞(0)|评价(0)|浏览(363)

前文分析了@RefreshScope注解和RefreshScope类相关内容,我们了解到此时被@RefreshScope注解的类所对应的代理类类型已经换成了LockedScopedProxyFactoryBean,本篇主要分析这个类的作用。

首先看一下这个类的类图:

从这个图上可以看到

  1. FactoryBean。 这是一个工厂bean,也就是说容器中getBean得到的对象是通过它的getObject方法得到的。
  2. BeanFactoryAware。 beanFactory注入,需要关注一下setBeanFactory方法做了什么事情。
  3. 实现了MethodInterceptor接口,也就是说这个是动态代理的一个增强器,需要关注invoke方法。

下面我们具体分析。

getObject

@Override
public Object getObject() {
   if (this.proxy == null) {
      throw new FactoryBeanNotInitializedException();
   }
   return this.proxy;
}

这个没啥,获取proxy对象。这是一个cglib生成的动态代理对象。 下面看一下这个对象怎么来的

setBeanFactory

@Override
public void setBeanFactory(BeanFactory beanFactory) {
   super.setBeanFactory(beanFactory);
   Object proxy = getObject();
   if (proxy instanceof Advised) {
      Advised advised = (Advised) proxy;
      advised.addAdvice(0, this);
   }
}

先调用父类方法,里面会生成proxy对象,然后取出来添加了一个增强器是自身。也就是MethodInterceptor。

@Override
public void setBeanFactory(BeanFactory beanFactory) {
   if (!(beanFactory instanceof ConfigurableBeanFactory)) {
      throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
   }
   ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;

    //设置工厂,以便于后面获取代理对象
   this.scopedTargetSource.setBeanFactory(beanFactory);

   ProxyFactory pf = new ProxyFactory();
   pf.copyFrom(this);
   pf.setTargetSource(this.scopedTargetSource);

   Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
   Class<?> beanType = beanFactory.getType(this.targetBeanName);
   if (beanType == null) {
      throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
            "': Target type could not be determined at the time of proxy creation.");
   }
   if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
      pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
   }

   // Add an introduction that implements only the methods on ScopedObject.
   ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
    // 添加一个DelegatingIntroductionInterceptor拦截器
   pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));

   // Add the AopInfrastructureBean marker to indicate that the scoped proxy
   // itself is not subject to auto-proxying! Only its target bean is.
    //标记接口
   pf.addInterface(AopInfrastructureBean.class);

    //创建cglib动态代理
   this.proxy = pf.getProxy(cbf.getBeanClassLoader());
}

可以看到有两个增强器DelegatingIntroductionInterceptor和LockedScopedProxyFactoryBean自身,LockedScopedProxyFactoryBean在前面,实际上当增强器经过LockedScopedProxyFactoryBean的时候就已经返回了。也就是说在refresh作用域场景下用不到DelegatingIntroductionInterceptor这个增强器。

invoke

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
   Method method = invocation.getMethod();
   if (AopUtils.isEqualsMethod(method) || AopUtils.isToStringMethod(method)
         || AopUtils.isHashCodeMethod(method)
         || isScopedObjectGetTargetObject(method)) {
      return invocation.proceed();
   }
   // 拿出proxy
   Object proxy = getObject();
   ReadWriteLock readWriteLock = this.scope.getLock(this.targetBeanName);
   if (readWriteLock == null) {
      if (logger.isDebugEnabled()) {
         logger.debug("For bean with name [" + this.targetBeanName
               + "] there is no read write lock. Will create a new one to avoid NPE");
      }
      readWriteLock = new ReentrantReadWriteLock();
   }
   Lock lock = readWriteLock.readLock();
   lock.lock();
   try {
      if (proxy instanceof Advised) {
      // 直接通过proxy拿到被代理的对象执行反射方法就返回了
         Advised advised = (Advised) proxy;
         ReflectionUtils.makeAccessible(method);
         return ReflectionUtils.invokeMethod(method,
               advised.getTargetSource().getTarget(),
               invocation.getArguments());
      }
      return invocation.proceed();
   }
   // see gh-349. Throw the original exception rather than the
   // UndeclaredThrowableException
   catch (UndeclaredThrowableException e) {
      throw e.getUndeclaredThrowable();
   }
   finally {
      lock.unlock();
   }
}

可以看到这里直接就返回了

最后把之前RefreshScope的一个内容加进来

RefreshScope的eagerlyInitialize

RefreshScope类还实现了ApplicationListener接口,也就是说监听上下文刷新完成事件:

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
   start(event);
}

public void start(ContextRefreshedEvent event) {
   if (event.getApplicationContext() == this.context && this.eager
         && this.registry != null) {
      eagerlyInitialize();
   }
}

private void eagerlyInitialize() {
   for (String name : this.context.getBeanDefinitionNames()) {
      BeanDefinition definition = this.registry.getBeanDefinition(name);
       // 作用域是refresh的bean定义
      if (this.getName().equals(definition.getScope())
            && !definition.isLazyInit()) {
          // 先实例化了
         Object bean = this.context.getBean(name);
         if (bean != null) {
            bean.getClass();
         }
      }
   }
}

可以看到,refresh作用域的bean定义也会在启动阶段就实例化了(非懒加载的)。

这里要注意一下这个getBean的过程:

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

String scopeName = mbd.getScope();
final 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);
      }
   });
   bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
   throw new BeanCreationException(beanName,
         "Scope '" + scopeName + "' is not active for the current thread; consider " +
         "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
         ex);
}

回调RefreshScope的get方法:

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

BeanLifecycleWrapper的getBean:

public Object getBean() {
   if (this.bean == null) {
      synchronized (this.name) {
         if (this.bean == null) {
            this.bean = this.objectFactory.getObject();
         }
      }
   }
   return this.bean;
}

objectFactory.getObject();这段就回去了:

beforePrototypeCreation(beanName);
try {
    return createBean(beanName, mbd, args);
}
finally {
    afterPrototypeCreation(beanName);
}

这里的createBean就是spring自己的了。

也就是说其实内部维护了一个bean实例的缓存,就是被代理对象,不存在的话就调用ObjectFactorygetObject获取,如果调用过销毁方法,这个bean就不存在,就会重新创建实例。重新创建实例则重新注入,更新的数据就被注入进去了。

总结一下

LockedScopedProxyFactoryBean做了什么呢?

  1. 工厂bean,提供了获取proxy的getObject方法
  2. setBeanFactory时初始化proxy对象,并在原基础上增加了一个增强器
  3. 新增加的这个增强器啥也没干,直接反射被代理对象的方法回去了,把其他的增强器直接屏蔽了。
  4. refresh作用域的bean定义在启动阶段会调用一次getBean进行实例化。这里的getBean过程和单例、原型的不太一样,通过BeanLifecycleWrapper维护了一个bean实例的缓存,可以通过BeanLifecycleWrapper来使这个缓存的bean释放。那么再次调用时就需要重新创建了。

至此我们基本明白了spring-cloud中如何通过refreshScope来进行bean刷新的了。那么nacos要做的就是在收到变更配置的消息时,通知到spring-cloud清理掉对应的缓存bean,重新绑定属性源,然后再次使用时就会重新创建一个新的,就会把新的配置注入进来。

下篇我们分析通知销毁缓存的过程。

相关文章