在使用Spring Cloud Ribbon用戶端進行負載均衡的時候,可以給RestTemplate(Bean) 加一個@LoadBalanced注解,就能讓這個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:
@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:
@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"
}
]