Spring Cloud Netflix Eureka项目将Netflix公司的Eureka项目加以封装,以适配Spring Boot自动化配置的机制,通过注解在Spring Boot项目启动时启动Eureka Server。
我们通过阅读相关源码,看看这一过程是如何实现的。
回顾我们的eureka-demo中的eureka-server项目,在我们的项目启动类Server上有一个@EnableEurekaServer注解。
@SpringBootApplication
@EnableEurekaServer
public class Server {
//...
}
查看这个注解的定义
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}
可以看到它Import导入了EurekaServerMarkerConfiguration类
@Configuration
public class EurekaServerMarkerConfiguration {
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
class Marker {
}
}
这个EurekaServerMarkerConfiguration会往Spring容器中注入一个eurekaServerMarkerBean,这个Marker是一个空类,那在这里起到什么作用呢?
仔细查看这个类的注释,可以看到它只是一个开关标记,用来激活EurekaServerAutoConfiguration类的。
/**
* Responsible for adding in a marker bean to activate
* {@link EurekaServerAutoConfiguration}
*
* @author Biju Kunjummen
*/
查看EurekaServerAutoConfiguration类的定义
@Configuration
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter
- @Configuration注解表示这是一个配置类,通过@Bean注解声明一些注入到Spring IOC容器中的Bean。
- @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class),表示只要Spring容器中有EurekaServerMarkerConfiguration.Marker.class类的实例存在,那么就会将这个EurekaServerAutoConfiguration也注入到Spring容器中。
- @Import(EurekaServerInitializerConfiguration.class)表明它导入了EurekaServerInitializerConfiguration这个类。
- 此外,这个EurekaServerAutoConfiguration继承自WebMvcConfigurer,可以用来定义Spring MVC的一些配置。
在这个类中,我们并没有发现与启动Eureka相关的代码,那么我们来看看它引入的这个EurekaServerInitializerConfiguration。
@Configuration
public class EurekaServerInitializerConfiguration
implements ServletContextAware, SmartLifecycle, Ordered {
可以看到,这也是一个配置类,同时它实现了ServletContextAware接口,可以在Servlet容器启动后得到ServletContext容器上下文;它还实现了SmartLifecycle,这样在spring 生命周期中会调用这个类相关的方法。比如在spring初始化时,会调用它start方法。
@Override
public void start() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//TODO: is this class even needed now?
eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
log.info("Started Eureka Server");
publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
EurekaServerInitializerConfiguration.this.running = true;
publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
}
catch (Exception ex) {
// Help!
log.error("Could not initialize Eureka servlet context", ex);
}
}
}).start();
}
start方法中启动了一个后台线程,它会执行这一行代码。
eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
eurekaServerBootstrap是EurekaServerInitializerConfiguration类的成员变量,对应的类是EurekaServerBootstrap。
来看看这个类的contextInitialized方法
public void contextInitialized(ServletContext context) {
try {
initEurekaEnvironment();//初始化Eureka运行环境
initEurekaServerContext();//初始化Eureka运行上下文
context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
}
catch (Throwable e) {
log.error("Cannot bootstrap eureka server :", e);
throw new RuntimeException("Cannot bootstrap eureka server :", e);
}
}
这个方法调用了initEurekaEnvironment(),初始化Eureka运行环境;调用了initEurekaServerContext(),初始化Eureka运行上下文。关于这两个方法的细节,我们在后面的文章再细说。
至此Eureka Server就随着Spring容器的一起启起了。
相关流程图如下: