亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談Spring Cloud Ribbon的原理

 更新時(shí)間:2018年02月22日 11:32:41   作者:白色的海  
這篇文章主要介紹了淺談Spring Cloud Ribbon的原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

Ribbon是Netflix發(fā)布的開(kāi)源項(xiàng)目,主要功能是提供客戶端的軟件負(fù)載均衡算法,將Netflix的中間層服務(wù)連接在一起。Ribbon客戶端組件提供一系列完善的配置項(xiàng)如連接超時(shí),重試等。簡(jiǎn)單的說(shuō),就是在配置文件中列出Load Balancer(簡(jiǎn)稱LB)后面所有的機(jī)器,Ribbon會(huì)自動(dòng)的幫助你基于某種規(guī)則(如簡(jiǎn)單輪詢,隨即連接等)去連接這些機(jī)器。我們也很容易使用Ribbon實(shí)現(xiàn)自定義的負(fù)載均衡算法。

說(shuō)起負(fù)載均衡一般都會(huì)想到服務(wù)端的負(fù)載均衡,常用產(chǎn)品包括LBS硬件或云服務(wù)、Nginx等,都是耳熟能詳?shù)漠a(chǎn)品。

而Spring Cloud提供了讓服務(wù)調(diào)用端具備負(fù)載均衡能力的Ribbon,通過(guò)和Eureka的緊密結(jié)合,不用在服務(wù)集群內(nèi)再架設(shè)負(fù)載均衡服務(wù),很大程度簡(jiǎn)化了服務(wù)集群內(nèi)的架構(gòu)。

具體也不想多寫(xiě)虛的介紹,反正哪里都能看得到相關(guān)的介紹。

直接開(kāi)擼代碼,通過(guò)代碼來(lái)看Ribbon是如何實(shí)現(xiàn)的。

配置

詳解:

1.RibbonAutoConfiguration配置生成RibbonLoadBalancerClient實(shí)例。

代碼位置:

spring-cloud-netflix-core-1.3.5.RELEASE.jar

org.springframework.cloud.netflix.ribbon

RibbonAutoConfiguration.class

@Configuration
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties(RibbonEagerLoadProperties.class)
public class RibbonAutoConfiguration {

 // 略

 @Bean
 @ConditionalOnMissingBean(LoadBalancerClient.class)
 public LoadBalancerClient loadBalancerClient() {
  return new RibbonLoadBalancerClient(springClientFactory());
 }
  // 略
}

先看配置條件項(xiàng),RibbonAutoConfiguration配置必須在LoadBalancerAutoConfiguration配置前執(zhí)行,因?yàn)樵贚oadBalancerAutoConfiguration配置中會(huì)使用RibbonLoadBalancerClient實(shí)例。

RibbonLoadBalancerClient繼承自LoadBalancerClient接口,是負(fù)載均衡客戶端,也是負(fù)載均衡策略的調(diào)用方。

2.LoadBalancerInterceptorConfig配置生成:

1).負(fù)載均衡攔截器LoadBalancerInterceptor實(shí)例

包含:

LoadBalancerClient實(shí)現(xiàn)類的RibbonLoadBalancerClient實(shí)例

負(fù)載均衡的請(qǐng)求創(chuàng)建工廠LoadBalancerRequestFactory:實(shí)例

2).RestTemplate自定義的RestTemplateCustomizer實(shí)例

代碼位置:

spring-cloud-commons-1.2.4.RELEASE.jar

org.springframework.cloud.client.loadbalancer

LoadBalancerAutoConfiguration.class

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
 // 略
 @Bean
 @ConditionalOnMissingBean
 public LoadBalancerRequestFactory loadBalancerRequestFactory(
   LoadBalancerClient loadBalancerClient) {
  return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
 }

 @Configuration
 @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
 static class LoadBalancerInterceptorConfig {
  @Bean
  public LoadBalancerInterceptor ribbonInterceptor(
    LoadBalancerClient loadBalancerClient,
    LoadBalancerRequestFactory requestFactory) {
   return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
  }

  @Bean
  @ConditionalOnMissingBean
  public RestTemplateCustomizer restTemplateCustomizer(
    final LoadBalancerInterceptor loadBalancerInterceptor) {
   return new RestTemplateCustomizer() {
    @Override
    public void customize(RestTemplate restTemplate) {
     List<ClientHttpRequestInterceptor> list = new ArrayList<>(
       restTemplate.getInterceptors());
     list.add(loadBalancerInterceptor);
     restTemplate.setInterceptors(list);
    }
   };
  }
 }
 // 略
}

先看配置條件項(xiàng):

要求在項(xiàng)目環(huán)境中必須要有RestTemplate類。

要求必須要有LoadBalancerClient接口的實(shí)現(xiàn)類的實(shí)例,也就是上一步生成的RibbonLoadBalancerClient。

3.通過(guò)上面一步創(chuàng)建的RestTemplateCustomizer配置所有RestTemplate實(shí)例,就是將負(fù)載均衡攔截器設(shè)置給RestTemplate實(shí)例。

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
 // 略

 @Bean
 public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
   final List<RestTemplateCustomizer> customizers) {
  return new SmartInitializingSingleton() {
   @Override
   public void afterSingletonsInstantiated() {
    for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
     for (RestTemplateCustomizer customizer : customizers) {
      customizer.customize(restTemplate);
     }
    }
   }
  };
 }

 // 略
 @Configuration
 @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
 static class LoadBalancerInterceptorConfig {
  @Bean
  public LoadBalancerInterceptor ribbonInterceptor(
    LoadBalancerClient loadBalancerClient,
    LoadBalancerRequestFactory requestFactory) {
   return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
  }

  @Bean
  @ConditionalOnMissingBean
  public RestTemplateCustomizer restTemplateCustomizer(
    final LoadBalancerInterceptor loadBalancerInterceptor) {
   return new RestTemplateCustomizer() {
    @Override
    public void customize(RestTemplate restTemplate) {
     List<ClientHttpRequestInterceptor> list = new ArrayList<>(
       restTemplate.getInterceptors());
     list.add(loadBalancerInterceptor);
     restTemplate.setInterceptors(list);
    }
   };
  }
 }
 // 略
}

restTemplate.setInterceptors(list)這個(gè)地方就是注入負(fù)載均衡攔截器的地方LoadBalancerInterceptor。

從這個(gè)地方實(shí)際上也可以猜出來(lái),RestTemplate可以通過(guò)注入的攔截器來(lái)構(gòu)建相應(yīng)的請(qǐng)求實(shí)現(xiàn)負(fù)載均衡。

也能看出來(lái)可以自定義攔截器實(shí)現(xiàn)其他目的。

4.RibbonClientConfiguration配置生成ZoneAwareLoadBalancer實(shí)例

代碼位置:

spring-cloud-netflix-core-1.3.5.RELEASE.jar

org.springframework.cloud.netflix.ribbon

RibbonClientConfiguration.class

@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
 // 略
 @Bean
 @ConditionalOnMissingBean
 public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
   ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
   IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
  if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
   return this.propertiesFactory.get(ILoadBalancer.class, config, name);
  }
  return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
    serverListFilter, serverListUpdater);
 }

 // 略
}

ZoneAwareLoadBalancer繼承自ILoadBalancer接口,該接口有一個(gè)方法:

 /**
  * Choose a server from load balancer.
  * 
  * @param key An object that the load balancer may use to determine which server to return. null if 
  *   the load balancer does not use this parameter.
  * @return server chosen
  */
 public Server chooseServer(Object key);

ZoneAwareLoadBalancer就是一個(gè)具體的負(fù)載均衡實(shí)現(xiàn)類,也是默認(rèn)的負(fù)載均衡類,通過(guò)對(duì)chooseServer方法的實(shí)現(xiàn)選取某個(gè)服務(wù)實(shí)例。

攔截&請(qǐng)求

1.使用RestTemplate進(jìn)行Get、Post等各種請(qǐng)求,都是通過(guò)doExecute方法實(shí)現(xiàn)

代碼位置:
spring-web-4.3.12.RELEASE.jar

org.springframework.web.client

RestTemplate.class

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {

 // 略

 protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
   ResponseExtractor<T> responseExtractor) throws RestClientException {

  Assert.notNull(url, "'url' must not be null");
  Assert.notNull(method, "'method' must not be null");
  ClientHttpResponse response = null;
  try {
   ClientHttpRequest request = createRequest(url, method);
   if (requestCallback != null) {
    requestCallback.doWithRequest(request);
   }
   response = request.execute();
   handleResponse(url, method, response);
   if (responseExtractor != null) {
    return responseExtractor.extractData(response);
   }
   else {
    return null;
   }
  }
  catch (IOException ex) {
   String resource = url.toString();
   String query = url.getRawQuery();
   resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);
   throw new ResourceAccessException("I/O error on " + method.name() +
     " request for \"" + resource + "\": " + ex.getMessage(), ex);
  }
  finally {
   if (response != null) {
    response.close();
   }
  }
 }

 // 略

}

支持的各種http請(qǐng)求方法最終都是調(diào)用doExecute方法,該方法內(nèi)調(diào)用創(chuàng)建方法創(chuàng)建請(qǐng)求實(shí)例,并執(zhí)行請(qǐng)求得到響應(yīng)對(duì)象。

2.生成請(qǐng)求實(shí)例創(chuàng)建工廠

上一步代碼中,調(diào)用createRequest方法創(chuàng)建請(qǐng)求實(shí)例,這個(gè)方法是定義在父類中。

先整理出主要的繼承關(guān)系:

createRequest方法實(shí)際是定義在HttpAccessor抽象類中。

public abstract class HttpAccessor {
 private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
 public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
  Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
  this.requestFactory = requestFactory;
 }
 public ClientHttpRequestFactory getRequestFactory() {
  return this.requestFactory;
 }
 protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
  ClientHttpRequest request = getRequestFactory().createRequest(url, method);
  if (logger.isDebugEnabled()) {
   logger.debug("Created " + method.name() + " request for \"" + url + "\"");
  }
  return request;
 }
}

在createRequest方法中調(diào)用getRequestFactory方法獲得請(qǐng)求實(shí)例創(chuàng)建工廠,實(shí)際上getRequestFactory并不是當(dāng)前HttpAccessor類中定義的,而是在子類InterceptingHttpAccessor中定義的。

public abstract class InterceptingHttpAccessor extends HttpAccessor {

 private List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();

 public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
  this.interceptors = interceptors;
 }

 public List<ClientHttpRequestInterceptor> getInterceptors() {
  return interceptors;
 }

 @Override
 public ClientHttpRequestFactory getRequestFactory() {
  ClientHttpRequestFactory delegate = super.getRequestFactory();
  if (!CollectionUtils.isEmpty(getInterceptors())) {
   return new InterceptingClientHttpRequestFactory(delegate, getInterceptors());
  }
  else {
   return delegate;
  }
 }
}

在這里做了個(gè)小動(dòng)作,首先還是通過(guò)HttpAccessor類創(chuàng)建并獲得SimpleClientHttpRequestFactory工廠,這個(gè)工廠主要就是在沒(méi)有攔截器的時(shí)候創(chuàng)建基本請(qǐng)求實(shí)例。

其次,在有攔截器注入的情況下,創(chuàng)建InterceptingClientHttpRequestFactory工廠,該工廠就是創(chuàng)建帶攔截器的請(qǐng)求實(shí)例,因?yàn)樽⑷肓素?fù)載均衡攔截器,所以這里就從InterceptingClientHttpRequestFactory工廠創(chuàng)建。

3.通過(guò)工廠創(chuàng)建請(qǐng)求實(shí)例

創(chuàng)建實(shí)例就看工廠的createRequest方法。

public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {

 private final List<ClientHttpRequestInterceptor> interceptors;

 public InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,
   List<ClientHttpRequestInterceptor> interceptors) {

  super(requestFactory);
  this.interceptors = (interceptors != null ? interceptors : Collections.<ClientHttpRequestInterceptor>emptyList());
 }


 @Override
 protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
  return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
 }

}

就是new了個(gè)InterceptingClientHttpRequest實(shí)例,并且把攔截器、基本請(qǐng)求實(shí)例創(chuàng)建工廠注進(jìn)去。

4.請(qǐng)求實(shí)例調(diào)用配置階段注入的負(fù)載均衡攔截器的攔截方法intercept

可從第1步看出,創(chuàng)建完請(qǐng)求實(shí)例后,通過(guò)執(zhí)行請(qǐng)求實(shí)例的execute方法執(zhí)行請(qǐng)求。

ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
 requestCallback.doWithRequest(request);
}
response = request.execute();

實(shí)際請(qǐng)求實(shí)例是InterceptingClientHttpRequest,execute實(shí)際是在它的父類中。

類定義位置:

spring-web-4.3.12.RELEASE.jar

org.springframework.http.client

InterceptingClientHttpRequest.class

看一下它們的繼承關(guān)系。

在execute方法中實(shí)際調(diào)用了子類實(shí)現(xiàn)的executeInternal方法。

public abstract class AbstractClientHttpRequest implements ClientHttpRequest {

 private final HttpHeaders headers = new HttpHeaders();

 private boolean executed = false;

 @Override
 public final HttpHeaders getHeaders() {
  return (this.executed ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
 }

 @Override
 public final OutputStream getBody() throws IOException {
  assertNotExecuted();
  return getBodyInternal(this.headers);
 }

 @Override
 public final ClientHttpResponse execute() throws IOException {
  assertNotExecuted();
  ClientHttpResponse result = executeInternal(this.headers);
  this.executed = true;
  return result;
 }

 protected void assertNotExecuted() {
  Assert.state(!this.executed, "ClientHttpRequest already executed");
 }

 protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException;

 protected abstract ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException;

}

其實(shí)就是InterceptingClientHttpRequest類的executeInternal方法,其中,又調(diào)用了一個(gè)執(zhí)行器InterceptingRequestExecution的execute,通關(guān)判斷如果有攔截器注入進(jìn)來(lái)過(guò),就調(diào)用攔截器的intercept方法。

這里的攔截器實(shí)際上就是在配置階段注入進(jìn)RestTemplate實(shí)例的負(fù)載均衡攔截器LoadBalancerInterceptor實(shí)例,可參考上面配置階段的第2步。

class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {

 // 略

 @Override
 protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
  InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
  return requestExecution.execute(this, bufferedOutput);
 }


 private class InterceptingRequestExecution implements ClientHttpRequestExecution {

  private final Iterator<ClientHttpRequestInterceptor> iterator;

  public InterceptingRequestExecution() {
   this.iterator = interceptors.iterator();
  }

  @Override
  public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
   if (this.iterator.hasNext()) {
    ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
    return nextInterceptor.intercept(request, body, this);
   }
   else {
    ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
    for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
     List<String> values = entry.getValue();
     for (String value : values) {
      delegate.getHeaders().add(entry.getKey(), value);
     }
    }
    if (body.length > 0) {
     StreamUtils.copy(body, delegate.getBody());
    }
    return delegate.execute();
   }
  }
 }

}

5.負(fù)載均衡攔截器調(diào)用負(fù)載均衡客戶端

在負(fù)載均衡攔截器LoadBalancerInterceptor類的intercept方法中,又調(diào)用了負(fù)載均衡客戶端LoadBalancerClient實(shí)現(xiàn)類的execute方法。

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

 private LoadBalancerClient loadBalancer;
 private LoadBalancerRequestFactory requestFactory;

 public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
  this.loadBalancer = loadBalancer;
  this.requestFactory = requestFactory;
 }

 public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
  // for backwards compatibility
  this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
 }

 @Override
 public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
   final ClientHttpRequestExecution execution) throws IOException {
  final URI originalUri = request.getURI();
  String serviceName = originalUri.getHost();
  Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
  return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
 }
}

在配置階段的第1步,可以看到實(shí)現(xiàn)類是RibbonLoadBalancerClient。

6.負(fù)載均衡客戶端調(diào)用負(fù)載均衡策略選取目標(biāo)服務(wù)實(shí)例并發(fā)起請(qǐng)求

在RibbonLoadBalancerClient的第一個(gè)execute方法以及getServer方法中可以看到,實(shí)際上是通過(guò)ILoadBalancer的負(fù)載均衡器實(shí)現(xiàn)類作的chooseServer方法選取一個(gè)服務(wù),交給接下來(lái)的請(qǐng)求對(duì)象發(fā)起一個(gè)請(qǐng)求。

這里的負(fù)載均衡實(shí)現(xiàn)類默認(rèn)是ZoneAwareLoadBalancer區(qū)域感知負(fù)載均衡器實(shí)例,其內(nèi)部通過(guò)均衡策略選擇一個(gè)服務(wù)。

ZoneAwareLoadBalancer的創(chuàng)建可以參考配置階段的第4步。

public class RibbonLoadBalancerClient implements LoadBalancerClient {
 @Override
 public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
  ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
  Server server = getServer(loadBalancer);
  if (server == null) {
   throw new IllegalStateException("No instances available for " + serviceId);
  }
  RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
    serviceId), serverIntrospector(serviceId).getMetadata(server));

  return execute(serviceId, ribbonServer, request);
 }

 @Override
 public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
  Server server = null;
  if(serviceInstance instanceof RibbonServer) {
   server = ((RibbonServer)serviceInstance).getServer();
  }
  if (server == null) {
   throw new IllegalStateException("No instances available for " + serviceId);
  }

  RibbonLoadBalancerContext context = this.clientFactory
    .getLoadBalancerContext(serviceId);
  RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

  try {
   T returnVal = request.apply(serviceInstance);
   statsRecorder.recordStats(returnVal);
   return returnVal;
  }
  // catch IOException and rethrow so RestTemplate behaves correctly
  catch (IOException ex) {
   statsRecorder.recordStats(ex);
   throw ex;
  }
  catch (Exception ex) {
   statsRecorder.recordStats(ex);
   ReflectionUtils.rethrowRuntimeException(ex);
  }
  return null;
 }
  
 // 略 

 protected Server getServer(ILoadBalancer loadBalancer) {
  if (loadBalancer == null) {
   return null;
  }
  return loadBalancer.chooseServer("default"); // TODO: better handling of key
 }

 protected ILoadBalancer getLoadBalancer(String serviceId) {
  return this.clientFactory.getLoadBalancer(serviceId);
 }

 public static class RibbonServer implements ServiceInstance {
  private final String serviceId;
  private final Server server;
  private final boolean secure;
  private Map<String, String> metadata;

  public RibbonServer(String serviceId, Server server) {
   this(serviceId, server, false, Collections.<String, String> emptyMap());
  }

  public RibbonServer(String serviceId, Server server, boolean secure,
    Map<String, String> metadata) {
   this.serviceId = serviceId;
   this.server = server;
   this.secure = secure;
   this.metadata = metadata;
  }

  // 略
 }

}

代碼擼完,總結(jié)下。

普通使用RestTemplate請(qǐng)求其他服務(wù)時(shí),內(nèi)部使用的就是常規(guī)的http請(qǐng)求實(shí)例發(fā)送請(qǐng)求。

為RestTemplate增加了@LoanBalanced 注解后,實(shí)際上通過(guò)配置,為RestTemplate注入負(fù)載均衡攔截器,讓負(fù)載均衡器選擇根據(jù)其對(duì)應(yīng)的策略選擇合適的服務(wù)后,再發(fā)送請(qǐng)求。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java使用FileReader讀取文件詳解

    Java使用FileReader讀取文件詳解

    本文將為大家介紹FileReader類的基本用法,包括如何創(chuàng)建FileReader對(duì)象,如何讀取文件,以及如何關(guān)閉流,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-09-09
  • Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為實(shí)例分析

    Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為實(shí)例分析

    這篇文章主要介紹了Java中構(gòu)造器內(nèi)部的多態(tài)方法的行為,結(jié)合實(shí)例形式分析了java構(gòu)造器內(nèi)部多態(tài)方法相關(guān)原理、功能及操作技巧,需要的朋友可以參考下
    2019-10-10
  • java中為何重寫(xiě)equals時(shí)必須重寫(xiě)hashCode方法詳解

    java中為何重寫(xiě)equals時(shí)必須重寫(xiě)hashCode方法詳解

    這篇文章主要給大家介紹了關(guān)于java中為什么重寫(xiě)equals時(shí)必須重寫(xiě)hashCode方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • java高效文件流讀寫(xiě)操作詳解

    java高效文件流讀寫(xiě)操作詳解

    這篇文章主要介紹了java高效文件流讀寫(xiě)操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Spring依賴注入的三種方式小結(jié)

    Spring依賴注入的三種方式小結(jié)

    本篇文章主要介紹了Spring依賴注入的三種方式小結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • 使用記事本編寫(xiě)java程序全過(guò)程圖解

    使用記事本編寫(xiě)java程序全過(guò)程圖解

    這篇文章主要介紹了如何使用記事本編寫(xiě)java程序,需要的朋友可以參考下
    2014-03-03
  • java實(shí)現(xiàn)百度坐標(biāo)的摩卡托坐標(biāo)與火星坐標(biāo)轉(zhuǎn)換的示例

    java實(shí)現(xiàn)百度坐標(biāo)的摩卡托坐標(biāo)與火星坐標(biāo)轉(zhuǎn)換的示例

    這篇文章主要介紹了java實(shí)現(xiàn)百度坐標(biāo)的摩卡托坐標(biāo)與火星坐標(biāo)轉(zhuǎn)換的示例,需要的朋友可以參考下
    2014-03-03
  • idea中如何使用(Undo Commit...)

    idea中如何使用(Undo Commit...)

    這篇文章主要介紹了idea中如何使用(Undo Commit...)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • spring集成okhttp3的步驟詳解

    spring集成okhttp3的步驟詳解

    okhttp是一個(gè)封裝URL,比HttpClient更友好易用的工具,下面這篇文章主要給大家介紹了關(guān)于spring集成okhttp3的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2018-04-04
  • Spring中ApplicationEventPublisher發(fā)布訂閱模式的實(shí)現(xiàn)

    Spring中ApplicationEventPublisher發(fā)布訂閱模式的實(shí)現(xiàn)

    本文主要介紹了Spring中ApplicationEventPublisher發(fā)布訂閱模式的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評(píng)論