@EnableEurekaServer的作用

2022-07-28,

作为一个小白,最近在学习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。

本文地址:https://blog.csdn.net/weixin_42163781/article/details/109577336

《@EnableEurekaServer的作用.doc》

下载本文的Word格式文档,以方便收藏与打印。