SpringCloud --- 服务发现注解之@EnableDiscoveryClient与@EnableEurekaClient

博客介绍了Spring Cloud中@EnableEurekaClient和@EurekaDiscoveryClient的区别。discovery service有多种实现,前者基于spring - cloud - netflix,适用于eureka注册中心;后者基于spring - cloud - commons,适用于其他注册中心。还对两者的源码进行了分析讲解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、@EnableEurekaClient

 2、@EurekaDiscoveryClient


下文是从stackoverflow上面找到的对这两者的解释:

There are multiple implementations of "Discovery Service" (eureka, consul, zookeeper). @EnableDiscoveryClient lives in spring-cloud-commons and picks the implementation on the classpath.  @EnableEurekaClient lives in spring-cloud-netflix and only works for eureka. If eureka is on your classpath, they are effectively the same.

原文地址为:What’s the difference between EnableEurekaClient and EnableDiscoveryClient?

意思也就是spring cloud中discovery service有许多种实现(eureka、consul、zookeeper等等),@EnableDiscoveryClient基于spring-cloud-commons, @EnableEurekaClient基于spring-cloud-netflix。

其实用更简单的话来说,就是如果选用的注册中心是eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么推荐使用@EnableDiscoveryClient。

1、@EnableEurekaClient

我们具体的对@EnableEurekaClient的源码看一下,如下:

/**
 * Convenience annotation for clients to enable Eureka discovery configuration
 * (specifically). Use this (optionally) in case you want discovery and know for sure that
 * it is Eureka you want. All it does is turn on discovery and let the autoconfiguration
 * find the eureka classes if they are available (i.e. you need Eureka on the classpath as
 * well).
 *
 * @author Dave Syer
 * @author Spencer Gibb
 */ 
@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
@Inherited 
@EnableDiscoveryClient 
public @interface EnableEurekaClient { }

注解@EnableEurekaClient上有@EnableDiscoveryClient注解,可以说基本就是EnableEurekaClient有@EnableDiscoveryClient的功能,另外上面的注释中提到,其实**@EnableEurekaClient**z注解就是一种方便使用eureka的注解而已,可以说使用其他的注册中心后,都可以使用@EnableDiscoveryClient注解,但是使用@EnableEurekaClient的情景,就是在服务采用eureka作为注册中心的时候,使用场景较为单一。

 Spring boot 2.0之后,@EnableEurekaClient已经不是由@EnableDiscoveryClient实现的了。

 

    /** 
     * Convenience annotation for clients to enable Eureka discovery configuration 
     * (specifically). Use this (optionally) in case you want discovery and know for sure that 
     * it is Eureka you want. All it does is turn on discovery and let the autoconfiguration 
     * find the eureka classes if they are available (i.e. you need Eureka on the classpath as 
     * well). 
     * 
     * @author Dave Syer 
     * @author Spencer Gibb 
     */  
    @Target(ElementType.TYPE)  
    @Retention(RetentionPolicy.RUNTIME)  
    @Documented  
    @Inherited  
    public @interface EnableEurekaClient {  
      
    }  

 2、@EurekaDiscoveryClient

/**
 * Annotation to enable a DiscoveryClient implementation.
 * @author Spencer Gibb
 */ 
@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
@Inherited 
@Import(EnableDiscoveryClientImportSelector.class) 
public @interface EnableDiscoveryClient { }

 注解@EnableDiscoveryClient上有注解@Import(EnableDiscoveryClientImportSelector.class)修饰,@Import不仅可以单独导入一个配置,另外也可以导入普通的java类,并将其声明为一个bean。此处导入EnableDiscoveryClientImportSelector.class后,加载我们用到的bean,EnableDiscoveryClientImportSelector.class源码如下:

/**
 * @author Spencer Gibb
 */ 
@Order(Ordered.LOWEST_PRECEDENCE - 100) 
public class EnableDiscoveryClientImportSelector extends SpringFactoryImportSelector<EnableDiscoveryClient> { 
    @Override protected boolean isEnabled() { 
        return new RelaxedPropertyResolver(
        getEnvironment()).getProperty( "spring.cloud.discovery.enabled", Boolean.class, Boolean.TRUE); 
    } 
      @Override protected boolean hasDefaultFactory() { 
        return true; 
    } 
}

这个类中有一个覆盖父类的方法isEnabled(),返回默认为true,那么说明只要是引入了EnableDiscoveryClientImportSelector类,spring.cloud.discovery.enabled就处于enable状态。

EnableDiscoveryClientImportSelector继承了类SpringFactoryImportSelector。

在关键的方法selectImports中:

 public String[] selectImports(AnnotationMetadata metadata) {
        if (!isEnabled()) {
            return new String[0];
        }
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(this.annotationClass.getName(), true));

        Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
                + metadata.getClassName() + " annotated with @" + getSimpleName() + "?");

        // Find all possible auto configuration classes, filtering duplicates
        List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
                .loadFactoryNames(this.annotationClass, this.beanClassLoader)));

        if (factories.isEmpty() && !hasDefaultFactory()) {
            throw new IllegalStateException("Annotation @" + getSimpleName()
                    + " found, but there are no implementations. Did you forget to include a starter?");
        }

        if (factories.size() > 1) {
            // there should only ever be one DiscoveryClient, but there might be more than
            // one factory
            log.warn("More than one implementation " + "of @" + getSimpleName()
                    + " (now relying on @Conditionals to pick one): " + factories);
        }

        return factories.toArray(new String[factories.size()]);
    }

 关键代码:

  // Find all possible auto configuration classes, filtering duplicates
        List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
                .loadFactoryNames(this.annotationClass, this.beanClassLoader)));

根据这一步来找到configuration class,这里的SpringFactoriesLoader.loadFactoryNames就是根据配置文件来load class,转到SpringFactoriesLoader.loadFactoryNames。源码如下:

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();

        try {
            Enumeration ex = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
            ArrayList result = new ArrayList();

            while(ex.hasMoreElements()) {
                URL url = (URL)ex.nextElement();
                Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
                String factoryClassNames = properties.getProperty(factoryClassName);
                result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
            }

            return result;
        } catch (IOException var8) {
            throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
        }
    }

这里loadMETA-INF下的spring.factories文件,这个文件中就是load那几个配置,这里要提一点的就是这个spring.factories指的是@EnableEurekaClient对应源码中META-INF下的spring.factories文件,

spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration

org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

打开EurekaClientConfigServerAutoConfiguration,如下:

package org.springframework.cloud.netflix.eureka.config;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.config.server.config.ConfigServerProperties;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.discovery.EurekaClient;

/**
 * Extra configuration for config server if it happens to be a Eureka instance.
 *
 * @author Dave Syer
 */
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass({ EurekaInstanceConfigBean.class, EurekaClient.class,
        ConfigServerProperties.class })
public class EurekaClientConfigServerAutoConfiguration {

    @Autowired(required = false)
    private EurekaInstanceConfig instance;

    @Autowired(required = false)
    private ConfigServerProperties server;

    @PostConstruct
    public void init() {
        if (this.instance == null || this.server == null) {
            return;
        }
        String prefix = this.server.getPrefix();
        if (StringUtils.hasText(prefix)) {
            this.instance.getMetadataMap().put("configPath", prefix);
        }
    }

}

这个类上的注解为:

@ConditionalOnClass({ EurekaInstanceConfigBean.class, EurekaClient.class,
        ConfigServerProperties.class })

意义为当前程序中存在EurekaInstanceConfigBean或者EurekaClient,或者ConfigServerProperties的时候,当前这个配置类会被进行加载,否则不会加载。

这个在加载EurekaClient.class,实例化EurekaClient,但是EurekaClient本身是由DiscoveryClient来实现的,代码如下:
 

/**
 * Define a simple interface over the current DiscoveryClient implementation.
 *
 * This interface does NOT try to clean up the current client interface for eureka 1.x. Rather it tries
 * to provide an easier transition path from eureka 1.x to eureka 2.x.
 *
 * EurekaClient API contracts are:
 *  - provide the ability to get InstanceInfo(s) (in various different ways)
 *  - provide the ability to get data about the local Client (known regions, own AZ etc)
 *  - provide the ability to register and access the healthcheck handler for the client
 *
 * @author David Liu
 */
@ImplementedBy(DiscoveryClient.class)
public interface EurekaClient extends LookupService {

在实例化DiscoveryClient后,对应的服务注册服务就开始运行起来了,由此基本的@EnableEurekaClient源码讲解就讲完了。

转自:https://blog.youkuaiyun.com/MyronCham/article/details/84312928

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值