SpringCloud部分源码解析

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
 */
@Configuration
public 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很庞大,不可能一一分析,文本的主要目的就是教大家如何分析源码,从何处下手,以便大家可以按照这种思路继续跟踪下去。


分享到:


相關文章: