Spring Coud Alibaba中nacos注册的源码解析

在Spring Cloud Alibaba中使用nacos作为服务注册组件,对应客户端首先需要引入依赖

<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

然后在启动类上添加注解

@EnableDiscoveryClient

看一下这个注解的全称org.springframework.cloud.client.discovery.EnableDiscoveryClient,是由Spring Cloud提供的注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {

	/**
	 * If true, the ServiceRegistry will automatically register the local server.
	 * @return - {@code true} if you want to automatically register.
	 */
	boolean autoRegister() default true;

}

通过这个这个注解就会引入一个EnableDiscoveryClientImportSelector类型的bean,在这个类中方法selectImports第一行打上断点,以debug模式启动项目
在这里插入图片描述
从以上的调用栈不难看出,在Spring容器的启动刷新(refresh)的过程中,会进行BeanFactoryPostProcessor的实例化并调用,其中有一个重要的类ConfigurationClassPostProcessor,就是用于处理注解注入的,比如@Configuration,这个类是Spring的核心类,本文不详细探讨,如有兴趣可以参考博客:
ConfigurationClassPostProcessorhttps://blog.csdn.net/m0_37607945/article/details/106676299
此处我们需要知道的是,通过EnableDiscoveryClientImportSelector我们注册了一个BeanDefinition。
在这里插入图片描述
在这里插入图片描述
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration定义如下:

@Configuration
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationConfiguration {

}

这个类的目的主要是用于读取Spring Cloud的配置(默认情况下是开启的,可以通过spring.cloud.service-registry.auto-registration.enabled=false关闭),通过以上的配置,最后会将AutoServiceRegistrationProperties作为一个Bean注入到容器中。
在这里插入图片描述
那么这个Bean何时起效呢?在spring-cloud-commons这个包中存在一个类org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration,这个类是通过SPI机制导入的(spring.factories)。
在这里插入图片描述
同样按照相同的模式spring-cloud-starter-alibaba-nacos-discovery也引入了一个自动注入的配置类com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration,这个类上面注解中包含如下信息

@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
		AutoServiceRegistrationAutoConfiguration.class,
		NacosDiscoveryAutoConfiguration.class })

不难看出,其中有两个类正是上面我们说到的类,而且在NacosServiceRegistryAutoConfiguration自动配置之前,会首先处理AutoServiceRegistrationConfigurationAutoServiceRegistrationAutoConfigurationNacosDiscoveryAutoConfiguration
其中AutoServiceRegistrationConfigurationAutoServiceRegistrationAutoConfiguration是由Spring Cloud官方提供的,而NacosDiscoveryAutoConfigurationNacosServiceRegistryAutoConfiguration是由spring-cloud-alibaba提供的。前者是针对服务与注册的公共逻辑,支持包括但不局限于nacos、consul、zookeeper以及eruka等这些服务与注册组件,而后者是专门针对nacos的。
在这里插入图片描述
以上这个类是在何时起作用的呢?主要是bean的实例化的问题,首先在web容器的初始化过程中onRefresh会实例化一些Bean,然后在在Spring容器的刷新之后也会进行一些非懒加载bean的实例化finishBeanFactoryInitialization.以下从这两个点来看看对nacos中这些bean的影响。

web容器的启动过程onRefresh

如果Spring Boot以web程序启动的话,那么代表容器的类型为org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext,那么在容器刷新的过程中,会进入onRefresh这个方法,这个方法做了一个扩展,就是启动一个web服务器,默认情况下为tomcat.
在这里插入图片描述
在这里插入图片描述
web容器的初始化

package org.springframework.boot.web.embedded.tomcat;

import java.util.Set;

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.boot.web.servlet.ServletContextInitializer;

/**
 * {@link ServletContainerInitializer} used to trigger {@link ServletContextInitializer
 * ServletContextInitializers} and track startup errors.
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 */
class TomcatStarter implements ServletContainerInitializer {

	private static final Log logger = LogFactory.getLog(TomcatStarter.class);

	private final ServletContextInitializer[] initializers;

	private volatile Exception startUpException;

	TomcatStarter(ServletContextInitializer[] initializers) {
		this.initializers = initializers;
	}

	@Override
	public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
		try {
			for (ServletContextInitializer initializer : this.initializers) {
				initializer.onStartup(servletContext);
			}
		}
		catch (Exception ex) {
			this.startUpException = ex;
			// Prevent Tomcat from logging and re-throwing when we know we can
			// deal with it in the main thread, but log for information here.
			if (logger.isErrorEnabled()) {
				logger.error("Error starting Tomcat context. Exception: " + ex.getClass().getName() + ". Message: "
						+ ex.getMessage());
			}
		}
	}

	public Exception getStartUpException() {
		return this.startUpException;
	}

}

在这里插入图片描述
最后一个为class org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext$Lambda@5048,这不是ServletWebServerApplicationContext类型对象,而是在这个类中定义一个匿名内部类,
这是定义在里面的匿名内部类,在创建服务器createWebServer时传入的:
在这里插入图片描述
根据传入的ServletContextInitializer创建WebServer对象。
在这里插入图片描述

首先看一下这个接口的定义:

@FunctionalInterface
public interface ServletContextInitializer {

	/**
	 * Configure the given {@link ServletContext} with any servlets, filters, listeners
	 * context-params and attributes necessary for initialization.
	 * @param servletContext the {@code ServletContext} to initialize
	 * @throws ServletException if any call against the given {@code ServletContext}
	 * throws a {@code ServletException}
	 */
	void onStartup(ServletContext servletContext) throws ServletException;

}
/**
 * Returns the {@link ServletContextInitializer} that will be used to complete the
 * setup of this {@link WebApplicationContext}.
 * @return the self initializer
 * @see #prepareWebApplicationContext(ServletContext)
 */
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
	// 对应onStartup的实现	
	return this::selfInitialize;
}

// 此处是ServletContextInitializer的onStartup实现
private void selfInitialize(ServletContext servletContext) throws ServletException {
	prepareWebApplicationContext(servletContext);
	registerApplicationScope(servletContext);
	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}

额外两个实现类也是在创建tomcat服务器的时候创建的,依次调用方法和对应源码如下所示:
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#prepareContext
org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory#mergeInitializers

	/**
	 * Utility method that can be used by subclasses wishing to combine the specified
	 * {@link ServletContextInitializer} parameters with those defined in this instance.
	 * @param initializers the initializers to merge
	 * @return a complete set of merged initializers (with the specified parameters
	 * appearing first)
	 */
	protected final ServletContextInitializer[] mergeInitializers(ServletContextInitializer... initializers) {
		List<ServletContextInitializer> mergedInitializers = new ArrayList<>();
		mergedInitializers.add((servletContext) -> this.initParameters.forEach(servletContext::setInitParameter));
		mergedInitializers.add(new SessionConfiguringInitializer(this.session));
		mergedInitializers.addAll(Arrays.asList(initializers));
		mergedInitializers.addAll(this.initializers);
		return mergedInitializers.toArray(new ServletContextInitializer[0]);
	}

在这里插入图片描述
当容器完全启动之后,就会执行以上三个ServletContextInitializer接口实现类的 onStartup方法。
前面两个分别是处理参数和session的,与本文关系不大,因此略过。

容器刷新操作

针对最后一个的分析

private void selfInitialize(ServletContext servletContext) throws ServletException {
	// 准备一个WebApplicationContext对象	
	prepareWebApplicationContext(servletContext);
	// 注册scope
	registerApplicationScope(servletContext);
	// 注册servletContext容器上下文相关信息
	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
	// 容器启动
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}

准备WebApplicationContext对象

  • 首先从servletContext中获取org.springframework.web.context.WebApplicationContext.ROOT属性,默认情况下为null
  • 将当前类设置为org.springframework.web.context.WebApplicationContext.ROOT属性值,当前类对象为org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3e2943ab, started on Fri Jul 31 13:53:02 CST 2020, parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@2d0399f4.
  • Set the ServletContext that this WebApplicationContext runs in.
// String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
/**
 - Prepare the {@link WebApplicationContext} with the given fully loaded
 - {@link ServletContext}. This method is usually called from
 - {@link ServletContextInitializer#onStartup(ServletContext)} and is similar to the
 - functionality usually provided by a {@link ContextLoaderListener}.
 - @param servletContext the operational servlet context
 */
protected void prepareWebApplicationContext(ServletContext servletContext) {
	Object rootContext = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
	if (rootContext != null) {
		if (rootContext == this) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - "
							+ "check whether you have multiple ServletContextInitializers!");
		}
		return;
	}
	Log logger = LogFactory.getLog(ContextLoader.class);
	servletContext.log("Initializing Spring embedded WebApplicationContext");
	try {
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
		if (logger.isDebugEnabled()) {
			logger.debug("Published root WebApplicationContext as ServletContext attribute with name ["
					+ WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
		}
		setServletContext(servletContext);
		if (logger.isInfoEnabled()) {
			long elapsedTime = System.currentTimeMillis() - getStartupDate();
			logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
		}
	}
	catch (RuntimeException | Error ex) {
		logger.error("Context initialization failed", ex);
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
		throw ex;
	}
}

此时控制台打印一下日志

2020-07-31 14:15:15.639  INFO 13928 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-07-31 14:17:48.231  INFO 13928 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1484350 ms

在这里插入图片描述
**注册ServletContextScope **

  • 首先创建一个ServletContextScope对象
  • 在bean工厂中进行注册,名称为application的scope
  • 设置到容器上下文servletContext对象属性当中
private void registerApplicationScope(ServletContext servletContext) {
	ServletContextScope appScope = new ServletContextScope(servletContext);
	getBeanFactory().registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
	// Register as ServletContext attribute, for ContextCleanupListener to detect it.
	servletContext.setAttribute(ServletContextScope.class.getName(), appScope);
}

在这里插入图片描述
在这里插入图片描述
注册servletContext容器上下文相关信息
调用的方法如下:
org.springframework.web.context.support.WebApplicationContextUtils#registerEnvironmentBeans(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, javax.servlet.ServletContext)

  • 将当前servletContext注册为名称为servletContext的单例Bean
  • 如果参数servletConfig不为null(默认为null),则注册名称为servletConfig的单例bean
  • 根据当前servletContext的初始化参数构建一个Map对象,并注册为名称为contextParameters的单例bean
  • 根据当前servletContext的参数构建一个Map对象,并注册为名称为contextAttributes的单例bean

对应源码:

/**
 * Register web-specific environment beans ("contextParameters", "contextAttributes")
 * with the given BeanFactory, as used by the WebApplicationContext.
 * @param bf the BeanFactory to configure
 * @param sc the ServletContext that we're running within
 */
public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, @Nullable ServletContext sc) {
	registerEnvironmentBeans(bf, sc, null);
}

/**
 * Register web-specific environment beans ("contextParameters", "contextAttributes")
 * with the given BeanFactory, as used by the WebApplicationContext.
 * @param bf the BeanFactory to configure
 * @param servletContext the ServletContext that we're running within
 * @param servletConfig the ServletConfig
 */
public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf,
		@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {

	if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) {
		bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);
	}

	if (servletConfig != null && !bf.containsBean(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME)) {
		bf.registerSingleton(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME, servletConfig);
	}

	if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) {
		Map<String, String> parameterMap = new HashMap<>();
		if (servletContext != null) {
			Enumeration<?> paramNameEnum = servletContext.getInitParameterNames();
			while (paramNameEnum.hasMoreElements()) {
				String paramName = (String) paramNameEnum.nextElement();
				parameterMap.put(paramName, servletContext.getInitParameter(paramName));
			}
		}
		if (servletConfig != null) {
			Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames();
			while (paramNameEnum.hasMoreElements()) {
				String paramName = (String) paramNameEnum.nextElement();
				parameterMap.put(paramName, servletConfig.getInitParameter(paramName));
			}
		}
		bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME,
				Collections.unmodifiableMap(parameterMap));
	}

	if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) {
		Map<String, Object> attributeMap = new HashMap<>();
		if (servletContext != null) {
			Enumeration<?> attrNameEnum = servletContext.getAttributeNames();
			while (attrNameEnum.hasMoreElements()) {
				String attrName = (String) attrNameEnum.nextElement();
				attributeMap.put(attrName, servletContext.getAttribute(attrName));
			}
		}
		bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME,
				Collections.unmodifiableMap(attributeMap));
	}
}

在这里插入图片描述
在这里插入图片描述
onStartup
创建一个ServletContextInitializerBeans对象,并传入bean工厂对象

/**
 * Returns {@link ServletContextInitializer}s that should be used with the embedded
 * web server. By default this method will first attempt to find
 * {@link ServletContextInitializer}, {@link Servlet}, {@link Filter} and certain
 * {@link EventListener} beans.
 * @return the servlet initializer beans
 */
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
	return new ServletContextInitializerBeans(getBeanFactory());
}
@SafeVarargs
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
		Class<? extends ServletContextInitializer>... initializerTypes) {
	this.initializers = new LinkedMultiValueMap<>();
	// 默认情况下initializerTypes集合为空的 因此此处会返回包含ServletContextInitializer的集合
	this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
			: Collections.singletonList(ServletContextInitializer.class);
	// 从容器中获取ServletContextInitializer类型的bean		
	addServletContextInitializerBeans(beanFactory);
	addAdaptableBeans(beanFactory);
	List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
			.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
			.collect(Collectors.toList());
	this.sortedList = Collections.unmodifiableList(sortedInitializers);
	logMappings(this.initializers);
}

在这里插入图片描述
在这里插入图片描述
其中servletEndpointRegistrar是在org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration.WebMvcServletEndpointManagementContextConfiguration类中定义的,只有在SERVLET环境下才会注入。

在这里插入图片描述
对应源码如下所示:

@Bean
public ServletEndpointRegistrar servletEndpointRegistrar(WebEndpointProperties properties,
		ServletEndpointsSupplier servletEndpointsSupplier) {
	DispatcherServletPath dispatcherServletPath = this.context.getBean(DispatcherServletPath.class);
	return new ServletEndpointRegistrar(dispatcherServletPath.getRelativePath(properties.getBasePath()),
			servletEndpointsSupplier.getEndpoints());
}

这个类会去构建EndpointBeans,获取Spring容器中包含有org.springframework.boot.actuate.endpoint.annotation.Endpoint注解的bean名称,并用于构建EndpointBean对象。是在WebEndpointAutoConfiguration类中引入。
org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer#discoverEndpoints

private Collection<E> discoverEndpoints() {
	Collection<EndpointBean> endpointBeans = createEndpointBeans();
	addExtensionBeans(endpointBeans);
	return convertToEndpoints(endpointBeans);
}

private Collection<EndpointBean> createEndpointBeans() {
	Map<EndpointId, EndpointBean> byId = new LinkedHashMap<>();
	String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.applicationContext,
			Endpoint.class);
	for (String beanName : beanNames) {
		if (!ScopedProxyUtils.isScopedTarget(beanName)) {
			EndpointBean endpointBean = createEndpointBean(beanName);
			EndpointBean previous = byId.putIfAbsent(endpointBean.getId(), endpointBean);
			Assert.state(previous == null, () -> "Found two endpoints with the id '" + endpointBean.getId() + "': '"
					+ endpointBean.getBeanName() + "' and '" + previous.getBeanName() + "'");
		}
	}
	return byId.values();
}

如下这些bean都包含了这个注解
在这里插入图片描述

而类NacosDiscoveryEndpoint就包含这个注解(对应bean的注入是通过SPI机制完成的,可以参考NacosDiscoveryEndpointAutoConfiguration类)
在这里插入图片描述

@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public NacosDiscoveryEndpoint nacosDiscoveryEndpoint(
		NacosDiscoveryProperties nacosDiscoveryProperties) {
	return new NacosDiscoveryEndpoint(nacosDiscoveryProperties);
}

由于依赖了NacosDiscoveryProperties,这里也会导致NacosDiscoveryProperties的实例化。

@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
	return new NacosDiscoveryProperties();
}

serviceRegistryEndpointBean的初始化

@Configuration
public class ServiceRegistryAutoConfiguration {

	@ConditionalOnBean(ServiceRegistry.class)
	@ConditionalOnClass(Endpoint.class)
	protected class ServiceRegistryEndpointConfiguration {

		@Autowired(required = false)
		private Registration registration;

		@Bean
		@ConditionalOnEnabledEndpoint
		public ServiceRegistryEndpoint serviceRegistryEndpoint(
				ServiceRegistry serviceRegistry) {
			ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(
					serviceRegistry);
			endpoint.setRegistration(this.registration);
			return endpoint;
		}

	}
}

实例化这个Bean会导致ServiceRegistry类型和Registration类型Bean的初始化操作。
在这里插入图片描述
然后又会在构造其中实例化NamingService对象,而这个对象就是用于服务注册与发现的关键类,比如registerInstance注册客户端实例、deregisterInstance取消注册、getAllInstances,selectInstances,selectOneHealthyInstance获取注册的实例、subscribe订阅等。

private final NamingService namingService;

public NacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
	this.nacosDiscoveryProperties = nacosDiscoveryProperties;
	this.namingService = nacosDiscoveryProperties.namingServiceInstance();
}

Registration类型在nacos中的实现为com.alibaba.cloud.nacos.registry.NacosRegistration.
在这里插入图片描述

此处不继续探讨了,可以参考博客
https://blog.csdn.net/m0_37607945/article/details/106441940
返回到ServletContextInitializerBeans类的构造中,通过addServletContextInitializerBeans方法,将ServletContextInitializer类型的bean进行了实例化,并在此过程中实例化了一些相关的bean,其中就包括了nacos的一些bean。然后后处理与ServletFilterEventListener,此处也不深入探讨了。

protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
	MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
	addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
	addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
	for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
		addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
				new ServletListenerRegistrationBeanAdapter());
	}
}

在这里插入图片描述
ServletContextInitializerBeans实现了AbstractCollection<ServletContextInitializer>,对其进行遍历,会遍历在构造中实例化的各个ServletContextInitializer,依次执行onStartup方法,完成web容器的启动过程。
在这里插入图片描述
注册过滤器
在这里插入图片描述
注册servlet
在这里插入图片描述

执行Spring容器的初始化操作finishBeanFactoryInitialization

实例化其他诸如nacosServiceDiscoverynacosAutoServiceRegistration的初始化
com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration#nacosAutoServiceRegistration

@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosAutoServiceRegistration nacosAutoServiceRegistration(
		NacosServiceRegistry registry,
		AutoServiceRegistrationProperties autoServiceRegistrationProperties,
		NacosRegistration registration) {
	// 此时完成了registry、registration、autoServiceRegistrationProperties的实例化	
	return new NacosAutoServiceRegistration(registry,
			autoServiceRegistrationProperties, registration);
}

在这里插入图片描述
NacosAutoServiceRegistration实现了AbstractAutoServiceRegistration抽象类,而NacosServiceRegistry实现了ServiceRegistry接口,这样Spring Cloud Ailbaba就和Spring Cloud统一起来了,NacosAutoServiceRegistration的实例化会将nacos的实现类设置到了AbstractAutoServiceRegistration抽象类中。在AbstractAutoServiceRegistration实现了公共逻辑(模板方式)。另外实现了ApplicationListener<WebServerInitializedEvent>接口。在抽象类中会监听对应的WebServerInitializedEvent事件,通过这个事件进行bind->start->register,最后会调用到真实实现类的逻辑。此处有个大前提,就是在容器中非懒加载的bean都实例化之后才会发布WebServerInitializedEvent事件。
在这里插入图片描述

发布容器事件并进行客户端的注册

@Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
	bind(event);
}

@Deprecated
public void bind(WebServerInitializedEvent event) {
	ApplicationContext context = event.getApplicationContext();
	if (context instanceof ConfigurableWebServerApplicationContext) {
		if ("management".equals(((ConfigurableWebServerApplicationContext) context)
				.getServerNamespace())) {
			return;
		}
	}
	this.port.compareAndSet(0, event.getWebServer().getPort());
	this.start();
}

public void start() {
	// 此方法是一个扩展点  由子类去实现
	if (!isEnabled()) {
		if (logger.isDebugEnabled()) {
			logger.debug("Discovery Lifecycle disabled. Not starting");
		}
		return;
	}

	// only initialize if nonSecurePort is greater than 0 and it isn't already running
	// because of containerPortInitializer below
	if (!this.running.get()) {
		// 此处会发布一个InstancePreRegisteredEvent事件
		this.context.publishEvent(
				new InstancePreRegisteredEvent(this, getRegistration()));
		// 此处会进行注册操作
		register();
		if (shouldRegisterManagement()) {
			registerManagement();
		}
		// 此处会发布一个InstanceRegisteredEvent事件
		this.context.publishEvent(
				new InstanceRegisteredEvent<>(this, getConfiguration()));
		this.running.compareAndSet(false, true);
	}
}

对应nacos中的实现:

@Override
protected boolean isEnabled() {
	return this.registration.getNacosDiscoveryProperties().isRegisterEnabled();
}

配置类中对应的实现com.alibaba.cloud.nacos.NacosDiscoveryProperties

/**
 * if you just want to subscribe, but don't want to register your service, set it to
 * false.
 */
private boolean registerEnabled = true;

子类执行注册

@Override
protected void register() {
	if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {
		log.debug("Registration disabled.");
		return;
	}
	if (this.registration.getPort() < 0) {
		this.registration.setPort(getPort().get());
	}
	super.register();
}

抽象类中的实现

/**
 * Register the local service with the {@link ServiceRegistry}.
 */
protected void register() {
	this.serviceRegistry.register(getRegistration());
}

此处又涉及到一个抽象方法需要在子类中实现

@Override
protected NacosRegistration getRegistration() {
	if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
		this.registration.setPort(this.getPort().get());
	}
	Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");
	return this.registration;
}

Spring Cloud一开始并不知道具体的注册逻辑,也不知道注册的对象实例,但是提供了以下两个接口

public interface Registration extends ServiceInstance {

}
public interface ServiceRegistry<R extends Registration> {

	/**
	 * Registers the registration. A registration typically has information about an
	 * instance, such as its hostname and port.
	 * @param registration registration meta data
	 */
	void register(R registration);
}

前者代表一个客户端实例,而后者则是进行这个客户端实例的注册实现,Spring Cloud在容器实例化之后通过发布事件然后执行对应的注册操作,但是不关心具体注册啥以及具体的注册逻辑,这些都由具体的提供商来实现(仅仅提供标准)。
而对于具体的服务注册与实现的实现这来说,必须实现这两个接口RegistrationServiceRegistry,另外还有抽象类AbstractAutoServiceRegistration。如果没有实现AbstractAutoServiceRegistration这个抽象类(其实是AutoServiceRegistration接口),容器在启动的时候会报错IllegalStateException,对应源代码如下:

@Configuration
@Import(AutoServiceRegistrationConfiguration.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationAutoConfiguration {

	@Autowired(required = false)
	private AutoServiceRegistration autoServiceRegistration;

	@Autowired
	private AutoServiceRegistrationProperties properties;

	@PostConstruct
	protected void init() {
		if (this.autoServiceRegistration == null && this.properties.isFailFast()) {
			throw new IllegalStateException("Auto Service Registration has "
					+ "been requested, but there is no AutoServiceRegistration bean");
		}
	}

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值