Spring cloud 中@EnableEurekaClient源码分析

 2019-10-17 21:53  阅读(1072)
文章分类:Spring boot

Spring cloud 中@EnableEurekaClient源码分析

上一篇文章中讲述了**@EnableEurekaClient@EnableDiscoveryClient区别,原想可能底层会有较多不同,但是查看源码的时候发现@EnableEurekaClient本身就是用@EnableDiscoveryClient**来实现的,因此没有多大的研究价值,但是如果继续讲@EnableEurekaClient源码的话,篇幅过长,因此另外单开一篇文章讲述@EnableDiscoveryClient的源码。

首先点进@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 {

    }

这里使用了**@EnableDiscoveryClient修饰,转到@EnableDiscoveryClient**,如下:

/** * 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文件,图如下:

20191017100237\_1.png

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源码讲解就讲完了。

下一篇文章具体讲述EurekaClient服务注册的过程。


来源:[]()

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> Spring cloud 中@EnableEurekaClient源码分析

相关推荐