@EnableEurekaClient注解,从源码的角度分析是如何work的

NetFlix Eureka client

Eureka client 负责与Eureka Server 配合向外提供注册与发现服务接口。首先看下eureka client是怎么定义,

Netflix的 eureka client的行为在LookupService中定义,Lookup service for finding active instances,定义了,

从outline中能看到起“规定”了如下几个最基本的方法。服务发现必须实现的基本类:com.netflix.discovery.shared.LookupService

Eureka client与Spring Cloud Eureka Client类图,如下所示:

前缀,带有S的是Spring Cloud封装的,带有N是NetFlix原生的。

public class EurekaDiscoveryClient implements DiscoveryClient {
public static final String DESCRIPTION = "Spring Cloud Eureka Discovery Client";
private final EurekaInstanceConfig config;
private final EurekaClient eurekaClient; //Netflix中的Eureka Client public String description() {
return "Spring Cloud Eureka Discovery Client";
} public ServiceInstance getLocalServiceInstance() {
return new ServiceInstance() {
public String getServiceId() {
return EurekaDiscoveryClient.this.config.getAppname();
} public String getHost() {
return EurekaDiscoveryClient.this.config.getHostName(false);
} public int getPort() {
return EurekaDiscoveryClient.this.config.getNonSecurePort();
} public boolean isSecure() {
return EurekaDiscoveryClient.this.config.getSecurePortEnabled();
} public URI getUri() {
return DefaultServiceInstance.getUri(this);
} public Map<String, String> getMetadata() {
return EurekaDiscoveryClient.this.config.getMetadataMap();
}
};
} public List<ServiceInstance> getInstances(String serviceId) {
List infos = this.eurekaClient.getInstancesByVipAddress(serviceId, false);
ArrayList instances = new ArrayList();
Iterator var4 = infos.iterator(); while(var4.hasNext()) {
InstanceInfo info = (InstanceInfo)var4.next();
instances.add(new EurekaDiscoveryClient.EurekaServiceInstance(info));
} return instances;
} public List<String> getServices() {
Applications applications = this.eurekaClient.getApplications();
if(applications == null) {
return Collections.emptyList();
} else {
List registered = applications.getRegisteredApplications();
ArrayList names = new ArrayList();
Iterator var4 = registered.iterator(); while(var4.hasNext()) {
Application app = (Application)var4.next();
if(!app.getInstances().isEmpty()) {
names.add(app.getName().toLowerCase());
}
} return names;
}
} @ConstructorProperties({"config", "eurekaClient"})
public EurekaDiscoveryClient(EurekaInstanceConfig config, EurekaClient eurekaClient) {
this.config = config;
this.eurekaClient = eurekaClient;
} public static class EurekaServiceInstance implements ServiceInstance {
private InstanceInfo instance; EurekaServiceInstance(InstanceInfo instance) {
this.instance = instance;
} public InstanceInfo getInstanceInfo() {
return this.instance;
} public String getServiceId() {
return this.instance.getAppName();
} public String getHost() {
return this.instance.getHostName();
} public int getPort() {
return this.isSecure()?this.instance.getSecurePort():this.instance.getPort();
} public boolean isSecure() {
return this.instance.isPortEnabled(PortType.SECURE);
} public URI getUri() {
return DefaultServiceInstance.getUri(this);
} public Map<String, String> getMetadata() {
return this.instance.getMetadata();
}
}
}

EurekaDiscoveryClient实现了DiscoveryClient,并依赖于com.netflix.discovery.EurekaClient

点开com.netflix.discovery.EurekaClient查看代码,可以看出EurekaClient继承了LookupService并实现了EurekaClient接口。

@ImplementedBy(DiscoveryClient.class)
public interface EurekaClient extends LookupService {
//其余省略
}

com.netflix.discovery.DiscoveryClient是netflix使用的客户端,从其class的注释可以看到他主要做这几件事情:
a) Registering the instance with Eureka Server
b) Renewalof the lease with Eureka Server
c) Cancellation of the lease from Eureka Server during shutdown

其中com.netflix.discovery.DiscoveryClient实现了com.netflix.discovery.EurekaClient,

而spring Cloud中的org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient

依赖于com.netflix.discovery.EurekaClient,因此Spring Cloud与NetFlix的关系由此联系到一起。

@Singleton
public class DiscoveryClient implements EurekaClient {
private static final Logger logger = LoggerFactory.getLogger(DiscoveryClient.class);
// Constants
public static final String HTTP_X_DISCOVERY_ALLOW_REDIRECT = "X-Discovery-AllowRedirect";
//其余省略
}

@EnableEurekaClient注解入口分析,分析主要调用链中的类和方法。

通过@EnableEurekaClient这个简单的注解,在spring cloud应用启动的时候,就可以把EurekaDiscoveryClient注入,继而使用NetFlix提供的Eureka client。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@EnableDiscoveryClient
public @interface EnableEurekaClient {
}

EnableEurekaClient上面加入了另外一个注解@EnableDiscoveryClient,看看这个注解的代码如下所示:

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

这个注解import了EnableDiscoveryClientImportSelector.class这样一个类,其实就是通过这个类来加载需要用到的bean。
点开EnableDiscoveryClientImportSelector类,如下代码:

/**
* @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;
} }

看到这里有覆盖了父类SpringFactoryImportSelector的一个方法isEnabled,注意,默认是TRUE,也就是只要import了这个配置,就会enable。

在其父类org.springframework.cloud.commons.util.SpringFactoryImportSelector
String[] selectImports(AnnotationMetadata metadata)方法中正是根据这个标记类判定是否加载如下定义的类

@Override
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()]);
}

在源码中70-71行,即在
org.springframework.core.io.support.SpringFactoriesLoader 中的109行的loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)方法

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

实际调用loadFactoryNames其实加载META-INF/spring.factories下的class。

具体@EnableEurekaClien注解开启之后,服务启动后,服务就查以注册了

@EnableEurekaClient源码分析的更多相关文章

  1. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  2. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  3. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  4. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  5. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  6. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  7. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  8. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  9. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

随机推荐

  1. 微信小程序 功能函数 计时器

    let lovetime = setInterval(function () { let str = '(' + n + ')' + '重新获取' that.setData({ getText2: s ...

  2. 《Effective C#》快速笔记(六)- - C# 高效编程要点补充

    目录 四十五.尽量减少装箱拆箱 四十六.为应用程序创建专门的异常类 四十七.使用强异常安全保证 四十八.尽量使用安全的代码 四十九.实现与 CLS 兼容的程序集 五十.实现小尺寸.高内聚的程序集 这是 ...

  3. 判断集合不为空Java

    list.size>0判断集合size是否大于0 list.isEmpty()判断集合是否为空,返回布尔值

  4. SQL局部变量

    声明局部变量 局部变量的声明需要使用declare 语句.并且必须以@开头 declare { @varaible_name datatype[,...n] } varaible_name :局部变量 ...

  5. cf1073d Berland Fair

    ~~~题面~~~ 题解: 可以发现,每走完一圈付的钱和买的数量是有周期性的,即如果没有因为缺钱而买不起某家店的东西,那么这一圈的所以决策将会和上一圈相同,这个应该是很好理解的,想想就好了. 因为钱数固 ...

  6. BZOJ2738:矩阵乘法——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=2738 Description 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数 ...

  7. 简述JavaScript的类与对象

    JavaScript语言是动态类型的语言,基于对象并由事件驱动.用面向对象的思想来看,它也有类的概念.JavaScript 没有class关键字,就是用function来实现. 1. 实现方式及变量/ ...

  8. 如何修改即时聊天websocket的端口号

    需要修改2个地方 1.修改\src\main\java\com\woodstudio\framework\modules\chat\StartFilter.java里面的端口号   2.修改\src\ ...

  9. 使用树莓派录音——USB声卡

    原文链接:http://jingyan.eeboard.com/article/73723 我们都知道树莓派只有音频输出口,而没有音频输入的接口,怎么办呢?其实只要一个USB声卡就可以了. USB声卡 ...

  10. [mysql]深入Mysql字符集设置[转载]

    http://www.laruence.com/2008/01/05/12.html 基本概念 • 字符(Character)是指人类语言中最小的表义符号.例如’A'.’B'等: • 给定一系列字符, ...