推荐:微服务汇总
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
没有修饰符,没有被注入进去。
另外当Spring容器中有多个相同类型的Bean时,可以通过注解@Qualifier
来进行区分,以便在注入的时候,明确表明你要注入哪个Bean,消除歧义。