(四)dubbo源码解析----consumer@Reference注解注⼊原理
dubbo 版本: 2.6.0
springboot版本: 2.x
spring 版本:5.x
概述
本⽂主要介绍下dubbo consumer中 @Reference 注解是如何注⼊到宿主对象的。
在使⽤@Reference注解过程中,总是会想⼏个问题:
1. 被@Reference 注解的 bean,是在什么时机注⼊的?
2. 被@Reference 注解的 bean,通常是⼀个接⼝,怎么可以被实例化呢?
答案是:
1. @Reference 的注⼊时机和 @Autowired 注解是类似的,但不完全⼀样。负责修饰 bean 属性的 BeanFactoryPostProcessor不
同。
2. @Reference修饰的域是通过动态代理实现的。也就是⽣成了⼀个动态的接⼝实现类。
接下来我们通过阅读源代码来分析⼀下这个具体过程。reference group
上⼀篇通过分析dubbo @Service注解的解析的源代码,了解到被 @Service注解修饰的类都会被封装为 ServiceBean(ServiceConfig的⼦类),然后通过ServiceConfig发布服务。通过 , 发现 dubbo consumer是通过ReferenceConfig去与 dubbo provider建⽴链接,通过查看继承树,发现ReferenceConfig只有⼀个⼦类ReferenceBean。因此猜测(与 @Service 注解类似), 通过ReferenceBean来实例化 consumer代理。
⾄于consumer代理的实例化时机, 应该改与@Autowired 注解类似,通过 BeanPostProcessor 来处理。
⽬前以上还都是猜测,需要通过阅读源代码去验证。我们定义⼀个 controller,使它有⼀个 dubbo service 的属性:
@RestController
@RequestMapping(value ="consumer")
public class ConsumerController {
@Reference
HelloService helloService;
@GetMapping("sayHello")
public String sayHello(@RequestParam String word){
return helloService.sayHello(word);
}
}
观察下helloService是什么时候实例化的,再看看具体是负责实例化的类:在AbstractAutowireCapableBeanFactory类中doCreateBean()⽅法打⼀个有条件的断点,当 beanName.equals(“consumerController”)时断住,然后看下BeanWrapper中的helloService实例什么时候不为空。发现是
initializeBean执⾏完后
Object exposedObject = bean;
try{
populateBean(beanName, mbd, instanceWrapper);
//这个⽅法执⾏完后,consumer代理类被注⼊完成。
exposedObject =initializeBean(beanName, exposedObject, mbd);
}
跟进代码,发现是applyBeanPostProcessorsBeforeInitialization()执⾏了consumer代理的实例化,为了弄清楚是哪个BeanPostProcessor具体操作的,我们加个有条件的断点*(beanName.equals(“consumerController”) && ((ConsumerController) current).helloService != null)*:
发现是 DubboConsumerAutoConfiguration的 postProcessBeforeInitialization()执⾏的注⼊。可是打开DubboConsumerAutoConfiguration的继承路径,发现它并不是⼀个 BeanPostProcessor 的派⽣类,那他是如何实现postProcessBeforeInitialization()⽅法的呢?。
继续阅读源代码, 发现它注⼊了⼀个匿名内部类,如果有⼈问起 JAVA 如何实现多继承,那可以告诉它匿名内部类是⼀种实现⽅式
OK, ⾄此我们已经知道@Reference 注解修饰的属性是什么时候实例化的了,是通过BeanPostFactory 的postProcessBeforeInitialization()⽅法(DubboConsumerAutoConfiguration)。⾄于具体怎么做的,我们再看下这个⽅法的代码吧。
try{
// 获取到 consumerController 的所有实例,我们这个例⼦只有⼀个
for(Field field : DeclaredFields()){
//判断该字段是否有 Reference 注解
Reference reference = Annotation(Reference.class);
if(reference != null){
DubboConsumerAutoConfiguration.this.initIdConfigMap(DubboConsumerAutoConfiguration.this.properties);
ReferenceBean<?> referenceBean =ConsumerBean(beanName, field, reference); Class<?> interfaceClass = InterfaceClass();
String group = Group();
String version = Version();
ClassIdBean classIdBean =new ClassIdBean(interfaceClass, group, version);
Object dubboReference = DubboConsumerAutoConfiguration.DUBBO_(classIdBean);
if(dubboReference == null){
synchronized(this){
// double check
dubboReference =
DubboConsumerAutoConfiguration.DUBBO_(classIdBean);
if(dubboReference == null){
referenceBean.afterPropertiesSet();
//获取 dubbo reference,也就是consumer 代理类
dubboReference = Object();
DubboConsumerAutoConfiguration.DUBBO_REFERENCES_MAP.put(classIdBean,
dubboReference);
}
}
}
field.setAccessible(true);
field.set(bean, dubboReference);
}
}
}catch(Exception e){
throw new BeanCreationException(beanName, e);
}
return bean;
}
接下来我们去看下ReferenceBean 的getObject()⽅法
public Object getObject()throws Exception {
return get();
}
get()⽅法是它的⽗类 ReferenceConfig的。
public synchronized T get(){
if(destroyed){
throw new IllegalStateException("Already destroyed!");
}
if(ref == null){
init();
}
return ref;
}
再看 init ⽅法:
private void init(){
//省略⼤量代码
ref =createProxy(map);
//省略代码
}
createProxy() :
getProxy(invoker):
getProxy(invoker, interfaces) :
不出意外InvokerInvocationHandler是InvocationHandler的派⽣类
⾄此可以看出@Reference修饰的属性是通过动态代理实例化的对象。
需要注意的是:dubbo 的AbstractConfig(ReferenceConfig 的⽗类) toString()⽅法反射的调⽤了所有的⽅法。
如果你⽤ idea 来 debug ReferenceBean 的代码,如果某些位置设置断点会报错(例如 DubboConsumerAutoConfiguration的postProcessBeforeInitialization⽅法⾥的⼀些⾏),导致应⽤起
不来,⽽正常启动则没问题。 因为debug模式下的 idea 会默认调⽤toString()⽅法。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论