天天看点

Spring Cloud 之@LoadBalanced注解,看完保证你还懂@Qualifier注解

Spring Cloud 之@LoadBalanced注解,看完保证你还懂@Qualifier注解

在使用Spring Cloud Ribbon客户端进行负载均衡的时候,可以给RestTemplate(Bean) 加一个​

​@LoadBalanced​

​注解,就能让这个RestTemplate在请求时拥有客户端负载均衡的能力:

package com.kaven.client.component;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RestTemplateComponent {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}      

是不是很神奇?当我打开​

​@LoadBalanced​

​注解的源码时,发现连方法都没有:

package org.springframework.cloud.client.loadbalancer;

import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {}      

唯一不同的地方,就是使用了注解​

​@Qualifier​

​。

搜索​

​@LoadBalanced​

​注解的使用地方,发现只有一处使用了,在LoadBalancerAutoConfiguration这个自动装配类中:

@LoadBalanced
  @Autowired(required = false)
  private List<RestTemplate> restTemplates = Collections.emptyList();

  @Bean
  public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
      final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
  }

  @Autowired(required = false)
  private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

  @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 restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
    }
  }      

这段自动装配的代码含义不难理解,就是利用了RestTemplate的拦截器,使用RestTemplateCustomizer对所有标注了注解​

​@LoadBalanced​

​的RestTemplate(Bean)添加一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换,获取到具体应该请求哪个服务实例ServiceInstance。

那么为什么这个RestTemplates能够将所有标注了注解​

​@LoadBalanced​

​​的RestTemplate(Bean)自动注入进来呢?这就要说说​

​@Autowired​

​​注解和​

​@Qualifier​

​这两个注解了。

@LoadBalanced
  @Autowired(required = false)
  private List<RestTemplate> restTemplates = Collections.emptyList();      

大家日常使用自动注入,很多都是用注解​

​@Autowired​

​​来注入一个Bean,其实注解​

​@Autowired​

​还可以注入List和Map,比如我定义两个Bean:

@Bean("user1")
    User user1() {
        return new User("1", "a");
    }
 
    @Bean("user2"))
    User user2() {
        return new User("2", "b");
    }      

然后我写一个Controller:

import com.example.demo.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@RestController
public class MyController {
 
    @Autowired(required = false)
    private List<User> users = Collections.emptyList();
 
    @Autowired(required = false)
    private Map<String,User> userMap = new HashMap<>();
 
    @RequestMapping("/list")
    public Object listUsers() {
        return users;
    }
    @RequestMapping("/map")
    public Object mapUsers() {
        return userMap;
    }
}      

在Controller中通过:

@Autowired(required = false)
    private List<User> users = Collections.emptyList();
 
    @Autowired(required = false)
    private Map<String,User> userMap = new HashMap<>();      

就可以自动将两个Bean注入进来,当注入Map的时候,Map的​

​key​

​​必须是String类型,然后Bean的​

​name​

​​将作为Map的​

​key​

​​,本例中,Map将有两个​

​key​

​​,分别为​

​user1​

​​和​

​user2​

​​,​

​value​

​分别为对应的User Bean实例。

访问​

​http://localhost:8080/map​

​:

{
    "user1": {
        "id": "1",
        "name": "a"
    },
    "user2": {
        "id": "2",
        "name": "b"
    }
}      

访问​

​http://localhost:8080/list​

​:

[
    {
        "id": "1",
        "name": "a"
    },
    {
        "id": "2",
        "name": "b"
    }
]      

然后我们给​

​user1​

​​和​

​user2​

​​分别标注上注解​

​@Qualifier​

​:

@Bean("user1")
    @Qualifier("valid")
    User user1() {
        return new User("1", "a");
    }
 
    @Bean("user2")
    @Qualifier("invalid")
    User user2() {
        return new User("2", "b");
    }      

然后将Controller中的​

​users​

​​和​

​userMap​

​​分别也标注上注解​

​@Qualifier​

​:

import com.example.demo.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@RestController
public class MyController {
 
    @Autowired(required = false)
    @Qualifier("valid")
    private List<User> users = Collections.emptyList();
 
    @Autowired(required = false)
    @Qualifier("invalid")
    private Map<String,User> userMap = new HashMap<>();
 
    @RequestMapping("/list")
    public Object listUsers() {
        return users;
    }
    @RequestMapping("/map")
    public Object mapUsers() {
        return userMap;
    }
}      

那么所有标注了注解​

​@Qualifier("valid")​

​​的​

​user​

​​(Bean)都会自动注入到​

​List<user> users​

​​中去(本例是​

​user1​

​​),所有标注了注解​

​@Qualifier("invalid")​

​​的​

​user​

​​(Bean)都会自动注入到​

​Map<String,User> userMap​

​​中去(本例是​

​user2​

​​),我们再次访问上面两个​

​url​

​。

访问​

​http://localhost:8080/list​

​:

[
    {
        "id": "1",
        "name": "a"
    }
]      

访问​

​http://localhost:8080/map​

​:

{
    "user2": {
        "id": "2",
        "name": "b"
    }
}      

看到这里我们可以理解注解​

​@LoadBalanced​

​​的用处了,其实就是一个修饰符,和注解​

​@Qualifier​

​​一样,比如我们给​

​user1​

​​标注上注解​

​@LoadBalanced​

​:

@Bean("user1")
    @LoadBalanced
    User user1() {
        return new User("1", "a");
    }
 
    @Bean("user2")
    User user2() {
        return new User("2", "b");
    }      

然后Controller中给​

​List<User> users​

​​标注上注解​

​@LoadBalanced​

​:

@Autowired(required = false)
    @LoadBalanced
    private List<User> users = Collections.emptyList();      

访问​

​http://localhost:8080/list​

​:

[
    {
        "id": "1",
        "name": "a"
    }
]      

其实注解​

​@LoadBalanced​

​​和注解​

​@Qualifier​

​​效果是一样的,只有​

​user1​

​​被注入进了List,​

​user2​

​没有修饰符,没有被注入进去。