Nacos
Nacos是阿里云提供的一个配置中心与服务注册发现中心组件。官网:https://nacos.io/zh-cn/docs/what-is-nacos.html
相关注解
@RefreshScope
@EnableDiscoveryClient
@LoadBalance
SpringCloud结合Nacos的示例在官网中已经很清楚了,在这里我就只记录一下我自己在SpringCloud中使用Nacos遇到的问题和解答。
1. @RefreshScope是如何工作?
总结:RefreshScope其实是名为”refresh"的一个scope,它与为人熟知的Singleton、Prototype、Session这几个scope根本上都是一样的,定义Bean的作用域。而有RefreshScope管理的Bean当接收到事件通知时,会刷新scope中的bean。
相关源码:
- RefreshScope#postProcessBeanFactory
RefreshScope继承GenericScope实现BeanFactoryPostProcessor,在ApplicationContext刷新过程中,通过调用postProcessBeanFactory方法,把名为”refresh"的scope注册到BeanFactory中。
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
this.beanFactory = beanFactory;
beanFactory.registerScope(this.name, this);
setSerializationId(beanFactory);
}
- RefreshAutoConfiguration.RefreshScopeBeanDefinitionEnhancer#postProcessBeanDefinitionRegistry
RefreshAutoConfiguration.RefreshScopeBeanDefinitionEnhancer实现BeanDefinitionRegistryPostProcessor接口,在ApplicationContext的refresh过程中会调用postProcessBeanDefinitionRegistry方法。
目的:调用ScopedProxyUtils.createScopedProxy方法创建某些bean的Scope代理实例,如:com.zaxxer.hikari.HikariDataSource
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
bindEnvironmentIfNeeded(registry);
for (String name : registry.getBeanDefinitionNames()) {
BeanDefinition definition = registry.getBeanDefinition(name);
if (isApplicable(registry, name, definition)) {
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition,
name);
BeanDefinitionHolder proxy = ScopedProxyUtils
.createScopedProxy(holder, registry, true);
definition.setScope("refresh");
if (registry.containsBeanDefinition(proxy.getBeanName())) {
registry.removeBeanDefinition(proxy.getBeanName());
}
registry.registerBeanDefinition(proxy.getBeanName(),
proxy.getBeanDefinition());
}
}
}
- AbstractBeanFactory#doGetBean
doGetBean方法中,会判断Bean的scope,把bean交由对应的scope对象管理。
......
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);
}
});
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);
}
}
.......
- RefreshEventListener#handle
RefreshEventListener实现SmartApplicationListener接口,会在AbstractApplicationContext#refresh刷新时作为Listener加载。
RefreshEventListener在接收到RefreshEvent之后,会调用ContextRefresher#refresh方法,最终会调用到RefreshScope#refreshAll方法public void refresh() throws BeansException, IllegalStateException { ...... // Check for listener beans and register them. registerListeners(); ...... }
- RefreshScope#refreshAll方法
refreshAll方法中,调用了destroy方法,把scope的bean实例全部销毁,当下次再访问到相关的bean时会触发重新加载,达到refresh的目的。
public void refreshAll() {
super.destroy();
this.context.publishEvent(new RefreshScopeRefreshedEvent());
}
2. @LoadBalance注解是如何与Ribbon结合作负载均衡的?
总结:@LoadBalance作用与RestTemplate上,触发调用Ribbon的postProcessor会根据@LoadBalance注解找到对应的RestTemplate上,给对应的RestTemplate添加上Interceptor,在调用rest接口前作拦截。
- LoadBalancerAutoConfiguration#loadBalancedRestTemplateInitializerDeprecated方法
LoadBalancerAutoConfiguration类自动注入相关的RestTemplate
loadBalancedRestTemplateInitializerDeprecated方法中调用RestTemplateCustomizer的custom方法,把loadBalance相关的Interceptor注入到相关的RestTemplate中:public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); ......
@Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { return () -> restTemplateCustomizers.ifAvailable(customizers -> { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } }); }
- RestTemplateCustomizer
@Bean @ConditionalOnMissingBean public RestTemplateCustomizer restTemplateCustomizer( final RetryLoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; }
3. @Qualifier注解作用在别的注解上?
- 先看一下@Qualifier注解的源码:
注释的意思是:/** * This annotation may be used on a field or parameter as a qualifier for * candidate beans when autowiring. It may also be used to annotate other * custom annotations that can then in turn be used as qualifiers. * * @author Mark Fisher * @author Juergen Hoeller * @since 2.5 * @see Autowired */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Qualifier { String value() default ""; }
@Qualifier注解是用于Autowired的时候作为一个条件选择器选择正确的bean实例进行注入操作。它可以用于注解另外一个注解,此时被注解的注解会变成一个选择器。
这段话翻译得很绕啊,为什么要说到这个呢,是因为在@LoadBalance的AutoConfiguration中,对应的RestTemplate注入就用到了@Qualifier的这个特性:
@LoadBalance:public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); ......
@LoadBalance被@Qualifier注解,所以@LoadBalance作为一个选择器,当Autowired的时候,会筛选有@LoadBalance的实例才会被注入。/** * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient. * @author Spencer Gibb */ @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Qualifier public @interface LoadBalanced { }
- @Qualifier注解工作源码
入口:
AbstractAutowireCapableBeanFactory#autowireByType ->AbstractAutowireCapableBeanFactory#resolveDependency -> DefaultListableBeanFactory#resolveMultipleBeans -> DefaultListableBeanFactory#findAutowireCandidates -> 最终通过抽象方法调用到QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate方法中:public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) { boolean match = super.isAutowireCandidate(bdHolder, descriptor); if (match) { match = checkQualifiers(bdHolder, descriptor.getAnnotations()); if (match) { MethodParameter methodParam = descriptor.getMethodParameter(); if (methodParam != null) { Method method = methodParam.getMethod(); if (method == null || void.class == method.getReturnType()) { match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations()); } } } } return match; }