本文浅述:在使用dubbo时,spring aop失效问题。如果你也遇到该问题,先检查是否使用@Reference注入dubbo服务的。

场景还原

版本:我使用的dubbo版本是2.6.
场景:我想消费者在调用我的dubbo接口时,都将自己应用名传过来,方便我鉴权,所以我在我提供的二方包中,提供aop,想通过dubbo的附加参数,将应用名传过来。大致代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.xxxx.service;
public interface XxxxService {
DicResponseDTO<List<String>> getAllFormKey();
}

-------------------------

@Aspect
@Component
public class XxxAspect {
@Around("execution(* com.xxxx.service.*.*(..))")
public Object aroundApi(ProceedingJoinPoint pjp) throws Throwable {
// todo set attachment param
}
}

调用方式大致如下:

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class TestController {

@Reference
private XxxxService xxxxServiceRe;

@RequestMapping("test")
public void ss() {
Object allFormKey =
xxxxServiceRe.getAllFormKey();
}
}

最后肯定没有如我所愿,要不然也不会有这一篇文章了。现象呢,始终走不到aop中。

原因分析

一切看起来很奇怪,我还特地验证了会不会因为我切入的是interface的问题。我在消费者项目中新建一个接口,然后用aop切人,用@Autowired注入,调用时aop能正常工作。

那么aop切入点是dubbo的接口时,为什么就走不到aop呢?这两个case对于调用者来说,唯一的区别就是一个用@Reference注入,一个用@Autowired注入。正好我也想看一下dubbo的@Reference的工作原理,就趁此机会看一眼。

废话不说,ctrl+shift+f搜索“Reference.class”,我找到了DubboConsumerAutoConfiguration,看到代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class DubboConsumerAutoConfiguration extends DubboCommonAutoConfiguration {
private static final Map<ClassIdBean, Object> DUBBO_REFERENCES_MAP =
new ConcurrentHashMap<ClassIdBean, Object>();

@Bean
public BeanPostProcessor beanPostProcessor() {
return new BeanPostProcessor() {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
...
try {
for (Field field : objClz.getDeclaredFields()) {
Reference reference = field.getAnnotation(Reference.class);
if (reference != null) {
DubboConsumerAutoConfiguration.this
.initIdConfigMap(DubboConsumerAutoConfiguration.this.properties);
ReferenceBean<?> referenceBean =
DubboConsumerAutoConfiguration.this.getConsumerBean(beanName, field, reference);
Object dubboReference = ...;
field.setAccessible(true);
field.set(bean, dubboReference);
}
}
} catch (Exception e) {
throw new BeanCreationException(beanName, e);
}
return bean;
}
...
};
}
}

可以看到,Reference是想通过spring注入BeanPostProcessor,在spring bean创建之后、初始化之前,将标注了@Reference注解的字段,为其new一个ReferenceBean,并保存application、registry等参数。而ReferenceBean实现了FactoryBean,则可以通过getObject()返回该字段的对象,当然其中还有缓存啥的,各位可以自己了解。然后拿到这个对象之后,通过反射调用field.set(bean, dubboReference)来注入。

至此,@Reference工作原理到此解释详尽。那么回顾aop。aop是spring提供的功能,再看看@Reference工作原理跟spring有半毛钱关系嘛?是不是完全没有经过spring 的 ioc ,那么spring怎么能给你实现aop的功能呢!!!!

解决方案

注入ioc中的bean

明白了原因了,那么我们就避免使用@Reference,然后用spring 的 @Autowired注入不就行了嘛。于是我用xml+@Autowired进行以下测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<dubbo:reference interface="com.xxxx.service.XxxxService" id="xxxService" check="false"
timeout="5000" retries="-1"/>

------------------------------------------------------------------

@RestController
public class TestController {

@Autowired
private XxxxService xxxxServiceRe;

@RequestMapping("test")
public void ss() {
Object allFormKey =
xxxxServiceRe.getAllFormKey();
}
}

那么这次结果肯定是令我满意的,aop工作的很好,那么我做的是中台应用,要是对方不愿意使用xml+@Autowired注入怎么办呢?

自己解决不了就抛出去

哈哈,在dubbo群里有小伙伴,可能看到聊天记录了,在很久之前我就把它抛给小马哥了。还挺佩服阿里的工作效率的,我当天说完,提完issue,当天就定为新特性,第二天就fix掉了。

issue:https://github.com/apache/dubbo/issues/5446
pr:https://github.com/apache/dubbo/pull/5454

该问题在 2.7.6 中,当作新特性上线,大家也可以升级到这个版本,来解决这一问题。