SpringCloud部分源码解析
发布日期:2021-07-01 00:01:51 浏览次数:2 分类:技术文章

本文共 22431 字,大约阅读时间需要 74 分钟。

SpringCloud集成了很多第三方框架,把它的全部源码拿出来解析几本书都讲不完,也不太现实,本文带领读者分析其中一小部分源码(其余源码读者有兴趣可以继续跟进),包括eureka-server、config、zuul的starter部分,分析其启动原理。 

如果我们开发出一套框架,要和SpringBoot集成,就需要放到它的starter里。因此我们分析启动原理,直接从每个框架的starter开始分析即可。

Eureka-Server源码解析

我们知道,要实现注册与发现,需要在启动类加上@EnableEurekaServer注解,我们进入其源码:

@EnableDiscoveryClient//表示eurekaserver也是一个客户端服务@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(EurekaServerMarkerConfiguration.class)public @interface EnableEurekaServer {}

注意看@Import注解,这个注解导入了EurekaServerMarkerConfiguration类,继续跟进这个类:

/** * Responsible for adding in a marker bean to activate * {@link EurekaServerAutoConfiguration} * * @author Biju Kunjummen */@Configurationpublic class EurekaServerMarkerConfiguration {    @Bean    public Marker eurekaServerMarkerBean() {        return new Marker();    }    class Marker {    }}

通过上面的注释,我们继续查看EurekaServerAutoConfiguration类的源码:

@Configuration@Import(EurekaServerInitializerConfiguration.class)@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)@EnableConfigurationProperties({ EurekaDashboardProperties.class,        InstanceRegistryProperties.class })@PropertySource("classpath:/eureka/server.properties")public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {    /**     * List of packages containing Jersey resources required by the Eureka server     */    private static 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();    @Bean    public HasFeatures eurekaServerFeature() {        return HasFeatures.namedFeature("Eureka Server",                EurekaServerAutoConfiguration.class);    }    //如果eureka.client.registerWithEureka=true,则把自己注册进去    @Configuration    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;        }    }    //实例化eureka-server的界面    @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;    }    class CloudServerCodecs extends DefaultServerCodecs {        public CloudServerCodecs(EurekaServerConfig serverConfig) {            super(getFullJson(serverConfig),                    CodecWrappers.getCodec(CodecWrappers.JacksonJsonMini.class),                    getFullXml(serverConfig),                    CodecWrappers.getCodec(CodecWrappers.JacksonXmlMini.class));        }    }    @Bean    public PeerAwareInstanceRegistry peerAwareInstanceRegistry(            ServerCodecs serverCodecs) {        this.eurekaClient.getApplications(); // force initialization        return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,                serverCodecs, this.eurekaClient,                this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(),                this.instanceRegistryProperties.getDefaultOpenForTrafficCount());    }    @Bean    @ConditionalOnMissingBean    public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,            ServerCodecs serverCodecs) {        return new PeerEurekaNodes(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);    }    /**     * Register the Jersey filter     */    @Bean    public FilterRegistrationBean jerseyFilterRegistration(            javax.ws.rs.core.Application eurekaJerseyApp) {        FilterRegistrationBean bean = new FilterRegistrationBean();        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.     */    @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
> classes = new HashSet
>(); for (String basePackage : EUREKA_PACKAGES) { Set
beans = provider.findCandidateComponents(basePackage); for (BeanDefinition bd : beans) { Class
cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader()); classes.add(cls); } } // Construct the Jersey ResourceConfig // Map
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 public FilterRegistrationBean traceFilterRegistration( @Qualifier("webRequestLoggingFilter") Filter filter) { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(filter); bean.setOrder(Ordered.LOWEST_PRECEDENCE - 10); return bean; }}

这个类上有一个注解:@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class),这后面指定的类就是刚才那个类,而@ConditionalOnBean这个注解的作用是:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。 

因此,启动时就会实例化EurekaServerAutoConfiguration这个类。 
@EnableConfigurationProperties({ EurekaDashboardProperties.class, 
InstanceRegistryProperties.class }) 
这个注解就是定义了一些eureka的配置项。

Config源码解析

通过上面的方法,我们找到了ConfigServerAutoConfiguration类:

@Configuration@ConditionalOnBean(ConfigServerConfiguration.Marker.class)@EnableConfigurationProperties(ConfigServerProperties.class)@Import({ EnvironmentRepositoryConfiguration.class, CompositeConfiguration.class, ResourceRepositoryConfiguration.class,        ConfigServerEncryptionConfiguration.class, ConfigServerMvcConfiguration.class, TransportConfiguration.class })public class ConfigServerAutoConfiguration {}

可以发现这个类是空的,只是多了几个注解, 

@EnableConfigurationProperties(ConfigServerProperties.class)表示开启config配置属性。 
最核心的注解是:@Import,他将其他一些配置类导入进这个类,其中: 
EnvironmentRepositoryConfiguration:环境配置类,内置了以下几种环境配置: 
1.Native

@Configuration    @Profile("native")    protected static class NativeRepositoryConfiguration {        @Autowired        private ConfigurableEnvironment environment;        @Bean        public NativeEnvironmentRepository nativeEnvironmentRepository() {            return new NativeEnvironmentRepository(this.environment);        }    }

2.git

@Configuration    @Profile("git")    protected static class GitRepositoryConfiguration extends DefaultRepositoryConfiguration {}

3.subversion

@Configuration    @Profile("subversion")    protected static class SvnRepositoryConfiguration {        @Autowired        private ConfigurableEnvironment environment;        @Autowired        private ConfigServerProperties server;        @Bean        public SvnKitEnvironmentRepository svnKitEnvironmentRepository() {            SvnKitEnvironmentRepository repository = new SvnKitEnvironmentRepository(this.environment);            if (this.server.getDefaultLabel()!=null) {                repository.setDefaultLabel(this.server.getDefaultLabel());            }            return repository;        }    }

4.vault

@Configuration    @Profile("subversion")    protected static class SvnRepositoryConfiguration {        @Autowired        private ConfigurableEnvironment environment;        @Autowired        private ConfigServerProperties server;        @Bean        public SvnKitEnvironmentRepository svnKitEnvironmentRepository() {            SvnKitEnvironmentRepository repository = new SvnKitEnvironmentRepository(this.environment);            if (this.server.getDefaultLabel()!=null) {                repository.setDefaultLabel(this.server.getDefaultLabel());            }            return repository;    }    }

//从代码可以看到Git是配置中心默认环境

@Bean        public MultipleJGitEnvironmentRepository defaultEnvironmentRepository() {            MultipleJGitEnvironmentRepository repository = new MultipleJGitEnvironmentRepository(this.environment);            repository.setTransportConfigCallback(this.transportConfigCallback);            if (this.server.getDefaultLabel()!=null) {                repository.setDefaultLabel(this.server.getDefaultLabel());            }            return repository;        }

我们进入MultipleJGitEnvironmentRepository类:

@ConfigurationProperties("spring.cloud.config.server.git")public class MultipleJGitEnvironmentRepository extends JGitEnvironmentRepository {}

这个类表示可以支持配置多个Git仓库,它继承自JGitEnvironmentRepository类:

public class JGitEnvironmentRepository extends AbstractScmEnvironmentRepository        implements EnvironmentRepository, SearchPathLocator, InitializingBean {/**     * Get the working directory ready.     */    public String refresh(String label) {        Git git = null;        try {            git = createGitClient();            if (shouldPull(git)) {                fetch(git, label);                // checkout after fetch so we can get any new branches, tags,                // ect.                checkout(git, label);                if (isBranch(git, label)) {                    // merge results from fetch                    merge(git, label);                    if (!isClean(git)) {                        logger.warn("The local repository is dirty. Resetting it to origin/" + label + ".");                        resetHard(git, label, "refs/remotes/origin/" + label);                    }                }            } else {                // nothing to update so just checkout                checkout(git, label);            }            // always return what is currently HEAD as the version            return git.getRepository().getRef("HEAD").getObjectId().getName();        } catch (RefNotFoundException e) {            throw new NoSuchLabelException("No such label: " + label, e);        } catch (NoRemoteRepositoryException e) {            throw new NoSuchRepositoryException("No such repository: " + getUri(), e);        } catch (GitAPIException e) {            throw new NoSuchRepositoryException("Cannot clone or checkout repository: " + getUri(), e);        } catch (Exception e) {            throw new IllegalStateException("Cannot load environment", e);        } finally {            try {                if (git != null) {                    git.close();                }            } catch (Exception e) {                this.logger.warn("Could not close git repository", e);            }        }    }}

refresh方法的作用就是configserver会从我们配置的git仓库拉取配置下来。

Zuul源码解析

同理,我们找到Zuul的配置类ZuulProxyAutoConfiguration:

@Configuration@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,        RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,        RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class })@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {    @SuppressWarnings("rawtypes")    @Autowired(required = false)    private List
requestCustomizers = Collections.emptyList(); @Autowired private DiscoveryClient discovery; @Autowired private ServiceRouteMapper serviceRouteMapper; @Override public HasFeatures zuulFeature() { return HasFeatures.namedFeature("Zuul (Discovery)", ZuulProxyAutoConfiguration.class); } @Bean @ConditionalOnMissingBean(DiscoveryClientRouteLocator.class) public DiscoveryClientRouteLocator discoveryRouteLocator() { return new DiscoveryClientRouteLocator(this.server.getServletPrefix(), this.discovery, this.zuulProperties, this.serviceRouteMapper); } //以下是过滤器,也就是之前zuul提到的实现的ZuulFilter接口 // pre filters //路由之前 @Bean public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) { return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties, proxyRequestHelper); } // route filters // 路由时 @Bean public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper, RibbonCommandFactory
ribbonCommandFactory) { RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers); return filter; } @Bean @ConditionalOnMissingBean(SimpleHostRoutingFilter.class) public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) { return new SimpleHostRoutingFilter(helper, zuulProperties); } @Bean public ApplicationListener
zuulDiscoveryRefreshRoutesListener() { return new ZuulDiscoveryRefreshListener(); } @Bean @ConditionalOnMissingBean(ServiceRouteMapper.class) public ServiceRouteMapper serviceRouteMapper() { return new SimpleServiceRouteMapper(); } @Configuration @ConditionalOnMissingClass("org.springframework.boot.actuate.endpoint.Endpoint") protected static class NoActuatorConfiguration { @Bean public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) { ProxyRequestHelper helper = new ProxyRequestHelper(); helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders()); helper.setTraceRequestBody(zuulProperties.isTraceRequestBody()); return helper; } } @Configuration @ConditionalOnClass(Endpoint.class) protected static class RoutesEndpointConfiguration { @Autowired(required = false) private TraceRepository traces; @Bean public RoutesEndpoint zuulEndpoint(RouteLocator routeLocator) { return new RoutesEndpoint(routeLocator); } @Bean public RoutesMvcEndpoint zuulMvcEndpoint(RouteLocator routeLocator, RoutesEndpoint endpoint) { return new RoutesMvcEndpoint(endpoint, routeLocator); } @Bean public ProxyRequestHelper proxyRequestHelper(ZuulProperties zuulProperties) { TraceProxyRequestHelper helper = new TraceProxyRequestHelper(); if (this.traces != null) { helper.setTraces(this.traces); } helper.setIgnoredHeaders(zuulProperties.getIgnoredHeaders()); helper.setTraceRequestBody(zuulProperties.isTraceRequestBody()); return helper; } } private static class ZuulDiscoveryRefreshListener implements ApplicationListener
{ private HeartbeatMonitor monitor = new HeartbeatMonitor(); @Autowired private ZuulHandlerMapping zuulHandlerMapping; @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof InstanceRegisteredEvent) { reset(); } else if (event instanceof ParentHeartbeatEvent) { ParentHeartbeatEvent e = (ParentHeartbeatEvent) event; resetIfNeeded(e.getValue()); } else if (event instanceof HeartbeatEvent) { HeartbeatEvent e = (HeartbeatEvent) event; resetIfNeeded(e.getValue()); } } private void resetIfNeeded(Object value) { if (this.monitor.update(value)) { reset(); } } private void reset() { this.zuulHandlerMapping.setDirty(true); } }}

通过@Import注解可以找到几个类: 

RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration、RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration、RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration 
我们知道zuul提供网关能力,通过上面这几个类就能分析到,它内部其实也是通过接口请求,找到每个服务提供的接口地址。 
进入RibbonCommandFactoryConfiguration类:

public class RibbonCommandFactoryConfiguration {    //以下提供了3个不同的请求模式    @Configuration    @ConditionalOnRibbonRestClient    protected static class RestClientRibbonConfiguration {        @Autowired(required = false)        private Set
zuulFallbackProviders = Collections.emptySet(); @Bean @ConditionalOnMissingBean public RibbonCommandFactory
ribbonCommandFactory( SpringClientFactory clientFactory, ZuulProperties zuulProperties) { return new RestClientRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders); } } @Configuration @ConditionalOnRibbonOkHttpClient @ConditionalOnClass(name = "okhttp3.OkHttpClient") protected static class OkHttpRibbonConfiguration { @Autowired(required = false) private Set
zuulFallbackProviders = Collections.emptySet(); @Bean @ConditionalOnMissingBean public RibbonCommandFactory
ribbonCommandFactory( SpringClientFactory clientFactory, ZuulProperties zuulProperties) { return new OkHttpRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders); } } @Configuration @ConditionalOnRibbonHttpClient protected static class HttpClientRibbonConfiguration { @Autowired(required = false) private Set
zuulFallbackProviders = Collections.emptySet(); @Bean @ConditionalOnMissingBean public RibbonCommandFactory
ribbonCommandFactory( SpringClientFactory clientFactory, ZuulProperties zuulProperties) { return new HttpClientRibbonCommandFactory(clientFactory, zuulProperties, zuulFallbackProviders); } } @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnRibbonHttpClientCondition.class) @interface ConditionalOnRibbonHttpClient { } private static class OnRibbonHttpClientCondition extends AnyNestedCondition { public OnRibbonHttpClientCondition() { super(ConfigurationPhase.PARSE_CONFIGURATION); } @Deprecated //remove in Edgware" @ConditionalOnProperty(name = "zuul.ribbon.httpclient.enabled", matchIfMissing = true) static class ZuulProperty {} @ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true) static class RibbonProperty {} } @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnRibbonOkHttpClientCondition.class) @interface ConditionalOnRibbonOkHttpClient { } private static class OnRibbonOkHttpClientCondition extends AnyNestedCondition { public OnRibbonOkHttpClientCondition() { super(ConfigurationPhase.PARSE_CONFIGURATION); } @Deprecated //remove in Edgware" @ConditionalOnProperty("zuul.ribbon.okhttp.enabled") static class ZuulProperty {} @ConditionalOnProperty("ribbon.okhttp.enabled") static class RibbonProperty {} } @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnRibbonRestClientCondition.class) @interface ConditionalOnRibbonRestClient { } private static class OnRibbonRestClientCondition extends AnyNestedCondition { public OnRibbonRestClientCondition() { super(ConfigurationPhase.PARSE_CONFIGURATION); } @Deprecated //remove in Edgware" @ConditionalOnProperty("zuul.ribbon.restclient.enabled") static class ZuulProperty {} @ConditionalOnProperty("ribbon.restclient.enabled") static class RibbonProperty {} }}

总结

前面带领大家分析了一小段源码,SpringCloud很庞大,不可能一一分析,文本的主要目的就是教大家如何分析源码,从何处下手,以便大家可以按照这种思路继续跟踪下去。

转载地址:https://lynnlovemin.blog.csdn.net/article/details/102493647 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:程序员如何选择适合自己的企业?
下一篇:ARP断网攻击和防御

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年04月22日 06时12分11秒