天天看點

SpringCloud Ribbon的@LoadBalanced注解你真的知道麼?

今天一直在糾結SpringCloud Ribbon中@LoadBalanced注解的作用,然後在網站檢視資料時,無意發現了下面這篇文章,個人覺得講的很好,實測通過,故轉載記錄在此,如有侵權,請聯系我删除并深感抱歉。

本人聯系方式:[email protected]

原文連結:https://blog.csdn.net/xiao_jun_0820/article/details/78917215

在使用springcloud ribbon用戶端負載均衡的時候,可以給RestTemplate bean 加一個@LoadBalanced注解,就能讓這個RestTemplate在請求時擁有用戶端負載均衡的能力:

@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注解.

搜尋@LoadBalanced注解的使用地方,發現隻有一處使用了,在LoadBalancerAutoConfiguration這個自動裝配類中:

@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。

那麼為什麼

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

這個restTemplates能夠将所有标注了@LoadBalanced的RestTemplate自動注入進來呢?這就要說說@Autowired注解和@Qualifier這兩個注解了。

大家日常使用很多都是用@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中的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 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 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,消除歧義。

繼續閱讀