作为一个小白,最近在学习Eureka的源码,没想到开头就遇到了迷惑的地方,下面是我迷惑的地方,特此记录。
问题1:为什么只需要引入依赖以及在application.yml中做好配置,然后在启动类中加入@EnableEurekaServer注解,eureka服务端就可以工作了呢,配置清晰可见,那么问题就出在注解上,这个注解他究竟做了什么事情,可以将eureka与cloud整合呢???
好吧,我费了九牛二虎之力,一点一点的去探究,感觉是我所认为的这样,下面进行讲解,如果有不对的地方,请指正出来,不要让我在错误的道路上越走越远!!!
首先来看这个注解(@EnableEurekaServer)他做了什么事情
package org.springframework.cloud.netflix.eureka.server;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerMarkerConfiguration.class})
public @interface EnableEurekaServer {
}
下面这个是@Import中引用的类,可以看到这个类的作用是向容器中加入一个Marker类,Marker没有做任何事情。
@Configuration
public class EurekaServerMarkerConfiguration {
public EurekaServerMarkerConfiguration() {
}
@Bean
public EurekaServerMarkerConfiguration.Marker eurekaServerMarkerBean() {
return new EurekaServerMarkerConfiguration.Marker();
}
class Marker {
Marker() {
}
}
}
问题2:上面就是@EnableEurekaServer注解底层所做的事情,那么他加入Marker 类的作用是什么呢?
进入下面如图的jar包中:
这个jar包中有一个spring.factories文件,至于为什么要这样找呢,这是因为SpringBoot的SPI机制,SpringBoot在自动装配过程中,最终会加载META-INF/spring.factories配置文件,然后解析等,在JVM高级特性的书中在双亲委派模型中对这个有一定的介绍,具体的可以去看看,这里就不多做讲解了。
这个是spring.factories文件中的内容
下面就是EurekaServerAutoConfiguration 类的定义,这个类加载了多个bean,这些bean完成了eureka server的主要功能
@Configuration
//eureka的核心bean都是在EurekaServerInitializerConfiguration类中进行初始化的
@Import({EurekaServerInitializerConfiguration.class})
//这个注解解释了@EnableEurekaServer注解中Mark类定义的原因,这个注解的作用是:判断当前容器中是否存在指定的bean,如果存在,就把当前加了@ConditionalOnBean注解的类加入到容器中去,(因此Mark类其实只是一个标记类,启动类中的注解@EnableEurekaServer就是控制开关)
@ConditionalOnBean({Marker.class})
//注解的作用是:使使用 @ConfigurationProperties 注解的类生效。
@EnableConfigurationProperties({EurekaDashboardProperties.class, InstanceRegistryProperties.class})
//加载指定的配置文件
@PropertySource({"classpath:/eureka/server.properties"})
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
private static final String[] EUREKA_PACKAGES = new String[]{"com.netflix.discovery", "com.netflix.eureka"};
@Autowired
private ApplicationInfoManager applicationInfoManager;
@Autowired
private EurekaServerConfig eurekaServerConfig;
@Autowired
private EurekaClientConfig eurekaClientConfig;
@Autowired
private EurekaClient eurekaClient;
@Autowired
private InstanceRegistryProperties instanceRegistryProperties;
public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson();
public EurekaServerAutoConfiguration() {
}
@Bean
public HasFeatures eurekaServerFeature() {
return HasFeatures.namedFeature("Eureka Server", EurekaServerAutoConfiguration.class);
}
//看板
@Bean
@ConditionalOnProperty(
prefix = "eureka.dashboard",
name = {"enabled"},
matchIfMissing = true
)
public EurekaController eurekaController() {
return new EurekaController(this.applicationInfoManager);
}
@Bean
public ServerCodecs serverCodecs() {
return new EurekaServerAutoConfiguration.CloudServerCodecs(this.eurekaServerConfig);
}
private static CodecWrapper getFullJson(EurekaServerConfig serverConfig) {
CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getJsonCodecName());
return codec == null ? CodecWrappers.getCodec(JACKSON_JSON.codecName()) : codec;
}
private static CodecWrapper getFullXml(EurekaServerConfig serverConfig) {
CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getXmlCodecName());
return codec == null ? CodecWrappers.getCodec(XStreamXml.class) : codec;
}
//服务注册
@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(ServerCodecs serverCodecs) {
this.eurekaClient.getApplications();
return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.eurekaClient, this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}
@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs) {
return new EurekaServerAutoConfiguration.RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
}
@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.applicationInfoManager);
}
@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry, EurekaServerContext serverContext) {
return new EurekaServerBootstrap(this.applicationInfoManager, this.eurekaClientConfig, this.eurekaServerConfig, registry, serverContext);
}
@Bean
public FilterRegistrationBean jerseyFilterRegistration(Application eurekaJerseyApp) {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new ServletContainer(eurekaJerseyApp));
bean.setOrder(2147483647);
bean.setUrlPatterns(Collections.singletonList("/eureka/*"));
return bean;
}
@Bean
public Application jerseyApplication(Environment environment, ResourceLoader resourceLoader) {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, environment);
provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
Set<Class<?>> classes = new HashSet();
String[] var5 = EUREKA_PACKAGES;
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
String basePackage = var5[var7];
Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);
Iterator var10 = beans.iterator();
while(var10.hasNext()) {
BeanDefinition bd = (BeanDefinition)var10.next();
Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader());
classes.add(cls);
}
}
Map<String, Object> propsAndFeatures = new HashMap();
propsAndFeatures.put("com.sun.jersey.config.property.WebPageContentRegex", "/eureka/(fonts|images|css|js)/.*");
DefaultResourceConfig rc = new DefaultResourceConfig(classes);
rc.setPropertiesAndFeatures(propsAndFeatures);
return rc;
}
@Bean
public FilterRegistrationBean traceFilterRegistration(@Qualifier("httpTraceFilter") Filter filter) {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(filter);
bean.setOrder(2147483637);
return bean;
}
static {
CodecWrappers.registerWrapper(JACKSON_JSON);
EurekaJacksonCodec.setInstance(JACKSON_JSON.getCodec());
}
class CloudServerCodecs extends DefaultServerCodecs {
CloudServerCodecs(EurekaServerConfig serverConfig) {
super(EurekaServerAutoConfiguration.getFullJson(serverConfig), CodecWrappers.getCodec(JacksonJsonMini.class), EurekaServerAutoConfiguration.getFullXml(serverConfig), CodecWrappers.getCodec(JacksonXmlMini.class));
}
}
static class RefreshablePeerEurekaNodes extends PeerEurekaNodes implements ApplicationListener<EnvironmentChangeEvent> {
RefreshablePeerEurekaNodes(final PeerAwareInstanceRegistry registry, final EurekaServerConfig serverConfig, final EurekaClientConfig clientConfig, final ServerCodecs serverCodecs, final ApplicationInfoManager applicationInfoManager) {
super(registry, serverConfig, clientConfig, serverCodecs, applicationInfoManager);
}
public void onApplicationEvent(final EnvironmentChangeEvent event) {
if (this.shouldUpdate(event.getKeys())) {
this.updatePeerEurekaNodes(this.resolvePeerUrls());
}
}
protected boolean shouldUpdate(final Set<String> changedKeys) {
assert changedKeys != null;
if (this.clientConfig.shouldUseDnsForFetchingServiceUrls()) {
return false;
} else if (changedKeys.contains("eureka.client.region")) {
return true;
} else {
Iterator var2 = changedKeys.iterator();
String key;
do {
if (!var2.hasNext()) {
return false;
}
key = (String)var2.next();
} while(!key.startsWith("eureka.client.service-url.") && !key.startsWith("eureka.client.availability-zones."));
return true;
}
}
}
@Configuration
protected static class EurekaServerConfigBeanConfiguration {
protected EurekaServerConfigBeanConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
EurekaServerConfigBean server = new EurekaServerConfigBean();
if (clientConfig.shouldRegisterWithEureka()) {
server.setRegistrySyncRetries(5);
}
return server;
}
}
}
问题3:这个类就是上面@Import({EurekaServerInitializerConfiguration.class})的类,这个类如何将bean初始化并装入容器的呢???
通过这个类的定义可以看到,这个类实现了SmartLifeCycle接口,他通过使用生命周期回调方法,在容器初始化完成之后,将eureka的核心bean初始化,并加入到容器之中。
@Configuration
public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered {
private static final Log log = LogFactory.getLog(EurekaServerInitializerConfiguration.class);
@Autowired
private EurekaServerConfig eurekaServerConfig;
private ServletContext servletContext;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private EurekaServerBootstrap eurekaServerBootstrap;
private boolean running;
private int order = 1;
public EurekaServerInitializerConfiguration() {
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void start() {
(new Thread(new Runnable() {
public void run() {
try {
EurekaServerInitializerConfiguration.this.eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
EurekaServerInitializerConfiguration.log.info("Started Eureka Server");
EurekaServerInitializerConfiguration.this.publish(new EurekaRegistryAvailableEvent(EurekaServerInitializerConfiguration.this.getEurekaServerConfig()));
EurekaServerInitializerConfiguration.this.running = true;
EurekaServerInitializerConfiguration.this.publish(new EurekaServerStartedEvent(EurekaServerInitializerConfiguration.this.getEurekaServerConfig()));
} catch (Exception var2) {
EurekaServerInitializerConfiguration.log.error("Could not initialize Eureka servlet context", var2);
}
}
})).start();
}
private EurekaServerConfig getEurekaServerConfig() {
return this.eurekaServerConfig;
}
private void publish(ApplicationEvent event) {
this.applicationContext.publishEvent(event);
}
public void stop() {
this.running = false;
this.eurekaServerBootstrap.contextDestroyed(this.servletContext);
}
public boolean isRunning() {
return this.running;
}
public int getPhase() {
return 0;
}
public boolean isAutoStartup() {
return true;
}
public void stop(Runnable callback) {
callback.run();
}
public int getOrder() {
return this.order;
}
}
总结:
(1)@EnableEurekaServer的作用是向容器中加入一个标识类Marker 。
(2)EurekaServerAutoConfiguration类上的@ConditionalOnBean({Marker.class})注解通过对容器中有无Marker类,实现了开关控制效果同时这个类装载了多个bean,这些bean是实现eureka服务端的主要功能。
(4)EurekaServerInitializerConfiguration是初始化eureka的实现类,这个类实现了SmartLifeCycle接口,通过生命周期回调方法初始化eureka。