引言
书接上篇 微服务负载均衡小能手-Ribbon 使用RIbion实现负载均衡远程调用
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
都知道没有@LoadBalanced注解前,RestTemplate就是一个简单的http请求工具类,贴上该注解,却能山鸡变凤凰,成为具有负载均衡的小能手,为啥呢?本篇就跟你讲讲为啥,开讲之前需要铺垫四个知识储备:
1>@Qualifier 2>SmartInitializingSingleton接口 3>ClientHttpRequestInterceptor拦截器 4>SpringBoot 自动配置
@Qualifier注解详解
Spring 给@Qualifier注解赋予2个功能
1:根据Bean名字注入属性
熟悉的Spring的小伙伴应该能看懂下面的代码:
@Service
public class OrderServiceImpl implements IOrderService {
@Autowired
private IDiscountService discountService ; //折扣服务
}
order业务层需要引入折扣业务逻辑,其中的@Autowired 能让Spring将容器中有且唯一的IDiscountService 接口实现类注入到OrderServiceImpl 中。很理想,真实情况是电商项目中的商品折扣种类很多,IDiscountService 接口实现类不会唯一,当spring容器中存在多个IDiscountService 接口实例时,上面代码执行肯定会报错
Field discountService in xxxx.OrderServiceImpl required a single bean, but n were found:
怎么办呢?此时@Qualifier注解就起作用啦。
public interface IDiscountService {
}
@Service("discount1Service")
public class Discount1ServiceImpl implements IDiscountService {
}
@Service("discount2Service")
public class Discount2ServiceImpl implements IDiscountService {
}
在定义IDiscountService 接口实现类时,给这些实现类指定Bean名:discount1Service discount2Service 后续使用时,使用@Qualifier("bean名称")直接指定
@Service
public class OrderServiceImpl implements IOrderService {
@Qualifier("discount2Service") //根据bean名字指定
@Autowired
private IDiscountService discountService ; //折扣服务
}
@Qualifier("xxx") xxx是 Bean 的名称,@Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了,完美解决了接口多实现问题。
2:Bean筛选标记符
@Qualifier 注解的这个功能,开发中用途算广,但是Ribbon 负载均衡功能@LoadBalanced 注解刚好用到。
还是上面的接口案例,现在有个需求:将所有IDiscountService接口实现类添加到List集合中
@Autowired
private List<IDiscountService> list= Collections.emptyList();
@Test
public void testList() {
for (IDiscountService discountService : list) {
System.out.println(discountService.getClass());
}
}
执行后,会打印出所有的IDiscountService接口实现类
class com.langfeiyes.demo.Discount1ServiceImpl
class com.langfeiyes.demo.Discount2ServiceImpl
ok,没问题,我们升级一下需求:只需要Discount1ServiceImpl实现类添加到List集合中
此时就需要使用@Qualifier 注解的筛选功能了。
public interface IDiscountService {
}
//不贴,没有筛选标记
@Service("discount1Service")
public class Discount1ServiceImpl implements IDiscountService {
}
@Qualifier //贴,筛选标记
@Service("discount2Service")
public class Discount2ServiceImpl implements IDiscountService {
}
2个接口实现类,只有Discount2ServiceImpl 贴上@Qualifier 标记,集合收集时,配合@Autowired可以实现自动筛选功能
@Qualifier
@Autowired(required = false)
private List<IDiscountService> list= Collections.emptyList();
@Test
public void testList() {
for (IDiscountService discountService : list) {
System.out.println(discountService.getClass());
}
}
执行后,会打印出所有的IDiscountService接口实现类
class com.langfeiyes.demo.Discount2ServiceImpl
到这,@Qualifier 注解知识点铺垫算介绍啦,下面看SmartInitializingSingleton 接口
SmartInitializingSingleton 接口
先看下该SmartInitializingSingleton源码
public interface SmartInitializingSingleton {
void afterSingletonsInstantiated();
}
SmartInitializingSingleton接口里面只有一个方法:afterSingletonsInstantiated 从方法名也大概看出作用。SmartInitializingSingleton是Spring提供的钩子接口,实现该接口的Bean会在Spring容器初始化所有Bean之后,由容器直接回调afterSingletonsInstantiated 方法。注意:回调的是所有单例Bean。
来一个例子演示一下
public class SomeBean implements SmartInitializingSingleton {
public SomeBean(){
System.out.println("我被构建了...");
}
@Override
public void afterSingletonsInstantiated() {
System.out.println("我被回调了....");
}
}
------------------
@SpringBoot
class DemoApplication {
@Bean
public SomeBean someBean(){
return new SomeBean();
}
}
查看打印返回值:
我被构建了...
我被回调了....
到这,SmartInitializingSingleton 接口知识点铺垫算介绍啦,下面看 ClientHttpRequestInterceptor拦截器
ClientHttpRequestInterceptor拦截器
截止目前,Spring支持3种http请求拦截器器,分别是
HandlerInterceptor :SpringMVC中组件,拦截普通客户端发起请求,比如:浏览器 ,使用最广的http请求拦截器
ClientHttpRequestInterceptor:spring-web组件,拦截是RestTemplate工具getForEntry/getForObject 发起的http请求,所以常被称之RestTemplate拦截器
RequestInterceptor:spring-cloud-feign组件,拦截是feign发起的远程调用请求,所以称之为Feign拦截器。
本篇重点讲ClientHttpRequestInterceptor拦截器,其他拦截有机会我们再深入。
先看下ClientHttpRequestInterceptor接口源码
@FunctionalInterface
public interface ClientHttpRequestInterceptor {
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException;
}
ClientHttpRequestInterceptor 接口为函数接口,只有一个intercept拦截方法,在定制拦截器时我们可以根据业务需求为getForEntry/getForObject 方法请求添加额外参数。
需求:getForEntry/getForObject 发起的请求都需要带上身份令牌token
public class RestTokenInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.add("token","xxxx");
return execution.execute(request,body);
}
}
到这,ClientHttpRequestInterceptor拦截器知识点铺垫算介绍啦,下面看:SpringBoot自动配置
SpringBoot自动配置
SpringBoot自动配置之前也写过来,有兴趣的朋友,可以看浅谈SpringBoot 入门合集。
SpringBoot自动配置用下图就可以讲清楚啦:
到这,@LoadBalanced 注解需要的知识储备算准备齐活了,下一篇就是@LoadBalanced注解的源码介绍啦。