由springcloud ribbon的 @LoadBalanced注解的使用理解

在使用springcloud ribbon客户端负载均衡的时候,能够给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载均衡的能力:html

@Bean
    @LoadBalanced
    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 {
}

惟一不一样的地方就是多了一个@Qulifier注解.java

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

 
@LoadBalanced @Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();  @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);  }
         }
      }
   }; }

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

这段自动装配的代码的含义不难理解,就是利用了RestTempllate的拦截器,使用RestTemplateCustomizer对全部标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的做用就是对请求的URI进行转换获取到具体应该请求哪一个服务实例 ServiceInstance。


那么为何spring

@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
这个restTemplates可以将全部标注了@LoadBalanced的RestTemplate自动注入进来呢?这就要说说@Autowired注解和@Qualifier这两个注解了。

你们平常使用不少都是用@Autowired来注入一个bean,其实@Autowired还能够注入List和Map,好比我定义两个Bean:app

@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中的user list 和user map分别也打上@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:dom

[
    {
        "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"
    }
]

和@Qualifier注解效果同样,只有user1被注入进了List,user2没有修饰符,没有被注入进去。

另外当spring容器中有多个相同类型的bean的时候,能够经过@Qualifier来进行区分,以便在注入的时候明确代表你要注入具体的哪一个bean,消除歧义。ide