为什么80%的码农都做不了架构师?>>>   

在之前的文章中介绍过了基于Spring 4 + Dubbo 的注解配置。参见《改造Dubbo,使其能够兼容Spring 4注解配置》。

但是,在使用中会发现,Dubbo仅仅提供了两个注解:

  • com.alibaba.dubbo.config.annotation.Reference
  • com.alibaba.dubbo.config.annotation.Service

而且,这两个注解中仅仅包含了基本的配置项。导致有一些配置无法通过注解方式完成。比如:<dubbo:method>,这个常用的配置,就没有提供对应的注解。

1.分析

在《改造Dubbo,使其能够兼容Spring 4注解配置》中,曾经分析过,Dubbo的注解完全是由AnnotationBean来完成的。 那先来看看AnnotationBean是怎么处理配置的:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {...Field[] fields = bean.getClass().getDeclaredFields();for (Field field : fields) {try {if (! field.isAccessible()) {field.setAccessible(true);}Reference reference = field.getAnnotation(Reference.class);if (reference != null) {Object value = refer(reference, field.getType());if (value != null) {field.set(bean, value);}}} catch (Throwable e) {logger.error("Failed to init remote service reference at filed " + field.getName() + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e);}}...}

其实AnnotationBean中有好几处类似这样的代码。其实逻辑都大同小异:

  1. 在类、方法、属性上查找ReferenceService注解;
  2. 将注解转换成对应的com.alibaba.dubbo.config.spring.ReferenceBean或者com.alibaba.dubbo.config.spring.ServiceBean

那再来看看ReferenceBean

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean

这个是继承ReferenceConfig,而这个ReferenceConfig是带有Method配置信息的:

public class ReferenceConfig<T> extends AbstractReferenceConfig {...// 方法配置private List<MethodConfig>   methods;...}

但是注解处理过程中并没有对这个属性进行赋值处理,追踪一下AnnotationBean,就会发现配置对象的处理是交由refer方法完成的:

private Object refer(Reference reference, Class<?> referenceClass) { //method.getParameterTypes()[0]...String key = reference.group() + "/" + interfaceName + ":" + reference.version();ReferenceBean<?> referenceConfig = referenceConfigs.get(key);if (referenceConfig == null) {referenceConfig = new ReferenceBean<Object>(reference);...}referenceConfigs.putIfAbsent(key, referenceConfig);referenceConfig = referenceConfigs.get(key);}return referenceConfig.get();}

ReferenceBean的构造器中传入了注解对象,并最终交由com.alibaba.dubbo.config.AbstractConfig#appendAnnotation方法处理:

protected void appendAnnotation(Class<?> annotationClass, Object annotation) {Method[] methods = annotationClass.getMethods();for (Method method : methods) {if (method.getDeclaringClass() != Object.class&& method.getReturnType() != void.class&& method.getParameterTypes().length == 0&& Modifier.isPublic(method.getModifiers())&& ! Modifier.isStatic(method.getModifiers())) {try {String property = method.getName();if ("interfaceClass".equals(property) || "interfaceName".equals(property)) {property = "interface";}String setter = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);Object value = method.invoke(annotation, new Object[0]);if (value != null && ! value.equals(method.getDefaultValue())) {Class<?> parameterType = ReflectUtils.getBoxedClass(method.getReturnType());if ("filter".equals(property) || "listener".equals(property)) {parameterType = String.class;value = StringUtils.join((String[]) value, ",");} else if ("parameters".equals(property)) {parameterType = Map.class;value = CollectionUtils.toStringMap((String[]) value);}try {Method setterMethod = getClass().getMethod(setter, new Class<?>[] { parameterType });setterMethod.invoke(this, new Object[] { value });} catch (NoSuchMethodException e) {// ignore}}} catch (Throwable e) {logger.error(e.getMessage(), e);}}}}

这个方法,也比较简单,就是将注解中的各个方法找出来,加上set然后反射执行。

另一个BUG

分析这个方法,还会发现Dubbo中另一个Bug:if (value != null && ! value.equals(method.getDefaultValue())){}只有当注解中的配置项的值与默认值不一致才会生效

比如:启动时检查 按Dubbo的文档中描述:

Dubbo缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止Spring初始化完成,以便上线时,能及早发现问题,默认check=true。

但是,com.alibaba.dubbo.config.annotation.Reference#check默认值是false。

2.方案

所以,如果想通过注解完成这个动作,就需要自己动手解决了。 思路有以下几个:

  1. 扩展com.alibaba.dubbo.config.annotation.Reference注解。
  2. 完全使用自定义注解来完成。

在《改造Dubbo,使其能够兼容Spring 4注解配置》中,曾经分析过,Dubbo的注解完全是由AnnotationBean来完成的。所以,以上两种方式,都是需要修改AnnotationBean的处理逻辑。

com.alibaba.dubbo.config.annotation.Reference并没有加上@Inherited,所以,要想不修改Dubbo源码来扩展Reference是难以操作的。

如果在完全替换,使用自定义注解。那就会牵连着要改动ReferenceBean,ReferenceConfig

所以,只好在保留Reference体系的基础上,叠加上自定义处理过程了。

3.实施

自定义注解:

InterfaceMethods:

package com.roc.dubbo.config.spring.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** Created by roc on 2017/3/30.*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface InterfaceMethods {Method[] methods() default {};
}

Method:

package com.roc.dubbo.config.spring.annotation;import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;/*** Dubbo方法配置* Created by roc on 2017/3/30.*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Method {/*** @return 方法名*/String name();/*** @return 负载均衡策略*/String loadbalance() default "";/*** @return 远程服务调用重试次数*/int retries() default -1;/*** @return 方法使用线程数限制*/int executes() default 0;/*** @return 每服务消费者最大并发调用限制*/int actives() default 0;/*** @return 是否过时*/boolean deprecated() default false;/*** @return 是否需要开启stiky策略*/boolean sticky() default false;/*** @return 是否需要返回*/boolean isReturn() default true;/*** @return 方法调用超时时间(毫秒)*/int timeout() default -1;
}

继上次《改造Dubbo,使其能够兼容Spring 4注解配置》扩展的com.roc.dubbo.config.spring.AnnotationBean的基础上,添加自定义注解的处理逻辑:

增加:getMethods方法

private com.roc.dubbo.config.spring.annotation.Method[] getMethods(AccessibleObject accessibleObject) {InterfaceMethods interfaceMethods = accessibleObject.getAnnotation(InterfaceMethods.class);com.roc.dubbo.config.spring.annotation.Method[] methodArray = null;if (interfaceMethods != null) {methodArray = interfaceMethods.methods();}return methodArray;}

修改:refer方法

private Object refer(Reference reference, Class<?> referenceClass, com.roc.dubbo.config.spring.annotation.Method[] methodArray) { // method.getParameterTypes()[0].......String methodKey = "&";if (methodArray != null) {for (int i = 0; i < methodArray.length; i++) {methodKey += methodArray[i] + "&";}}String key = reference.group() + "/" + interfaceName + ":" + reference.version() + methodKey;ReferenceBean<?> referenceConfig = referenceConfigs.get(key);if (referenceConfig == null) {referenceConfig = new ReferenceBean<Object>(reference);//修复check默认值的BUG
//            referenceConfig.setCheck(reference.check());// >>>>>>>>>>>>>>>>>>>>>>>>if (void.class.equals(reference.interfaceClass()) && "".equals(reference.interfaceName())&& referenceClass.isInterface()) {referenceConfig.setInterface(referenceClass);}if (methodArray != null && methodArray.length > 0) {List<MethodConfig> methodConfigList = new ArrayList<>();for (int i = 0; i < methodArray.length; i++) {com.roc.dubbo.config.spring.annotation.Method m = methodArray[i];MethodConfig mc = new MethodConfig();mc.setName(m.name());mc.setDeprecated(m.deprecated());mc.setExecutes(m.executes());if (m.retries() >= 0) {mc.setRetries(m.retries());}if (StringUtils.isNotEmpty(m.loadbalance())) {mc.setLoadbalance(m.loadbalance());}if(m.timeout() >= 0){mc.setTimeout(m.timeout());}mc.setActives(m.actives());mc.setSticky(m.sticky());mc.setReturn(m.isReturn());methodConfigList.add(mc);}referenceConfig.setMethods(methodConfigList);}.........}referenceConfigs.putIfAbsent(key, referenceConfig);referenceConfig = referenceConfigs.get(key);}return referenceConfig.get();}

这样,在使用时需要同时使用Dubbo注解和自定义注解:

@Reference
@InterfaceMethods(methods = {@Method(name = "applyPay", retries = 0, timeout = 3000)})private PayService payCenter;

通过这样改动,即可在不改动Dubbo源码的基础上(不用对Dubbo重编译打包)加上自定义注解处理逻辑。

4 其他解题思路

上面通过自定义注解,并在AnnotationBean中添加额外逻辑来处理。如果仅仅是为了加上方法超时,重试等配置参数,仍然感觉比较繁杂。

其实,Dubbo的服务注册、引用处理最终表现形式就是就是注册中心的URL。 例如:

consumer://127.0.0.1/com.roc.pay.service.PayService?application=egege_web&applyPay.timeout=4000&category=consumers&check=false&default.check=false&default.group=TEST&dubbo=2.5.3&interface=com.roc.pay.service.PayService&methods=applyBatchPay,applyPay&organization=com.egege&owner=egege&pid=1303&revision=0.1.1&side=consumer×tamp=1490867465568

URL中对于applyPay方法的超时配置表现为:applyPay.timeout=4000

这样就说明各种配置,最终都是转换成了URL的参数。

所以,我们其实可以通过Dubbo注解中的parameters属性来直接操作URL:

@Reference(parameters = {"applyPay.timeout","4000"})
private PayService payCenter;

这样即可。

转载于:https://my.oschina.net/roccn/blog/871032

改造Dubbo,使其可以对接口方法进行注解配置相关推荐

  1. Sql server 2005日志文件太大,使其减小的方法

    Sql server 2005日志文件太大,使其减小的方法 Sqlserver2005日志文件太大,使其减小的方法: 运行下面的三行 dbName为数据库名: backup log dbNamewit ...

  2. Sqlserver2005日志文件太大,使其减小的方法

    Sqlserver2005日志文件太大,使其减小的方法: 运行下面的三行 dbName为数据库名: backup log dbNamewith NO_LOG  backup log dbNamewit ...

  3. 织梦 php MIP改造,织梦dedecms MIP改造图片img转img-mip处理方法

    MIP 十分关注页面速度,也因此禁用了一些引起拖慢速度的HTML 标签(禁用列表).例如,标签会引起浏览器的repaint 和reflow,为了避免这些,MIP 提供了替代标签.下面淘站网为大家介绍d ...

  4. spring:利用Spring AOP 使日志输入与方法分离

    对方法进行日志输出是一种很常见的功能.传统的做法是把输出语句写在方法体的内部,在调用该方法时,用输入语句输出信息来记录方法的执行! 1.先写一个普通类: package com.importnew;p ...

  5. tmux的使用方法和个性化配置

    tmux的使用方法和个性化配置 - liuerfire's Blog - Linux, Python, Vim, OnePiece... tmux的使用方法和个性化配置 liuerfire poste ...

  6. Servlet和HTTP请求协议-学习笔记01【Servlet_快速入门-生命周期方法、Servlet_3.0注解配置、IDEA与tomcat相关配置】

    Java后端 学习路线 笔记汇总表[黑马程序员] Servlet和HTTP请求协议-学习笔记01[Servlet_快速入门-生命周期方法.Servlet_3.0注解配置.IDEA与tomcat相关配置 ...

  7. @transactional注解_为啥同一个类中普通方法调用Spring注解方法,注解会失效?看完你就明白,So easy!...

    Spring注解(如@Transactional.@Cacheable.@Async等),在使用不当时,很可能会失效.失效的情况有很多种,本文我们就来瞅瞅,为啥同一个类中普通方法调用Spring注解方 ...

  8. Dubbo(九)之注解配置

    转载自  Dubbo注解配置 以注解配置的方式来配置你的 Dubbo 应用 提示 需要 2.6.3 及以上版本支持. 点此查看 完整示例 服务提供方 Service注解暴露服务 @Service pu ...

  9. rhel mysql安装_RHEL6.4下MySQL安装方法及简单配置

    1.MySQL安装方法简介 1.rpm包yum安装 2.通用二进制包安装 3.源码编译安装 注意:实验所采用的系统平台为:RHEL6.4 2.rpm ins 首页 → 数据库技术 背景: 阅读新闻 R ...

最新文章

  1. CososJS学习笔记(1) 环境配置(填坑版,让你少走弯路!)
  2. 用神经网络测量训练集的半衰期
  3. 智邦国际怎么样?企业管理软件好不好?
  4. django学习之Model(四)MakingQuery
  5. strstr和memcmp函数的实现
  6. 由C过渡到C++-入门知识点
  7. python识别发票二维码_python如何实现二维码的生成和识别
  8. [SAP ABAP开发技术总结]OLE
  9. matlab处理最优化问题,matlab求最优化问题
  10. 5G 是否能让国产手机回到群雄割据时代?
  11. IDEA 编写JDBC 第一个示例
  12. ubuntu16.04下 sublime text输入中文
  13. Android 照相机
  14. VS 0x80041FEB
  15. 瘦了红颜, 多了寂寞
  16. CTS测试中的testSensorOperations项
  17. mongodb 启动报错 ERROR: child process failed, exited with error number 1
  18. python制作网页难吗_怎么做网页制作(用python做网页与html)
  19. MCU_如何通过硬件VID 查找生产厂家
  20. ORACLE12.2 RAC+DG(2+1) RU patch步骤

热门文章

  1. 周三多管理学第七版pdf_考研(管理学)相对好考的211院校推荐
  2. Git 只拉取部分文件
  3. java plus方法_Java中MyBatis Plus知识点总结
  4. python transformers_transformers 安装
  5. 数组怎么用getchar_C语言 | 数组
  6. GitLab安装文档
  7. [HDU] Tr A
  8. 20162311 算法复杂度-3
  9. LeetCode Minimum Moves to Equal Array Elements II
  10. C#:添加web service引用