05、Eureka教程-Spring-Cloud-Netflix-Eureka-Server启动入口寻找

一、先看看@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太像了,直觉告诉我们这个类就是启动类。我们可以点进去看一下
&nbsp;
&nbsp;

果然,和原生的类中代码是一模一样的,一个字都没变。但是此时又有一个疑问了,这个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();

}

&nbsp;
&nbsp;

&nbsp;
&nbsp;

&nbsp;

&nbsp;

&nbsp;