NacosServiceManage作为Nacos服务的管理器,内部实现了NacosNamingService的实例化。先看一下SpringBoot自动加载NacosServiceManager的流程。
查看spring-cloud-starter-alibaba-nacos-discovery-2.2.x.RELEASE.jar包下的META-INFO目录下的spring.factories文件。通过该文件找到NacosServiceAutoConfiguration类,该类文件内容如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
public class NacosServiceAutoConfiguration {
@Bean
public NacosServiceManager nacosServiceManager() {
return new NacosServiceManager();
}
}
可以看到,NacosServiceManager类的实例化交由Spring容器进行管理。 那么它与NacosNamingService的实例化有什么关系呢?再通过上述spring.factories文件,找到NacosDiscoveryEndpointAutoConfiguration类,该类文件内容如下:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Endpoint.class)
@ConditionalOnNacosDiscoveryEnabled
public class NacosDiscoveryEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnAvailableEndpoint
public NacosDiscoveryEndpoint nacosDiscoveryEndpoint(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosDiscoveryEndpoint(nacosServiceManager, nacosDiscoveryProperties);
}
@Bean
@ConditionalOnEnabledHealthIndicator("nacos-discovery")
public HealthIndicator nacosDiscoveryHealthIndicator(
NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
Properties nacosProperties = nacosDiscoveryProperties.getNacosProperties();
return new NacosDiscoveryHealthIndicator(
nacosServiceManager.getNamingService(nacosProperties));
}
}
看一下实例化NacosDiscoveryHealthIndicator时,构造方法的入参,nacosServiceManager.getNamingService(nacosProperties)方法。
进入该方法,内容如下(具体的实现细节将用代码注释说明):
import static com.alibaba.nacos.api.NacosFactory.createNamingService;
public class NacosServiceManager {
private static final Logger log = LoggerFactory.getLogger(NacosServiceManager.class);
private NacosDiscoveryProperties nacosDiscoveryPropertiesCache;
private NamingService namingService;
private NamingMaintainService namingMaintainService;
public NamingService getNamingService(Properties properties) {
// 判断下成员变量namingService是否没空。
if (Objects.isNull(this.namingService)) {
// namingService成员变量为空,调用buildNamingService方法。
buildNamingService(properties);
}
return namingService;
}
private NamingService buildNamingService(Properties properties) {
// 可以看到namingService的实例化采用了单例模式,并使用双重校检机制保证了并发安全
// 但有问题:namingService成员变量没有用volatile修饰,指令重排会造成对象的半初始化问题,虽然该问题的场景很难出现。
if (Objects.isNull(namingService)) {
synchronized (NacosServiceManager.class) {
if (Objects.isNull(namingService)) {
namingService = createNewNamingService(properties);
}
}
}
return namingService;
}
private NamingService createNewNamingService(Properties properties) {
try {
// 注意:调用的是NacosFactory的静态方法(CSDN不支持代码的斜体),看官们看一下类头的import信息,导入了NacosFactory类的createNamingService静态方法(Java1.5支持导入类的静态成员,包括静态方法)。
return createNamingService(properties);
}
catch (NacosException e) {
throw new RuntimeException(e);
}
}
}
接着看一下NacosFactory类的createNamingService(Properties properties)方法,内容如下:
public class NacosFactory {
public static NamingService createNamingService(Properties properties) throws NacosException {
return NamingFactory.createNamingService(properties);
}
}
看一下NamingFactory类的createNamingService(Properties properties)方法,内容如下:
public class NamingFactory {
public static NamingService createNamingService(Properties properties) throws NacosException {
try {
// 通过NacosNamingService的完全限定名获取该类的类对象
Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
// 反射获取指定入参的构造函数对象
Constructor constructor = driverImplClass.getConstructor(Properties.class);
// 实例化NacosNamingService
return (NamingService) constructor.newInstance(properties);
} catch (Throwable e) {
throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
}
}
}
以上就是NacosNamingService的加载流程。通过源码分析我们可以看到源码使用了哪些设计模式:
(1)单例模式,NacosServiceManager中的buildNamingService(Properties properties)方法。
(2)静态工厂模式,NacosFactory和NamingFactory。
上述源码也有问题,那就是对象的半初始化问题。不知各位看官们对此有什么高见,烦请给予评论。