一、先看看@EnableEurekaServer注解
老规矩,用过Spring-Cloud-Netflix-Eureka
的都知道,想要用Eureka,要先在启动类上加上@EnableEurekaServer
注解,Spring-Cloud会自动注入Eureka-Server相关的内容。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}
从类中可以看到@EnableEurekaServer
上通过@Import
注解导入了EurekaServerMarkerConfiguration
类,那么就看看这个类中做了什么吧~
@Configuration(proxyBeanMethods = false)
public class EurekaServerMarkerConfiguration {
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
class Marker {
}
}
这里可以看到在EurekaServerMarkerConfiguration
类中注册了一个Marker
的Bean,但是就是一个空类,什么操作都没有。此时看似线索断了,但是熟悉Spring的人应该能想到,它大概率是用在@Conditional
系列注解上了~就算不熟悉也没关系,我们可以通过idea的工具来帮忙查找他是在哪里被引用了。可以用鼠标点击类名,再按ALT+F7
。
果然,这个类是被标记在EurekaServerAutoConfiguration
类的上面,必须要现有urekaServerMarkerConfiguration.Marker
,EurekaServerAutoConfiguration
才能被加载。
二、观察一下EurekaServerAutoConfiguration
通过上面的操作,我们知道了在加上@EnableEurekaServer
的情况下,Spring-Cloud会自动加载EurekaServerAutoConfiguration
的内容,那我们就观察一下它吧。
package org.springframework.cloud.netflix.eureka.server;
/**
* @author Gunnar Hillert
* @author Biju Kunjummen
* @author Fahim Farook
*/
@Configuration(proxyBeanMethods = false)
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({
EurekaDashboardProperties.class,
InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
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;
/**
* A {@link CloudJacksonJson} instance.
*/
public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson();
@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);
}
static {
CodecWrappers.registerWrapper(JACKSON_JSON);
EurekaJacksonCodec.setInstance(JACKSON_JSON.getCodec());
}
@Bean
public ServerCodecs serverCodecs() {
return new 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(CodecWrappers.XStreamXml.class)
: codec;
}
@Bean
@ConditionalOnMissingBean
public ReplicationClientAdditionalFilters replicationClientAdditionalFilters() {
return new ReplicationClientAdditionalFilters(Collections.emptySet());
}
@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
ServerCodecs serverCodecs) {
this.eurekaClient.getApplications(); // force initialization
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,
ReplicationClientAdditionalFilters replicationClientAdditionalFilters) {
return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
this.eurekaClientConfig, serverCodecs, this.applicationInfoManager,
replicationClientAdditionalFilters);
}
@Bean
@ConditionalOnMissingBean
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(
javax.ws.rs.core.Application eurekaJerseyApp) {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<Filter>();
bean.setFilter(new ServletContainer(eurekaJerseyApp));
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
bean.setUrlPatterns(
Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));
return bean;
}
/**
* Construct a Jersey {@link javax.ws.rs.core.Application} with all the resources
* required by the Eureka server.
* @param environment an {@link Environment} instance to retrieve classpath resources
* @param resourceLoader a {@link ResourceLoader} instance to get classloader from
* @return created {@link Application} object
*/
@Bean
public javax.ws.rs.core.Application jerseyApplication(Environment environment,
ResourceLoader resourceLoader) {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
false, environment);
// Filter to include only classes that have a particular annotation.
//
provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
// Find classes in Eureka packages (or subpackages)
//
Set<Class<?>> classes = new HashSet<>();
for (String basePackage : EUREKA_PACKAGES) {
Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);
for (BeanDefinition bd : beans) {
Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(),
resourceLoader.getClassLoader());
classes.add(cls);
}
}
// Construct the Jersey ResourceConfig
Map<String, Object> propsAndFeatures = new HashMap<>();
propsAndFeatures.put(
// Skip static content used by the webapp
ServletContainer.PROPERTY_WEB_PAGE_CONTENT_REGEX,
EurekaConstants.DEFAULT_PREFIX + "/(fonts|images|css|js)/.*");
DefaultResourceConfig rc = new DefaultResourceConfig(classes);
rc.setPropertiesAndFeatures(propsAndFeatures);
return rc;
}
@Bean
@ConditionalOnBean(name = "httpTraceFilter")
public FilterRegistrationBean<?> traceFilterRegistration(
@Qualifier("httpTraceFilter") Filter filter) {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<Filter>();
bean.setFilter(filter);
bean.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return bean;
}
@Configuration(proxyBeanMethods = false)
protected static class EurekaServerConfigBeanConfiguration {
@Bean
@ConditionalOnMissingBean
public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
EurekaServerConfigBean server = new EurekaServerConfigBean();
if (clientConfig.shouldRegisterWithEureka()) {
// Set a sensible default if we are supposed to replicate
server.setRegistrySyncRetries(5);
}
return server;
}
}
}
上面可以看到,在此类中配置了许多Bean,其中EurekaServerBootstrap
最值得我们注意,因为它的类名和原生的EurekaBootStrap
太像了,直觉告诉我们这个类就是启动类。我们可以点进去看一下
果然,和原生的类中代码是一模一样的,一个字都没变。但是此时又有一个疑问了,这个contextInitialized
是被谁调用的呢?
三、是谁调用了EurekaServerBootstrap的contextInitialized方法?
老样子,在idea中按Ctrl+鼠标左键就知道了
:
*/
package org.springframework.cloud.netflix.eureka.server;
/**
* @author Dave Syer
*/
@Configuration(proxyBeanMethods = false)
public class EurekaServerInitializerConfiguration
implements ServletContextAware, SmartLifecycle, Ordered {
@Override
public void start() {
new Thread(() -> {
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();
}
}
可以看到,是一个名为EurekaServerInitializerConfiguration
的类调用了它,并且通过观察发现EurekaServerInitializerConfiguration
实现了SmartLifecycle
接口,SmartLifecycle
接口又继承了Lifecycle
接口,通过Lifecycle
接口回调start
方法新起一个线程来启动Euerka不清楚\Lifecycle\
可以复习一下Spring的源码
那么此时细心的朋友可能会问了,EurekaServerInitializerConfiguration
又是被谁加载的呢?往上看,在EurekaServerAutoConfiguration
中是不是有一个@Import(EurekaServerInitializerConfiguration.class)
四、其他
到这里大家应该知道原生Eureka和Spring-Cloud中的Eureka的启动入口了吧在这里提一句,其实zuul和Spring-Cloud整合也是和Eureka一样哟,有兴趣的朋友可以看看
讲一下Lifecycle
接口吧~它是Spring2.0版本开始引入的,在Spring-context
工程中。具体是在Spring容器刷新中调用的,额,直接上图吧:
package org.springframework.context;
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}