- 浏览: 99421 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (50)
- druid (1)
- java (7)
- database (1)
- 源码 (6)
- dubbo (4)
- jetty (1)
- protocol (1)
- 工作流 (3)
- 电商 (1)
- 支付平台 (1)
- 大数据 (0)
- 微信小程序 (1)
- 短信平台 (1)
- jms (1)
- jndi (1)
- spi (1)
- FileUpload (0)
- concurrent (1)
- 统计业务 (0)
- 业务 (3)
- sql (2)
- andriod (1)
- maven (1)
- OAuth (1)
- ws (1)
- spring (6)
- mybatis (1)
- java rocketmq (0)
- rocketmq (1)
- RxJava (1)
- java模式 (1)
- AI (0)
- 机器学习 (1)
- springboot (1)
- tomcat (1)
- 协议 (1)
- springcloud (1)
- stream (1)
- 技术管理 (1)
- k8s (1)
- ser (0)
- istio (1)
- ddd (1)
- 微服务 (1)
- 操作笔记 (1)
最新评论
-
herman_liu76:
luozhen89 写道讲得很好。知识后面的KAFKA跟OAu ...
尽量把OAuth2.0的原理讲透透的 -
luozhen89:
讲得很好。知识后面的KAFKA跟OAuth有什么关系,没看懂。 ...
尽量把OAuth2.0的原理讲透透的 -
herman_liu76:
ZHENFENGSHISAN 写道太累了啊,哥唉~ 我也觉得很 ...
代码快看哭了-吐槽与感悟汇总 -
ZHENFENGSHISAN:
太累了啊,哥
代码快看哭了-吐槽与感悟汇总 -
herman_liu76:
1126481146 写道厉害啊,有联系方式吗,学习学习,我现 ...
druid 源码分析与学习(含详细监控设计思路的彩蛋)
Dubbo以前也看过些源码,正好同事写了一个基于netty的通讯架构,想自己试试模仿dubbo,使用此通讯架构写一个RPC框架学习一下。根据百度百科定义:Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。我的目标仅是实现一个与spring集成的rpc调用框架。
初看了一下dubbo的解析XML标签,平时工作太忙了,写什么自定义标签解析也很麻烦,就没进展了。直到有一天,因为项目中处理mybatis的mapper接口冲突问题,为了解决问题,看了一下mybatis的一点源码,丰富了对与spring整合的技巧。感觉可以结合dubbo与mybatis与spring的整合技巧,用很少的时间,很少的代码写一个RPC框架了。
核心功能:客户端动态代理,把接口类,方法,参数类,参数值发过去,服务端按这个找到可用的接口实现,再通过反射调用得到返回值,再传回客户端。
本文先介绍一下碰到的mybatis冲突问题与解决,再介绍对这个仿dubbo框架的构思,再介绍如何实现客户端与服务端的代码的。最后总结一下,特别是对druid,dubbo学习后的使用体会与实践。由于时间仓促,学艺不精,里面有错误欢迎指正。
一、mybatis使得中碰到的问题
最近的一个应用中,同一个数据库使用了两套mybatis的dao层,一套是老系统遗留的,一套是新做的公共maven包。由于自动生成,只是包不同,mapper接口名都一样,所以类似com.a.userMapper.java与com.b.userMapper.java这样的接口类共存,结果发现spring启动冲突了。
先介绍一下IOC容器中规则,从name,或者是别名,或者id,一定只能得到一个bean;
从type可以得到一个bean,或者是一组bean。
在加载bean的时候,默认有个校验机制,SpringMVC中bean的加载,是采用类似 键值对(key/value)的映射方式存储的,而当中的(key)键,默认是用类名来作为键的(如果不取别名的话)。这样如果不同包路径下的两个组件(controller/service)重名的话就会触发这个校验机制,抛异常。
网上查到的解决方案都是取别名。但是普通的类可以写别名,可不知道如何介入到mapper接口的实现类中呢?人家是动态生成的。后来看了点文章,spring的IOC容器提供了好几个接口,允许你加入你的控制。按介入的先后顺序,总结如下:
1. BeanNameGenerator:beandefination是IOC中最核心的对象了。在生成beandefination时,可以有BeanNameGenerator介入,提供一个产生名字的规则。解决mybatis的那个冲突就是靠这个了。
2.BeanFactoryPostProcessor: Spring中BeanFactoryPostProcessor和下面介绍的BeanPostProcessor都是Spring初始化bean时对外暴露的扩展点。PostProcessor部分表示这是一个后置处理接口,相应还有前置处理接口。这里Factory的处理(工厂)肯定比bean产生的早,所以前者早于后者。BeanFactoryPostProcessor确实是在beandefination准备好之后可以介入的,它可以修改beanDefination。
3.BeanPostProcessor:它是对从beandefination实例化后的Bean做进一步的操作。比如Aware一些外部的东西,容器事件监听啊什么的。
4.最后还有一个InitializingBean接口,它的afterpropetySet接口我经常用。就是当bean生成了,感知aware外部也好了,一切都准备好了。那可能做些初始化的工作了。比如我之前在此方法中会启动通讯组件,或者微核心框架,并把自己引用的实现了微核心中接口的属性类传进去,以便让这些通讯组件或者微核心框架正常工作。都会放在这个接口的方法中实现,spring会调用这个接口方法的。
既然知道问题是名字冲突,冲突的同名接口的beanDefination都无法生成,所以只能想办法修改bean名字了,要是可以用包全名肯定不冲突。在spring的配置中,扫描注解对象中可以配置一个BeanNameGenerator,但mabatis中怎么办?只好看源码,从spring-mybatis配置中看到这个类MapperScannerConfigurer,于是点进去看了一个源码,一个熟悉的对象出现了,就是BeanNameGenerator,不正是切入点嘛,于是我在配置的属性中加入自定义的namegenarator类,再用实现BeanFactoryPostProcessor类来打印出所有的beandefination,果然不冲突了,都是包全名。这样冲突解决了,如果一个service中使用上面两个原来冲突的mapper接口,eclipse会提示选择一个,或者写全名。
处理好冲突,正好想到mybatis也是根据一个提供的接口,动态代理实现数据库操作嘛。dubbo的客户端也是这样啊,为何mybatis不用xml定义标签呢?那正好学习一下mybatis的方式。
mybatis通过MapperScannerConfigurer扫描配置的mapper接口与xml文件,再加入sqlsession,产生一个个beanDefination,定义里放置的是实现了beanFactory的类,再由这个类的getObject来动态产生了一个实现接口的类。事实上,当beanDefination中如果放了一个beanFactory的类,就会被spring调用产生另外你getObject产生的类,而不是通常new出来的类。就正是可以做手脚的地方。dubbo是通过自定义的标签解析,来产生同样的beanDefination,里面放的也是实现了beanFactory的类。看来是异曲同工,最终目标就是产生这样的beanDefination。好吧,于是我选择mybatis的方式来写自己的miniDubbo了,放弃dubbo的标签定义与解析。
mybatis的那个配置类MapperScannerConfigurer是实现了postProcessBeanDefinitionRegistry接口,在这个过程中扫描那些DAO与XML并产生一个个beanDefination的。我也这么用了。
话说前面介绍的几个介入的接口没有它啊,查了一下:
public interface BeanDefinitionRegistryPostProcessor
extends BeanFactoryPostProcessor。好吧,它是BeanFactoryPostProcessor的子接口。介入的位置正好是在beanDefination产生后。
二、客户端的设计
客户端的功能就是找到所有的接口,产生出一个个实现类,实现类功能不是执行方法调用,因为没有真正的功能实现类。只是通过代理的实现类,把获取到的接口的名子,方法的名字,参数类型,参数值等传给服务器,服务器当然找到服务器上实现相同接口的真正的实现类,调用后,得到值,再由服务器传递回来。客户端把传递过来的值返回调用者。
调用者是透明调用,不知道其后面是远程调用,与本地调用的感觉是一样一样的。都可以是autowired的接口。
要实现对任何接口的适用,动态代理是少不了的。除非你一个接口写一个静态代理。动态代理中最主要是一个invocation的产生,把在invoke方法中,把参数类型,值都发出去。
设计以下类:
1. Configurer类,它配置在spring-context.xml中,模仿mybatis,配置好接口。咱不学习dubbo的自定义标准产生beanDefination的方式了。它用于加载配置的接口,产生bean定义并放入springIOC中。它 implements BeanDefinitionRegistryPostProcessor。在postProcessBeanDefinitionRegistry方法中new 出一个个Beandefination,里面放置一个 2
2.ReferenceBean类,正是上面的beanDefination中放入的类。ReferenceBean<T> implements FactoryBean接口。在getObject()方法getObject()中返回真正的代理类T。每一个接口产生一个bean定义,每个bean定义中放的就是这样一个类。
ReferenceBean中的动态代理类怎么生成呢?在ReferenceBean类的afterPropertiesSet()中可以生成啊。
3.还要有一个接口。客户端只有这个接口。
4.主要的类就3个,其它还有一个controller,将会autowired这个接口用于测试。
三、服务端的设计
服务端的功能,首先收到信息的是通讯服务端。我们知道基于netty的tcp通讯框架主要是处理channelHandler。客户端其实把调用的接口,方法,参数类型与值传过来,你也可以简单传过来一些String,关键是找到服务端的spring IOC中的实现类。实现真正调用。想到实现任何的接口方法,调用实现类,那肯定反射调用了。method.invoke()。
我用的这个通讯组件传的是方法名,参数是jason对象的字符串。服务端根据方法名,找到实现channelHandler(msgHandler)的一个注册进来的类,用这个类处理消息并返回的,返回的意思就是写回netty通道给客户端。是根据方法,并不是根据接口名,找到处理类的。接口名与方法名不是一个级别的。dubbo好象有一个请求结构体,封装了类、方法、参数类型与值,我不搞那么复杂了。
客户端发过来的消息是有方法名的,再找到处理类。我干脆不管什么方法,都用同一个handler吧,在handler中再找找map存放方法与对应处理类。收到任何消息,就用同一个处理类,它的方法会从消息体内含有的方法名查找这个方法要用的实现类。我如何建的这个map,会放置好方法名对应的接口实现类呢?
这个接口实现类是spring管理的常见的接口实现类。如何把自己放入另一个map中呢?也不是所有实现类都要放MAP中,有些不需要暴露出来给通讯层又的如何区分呢?所以自己实现注册进MAP并不好,还是学习dubbo方式吧(当然可以用自己的annotation,在beanDefinaton产生后介入处理),从spring配置中拿到信息,这样再从spring中拿到实现类,构建这样一个过渡类吧。想想在mybatis中,每个动态代理类必然要有一个数据库连接,连接是IOC中的对象,所以都要拿到容器中的连接对象。
要从方法反射调用类,干脆定义一个invocor对象作为过渡类吧,它持有接口实现类的对象,还有访求参数对象数组与参数值对象数组。这样,前面说的MAP中就是方法名与持有实现类的invoker类了。
总体的实现策略,与客户端一样,也是一个配置类开始的,再产生一个个beanDefination,里面放置一个serviceBean类持有配置参数,再它的afterporpetyset中,产生一个invoker类,并从容器中找到实现类给它,参数也给它,就完整了。再把它们注册到通讯层处理的静态map表中。
另外通讯层的tcp服务在哪启动呢?要么在配置类的afterpropetySet中,也可以在serviceBean的afterpropetySet方法中启动。反正启动一次,在整个spring容器启动中完成就行了,serviceBean比较多,可能会重复。就选择在配置类config中启动吧。
1.config类,用于产生serviceBean的定义,并启动服务。它配置在spring-context.xml中,模仿mybatis,并设置接口与实现是谁。
2.ServiceBean.java. 上面的代码中beanDefinition.setBeanClass(ServiceBean.class);这句是用的它。它的目标是产生invoker对象,并把真正实现类的定义置给它。最后它会把invoker对象注册到通讯处理类的map中,供选用。
3. InvokerHolder类,它是被serviceBean生成的,serviceBean生了它,并传递给它实现类后,serviceBean也就没啥用了。这个InvokerHolder类就是根据参数值,进行反射调用。等通讯层找到它,用通讯层拿到的值设置好这个对象的值,就可以调用了。
public class InvokerHolder {
Object proxy;
String methodName;
Class<?>[] parameterTypes;
Object[] arguments;
protected Object doInvoke(Object[] arguments) throws Exception
{
Method method = proxy.getClass().getMethod(methodName, parameterTypes);
return method.invoke(proxy, arguments);
}
}
4.MsgHandler类,这个是低层通讯用的,从通讯中拿到需要的数据,找到invoker,并调用,再处理返回值写入nettyChannel。
5. 接口类与接口实现类,这个就不说明了。客户端接口与服务端接口应该是同一个包的。客户端没有实现,服务端有。
下图为服务端工程,类不是很多,上面都介绍过:
下图为客户端运行结果,客户端controller中autowired了接口,服务端invoker了调用。只有最后一句“我从服务端实现了这个接口”是接口方法的返回值。
四、总结
上述功能已经在测试中很快实现了。以前看过些源码,但真正写出来,写之前还是思考不少东西。代码虽然不多,平时就要有空想想,看资料还是花了些时间的。碰巧平时处理项目中的mybatis问题中发现了另一种处理方式,所以快速就写出来。
之前看过些dubbo与druid的源码,但直到近来在项目中使用,才真正感觉到源码的价值了。不久前写一个业务数据内存中的处理,用到了druid中的连接池的线程管理方式。写另一业务依次处理中,用到了我前面一个文章写的过滤链模式。dubbo中的主要的代理与技术目前还没有在实际中用过,但对于类的设计与关系处理更透彻了,看其它代码也很快。有些人喜欢看java的源码,但我建议看这两个项目的源码,这是更系统化的解决问题的代码,而不仅是什么arrayList,Hashmap里面一些技巧的学习。有点象设计模式与具体代码技巧的区别。
如果你写基于netty的通讯架构,可以模仿dubbo协议。如果用到集群,用到SPI,用到策略,用到与zookeeper, redis连接,如果让系统集成多种实现需要写抽象层的代码,也可以学习它的思路。
现在dubbo与spring cloud是微服务的两个主流,不知道spring cloud中有啥可以借鉴的,过一阵有空用用它。
初看了一下dubbo的解析XML标签,平时工作太忙了,写什么自定义标签解析也很麻烦,就没进展了。直到有一天,因为项目中处理mybatis的mapper接口冲突问题,为了解决问题,看了一下mybatis的一点源码,丰富了对与spring整合的技巧。感觉可以结合dubbo与mybatis与spring的整合技巧,用很少的时间,很少的代码写一个RPC框架了。
核心功能:客户端动态代理,把接口类,方法,参数类,参数值发过去,服务端按这个找到可用的接口实现,再通过反射调用得到返回值,再传回客户端。
本文先介绍一下碰到的mybatis冲突问题与解决,再介绍对这个仿dubbo框架的构思,再介绍如何实现客户端与服务端的代码的。最后总结一下,特别是对druid,dubbo学习后的使用体会与实践。由于时间仓促,学艺不精,里面有错误欢迎指正。
一、mybatis使得中碰到的问题
最近的一个应用中,同一个数据库使用了两套mybatis的dao层,一套是老系统遗留的,一套是新做的公共maven包。由于自动生成,只是包不同,mapper接口名都一样,所以类似com.a.userMapper.java与com.b.userMapper.java这样的接口类共存,结果发现spring启动冲突了。
先介绍一下IOC容器中规则,从name,或者是别名,或者id,一定只能得到一个bean;
从type可以得到一个bean,或者是一组bean。
在加载bean的时候,默认有个校验机制,SpringMVC中bean的加载,是采用类似 键值对(key/value)的映射方式存储的,而当中的(key)键,默认是用类名来作为键的(如果不取别名的话)。这样如果不同包路径下的两个组件(controller/service)重名的话就会触发这个校验机制,抛异常。
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException { if (!this.registry.containsBeanDefinition(beanName)) { return true; } BeanDefinition existingDef = this.registry.getBeanDefinition(beanName); BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition(); if (originatingDef != null) { existingDef = originatingDef; } if (isCompatible(beanDefinition, existingDef)) { return false; } throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName + "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " + "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]"); }
网上查到的解决方案都是取别名。但是普通的类可以写别名,可不知道如何介入到mapper接口的实现类中呢?人家是动态生成的。后来看了点文章,spring的IOC容器提供了好几个接口,允许你加入你的控制。按介入的先后顺序,总结如下:
1. BeanNameGenerator:beandefination是IOC中最核心的对象了。在生成beandefination时,可以有BeanNameGenerator介入,提供一个产生名字的规则。解决mybatis的那个冲突就是靠这个了。
2.BeanFactoryPostProcessor: Spring中BeanFactoryPostProcessor和下面介绍的BeanPostProcessor都是Spring初始化bean时对外暴露的扩展点。PostProcessor部分表示这是一个后置处理接口,相应还有前置处理接口。这里Factory的处理(工厂)肯定比bean产生的早,所以前者早于后者。BeanFactoryPostProcessor确实是在beandefination准备好之后可以介入的,它可以修改beanDefination。
3.BeanPostProcessor:它是对从beandefination实例化后的Bean做进一步的操作。比如Aware一些外部的东西,容器事件监听啊什么的。
4.最后还有一个InitializingBean接口,它的afterpropetySet接口我经常用。就是当bean生成了,感知aware外部也好了,一切都准备好了。那可能做些初始化的工作了。比如我之前在此方法中会启动通讯组件,或者微核心框架,并把自己引用的实现了微核心中接口的属性类传进去,以便让这些通讯组件或者微核心框架正常工作。都会放在这个接口的方法中实现,spring会调用这个接口方法的。
既然知道问题是名字冲突,冲突的同名接口的beanDefination都无法生成,所以只能想办法修改bean名字了,要是可以用包全名肯定不冲突。在spring的配置中,扫描注解对象中可以配置一个BeanNameGenerator,但mabatis中怎么办?只好看源码,从spring-mybatis配置中看到这个类MapperScannerConfigurer,于是点进去看了一个源码,一个熟悉的对象出现了,就是BeanNameGenerator,不正是切入点嘛,于是我在配置的属性中加入自定义的namegenarator类,再用实现BeanFactoryPostProcessor类来打印出所有的beandefination,果然不冲突了,都是包全名。这样冲突解决了,如果一个service中使用上面两个原来冲突的mapper接口,eclipse会提示选择一个,或者写全名。
处理好冲突,正好想到mybatis也是根据一个提供的接口,动态代理实现数据库操作嘛。dubbo的客户端也是这样啊,为何mybatis不用xml定义标签呢?那正好学习一下mybatis的方式。
mybatis通过MapperScannerConfigurer扫描配置的mapper接口与xml文件,再加入sqlsession,产生一个个beanDefination,定义里放置的是实现了beanFactory的类,再由这个类的getObject来动态产生了一个实现接口的类。事实上,当beanDefination中如果放了一个beanFactory的类,就会被spring调用产生另外你getObject产生的类,而不是通常new出来的类。就正是可以做手脚的地方。dubbo是通过自定义的标签解析,来产生同样的beanDefination,里面放的也是实现了beanFactory的类。看来是异曲同工,最终目标就是产生这样的beanDefination。好吧,于是我选择mybatis的方式来写自己的miniDubbo了,放弃dubbo的标签定义与解析。
mybatis的那个配置类MapperScannerConfigurer是实现了postProcessBeanDefinitionRegistry接口,在这个过程中扫描那些DAO与XML并产生一个个beanDefination的。我也这么用了。
话说前面介绍的几个介入的接口没有它啊,查了一下:
public interface BeanDefinitionRegistryPostProcessor
extends BeanFactoryPostProcessor。好吧,它是BeanFactoryPostProcessor的子接口。介入的位置正好是在beanDefination产生后。
二、客户端的设计
客户端的功能就是找到所有的接口,产生出一个个实现类,实现类功能不是执行方法调用,因为没有真正的功能实现类。只是通过代理的实现类,把获取到的接口的名子,方法的名字,参数类型,参数值等传给服务器,服务器当然找到服务器上实现相同接口的真正的实现类,调用后,得到值,再由服务器传递回来。客户端把传递过来的值返回调用者。
调用者是透明调用,不知道其后面是远程调用,与本地调用的感觉是一样一样的。都可以是autowired的接口。
要实现对任何接口的适用,动态代理是少不了的。除非你一个接口写一个静态代理。动态代理中最主要是一个invocation的产生,把在invoke方法中,把参数类型,值都发出去。
设计以下类:
1. Configurer类,它配置在spring-context.xml中,模仿mybatis,配置好接口。咱不学习dubbo的自定义标准产生beanDefination的方式了。它用于加载配置的接口,产生bean定义并放入springIOC中。它 implements BeanDefinitionRegistryPostProcessor。在postProcessBeanDefinitionRegistry方法中new 出一个个Beandefination,里面放置一个 2
<bean id="configurer" class="dubbura.spring.Configurer"> <property name="beanName" value="msgSendI"></property> </bean>
2.ReferenceBean类,正是上面的beanDefination中放入的类。ReferenceBean<T> implements FactoryBean接口。在getObject()方法getObject()中返回真正的代理类T。每一个接口产生一个bean定义,每个bean定义中放的就是这样一个类。
ReferenceBean中的动态代理类怎么生成呢?在ReferenceBean类的afterPropertiesSet()中可以生成啊。
public void afterPropertiesSet() throws Exception { // TODO Auto-generated method stub System.out.println("begin get the proxy object"); InvocationHandler invocationHandler=new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("method invoke:"+method.getName()); System.out.println("method invoke:"+args[0].toString()); Client client=ClientCenter.getAClient("192.168.117.35", 9166, "85f035103434343402655fff888", "h4343888",null); MiddleMsg msg = new MiddleMsg(method.getName(), args[0].toString()); //下面是发同步消息 MiddleMsg resultMsg=client.sendMsgSync(msg); System.out.println("【收到消息:】"+resultMsg.getBody()); ResultObject result=new ResultObject(); Map map=new HashMap(); map.put("data",resultMsg.getBody()); result.setAttachment(map); // return method.invoke(this, args); return result; } }; invoker=invocationHandler; ref =(T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] { interfaceClass }, invocationHandler); System.out.println("has create the Proxy Bean"); }
3.还要有一个接口。客户端只有这个接口。
4.主要的类就3个,其它还有一个controller,将会autowired这个接口用于测试。
三、服务端的设计
服务端的功能,首先收到信息的是通讯服务端。我们知道基于netty的tcp通讯框架主要是处理channelHandler。客户端其实把调用的接口,方法,参数类型与值传过来,你也可以简单传过来一些String,关键是找到服务端的spring IOC中的实现类。实现真正调用。想到实现任何的接口方法,调用实现类,那肯定反射调用了。method.invoke()。
我用的这个通讯组件传的是方法名,参数是jason对象的字符串。服务端根据方法名,找到实现channelHandler(msgHandler)的一个注册进来的类,用这个类处理消息并返回的,返回的意思就是写回netty通道给客户端。是根据方法,并不是根据接口名,找到处理类的。接口名与方法名不是一个级别的。dubbo好象有一个请求结构体,封装了类、方法、参数类型与值,我不搞那么复杂了。
客户端发过来的消息是有方法名的,再找到处理类。我干脆不管什么方法,都用同一个handler吧,在handler中再找找map存放方法与对应处理类。收到任何消息,就用同一个处理类,它的方法会从消息体内含有的方法名查找这个方法要用的实现类。我如何建的这个map,会放置好方法名对应的接口实现类呢?
这个接口实现类是spring管理的常见的接口实现类。如何把自己放入另一个map中呢?也不是所有实现类都要放MAP中,有些不需要暴露出来给通讯层又的如何区分呢?所以自己实现注册进MAP并不好,还是学习dubbo方式吧(当然可以用自己的annotation,在beanDefinaton产生后介入处理),从spring配置中拿到信息,这样再从spring中拿到实现类,构建这样一个过渡类吧。想想在mybatis中,每个动态代理类必然要有一个数据库连接,连接是IOC中的对象,所以都要拿到容器中的连接对象。
要从方法反射调用类,干脆定义一个invocor对象作为过渡类吧,它持有接口实现类的对象,还有访求参数对象数组与参数值对象数组。这样,前面说的MAP中就是方法名与持有实现类的invoker类了。
总体的实现策略,与客户端一样,也是一个配置类开始的,再产生一个个beanDefination,里面放置一个serviceBean类持有配置参数,再它的afterporpetyset中,产生一个invoker类,并从容器中找到实现类给它,参数也给它,就完整了。再把它们注册到通讯层处理的静态map表中。
另外通讯层的tcp服务在哪启动呢?要么在配置类的afterpropetySet中,也可以在serviceBean的afterpropetySet方法中启动。反正启动一次,在整个spring容器启动中完成就行了,serviceBean比较多,可能会重复。就选择在配置类config中启动吧。
1.config类,用于产生serviceBean的定义,并启动服务。它配置在spring-context.xml中,模仿mybatis,并设置接口与实现是谁。
<bean id="configurer" class="dubburaver.spring.Configurer"> <property name="interfaceName" value="MsgSendI"></property> <property name="actionName" value="spush_notification"></property> </bean>
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { // TODO Auto-generated method stub System.out.println("begin......"); BeanDefinition beanDefinitionref = registry.getBeanDefinition("msgSendImpl"); int a=registry.getBeanDefinitionCount(); System.out.println(a); // BeanDefinition candidate=null; RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClass(ServiceBean.class); beanDefinition.setBeanClassName(ServiceBean.class.getName()); beanDefinition.getPropertyValues().addPropertyValue("interfaceName", actionName); // definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));//mybatis的方式:运行时加载对象 beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(beanDefinitionref, "msgSendI"));//dubbo的方式:定义中引用定义。 registry.registerBeanDefinition(ServiceBean.class.getName(), beanDefinition); int aa=registry.getBeanDefinitionCount(); System.out.println(aa); }
public void afterPropertiesSet() throws Exception { initMiddlerWareStart(actionName,MsgHandler.class);//启动TCP监听 } public void initMiddlerWareStart(String msgName, Class clazz) { System.out.println("启动服务器,在9166端口监听tcp"); try { MsgServiceHandlerRegister register = MsgServiceHandlerRegister.getRegister(); // 注册事件处理类 register.addMsgServiceHandler(msgName, clazz); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub try { java.util.List<ClientApp> clientAppList = new java.util.ArrayList<ClientApp>(); ClientApp eachClient = new ClientApp();// 通讯层用的客户对象,校验客户端 eachClient.setAppKey("85f035102cb411e8b971002655fff888"); eachClient.setAppSecret("homer888"); clientAppList.add(eachClient); ServerInit.init(9166, clientAppList); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); // isMiddlewearStarted=false; } } }).start(); System.out.println("启动消息服务成功!端口:" + 9166); } catch (Exception e) { // isMiddlewearStarted = false; e.printStackTrace(); System.out.println("启动消息服务失败!异常:" + e.toString()); } }
2.ServiceBean.java. 上面的代码中beanDefinition.setBeanClass(ServiceBean.class);这句是用的它。它的目标是产生invoker对象,并把真正实现类的定义置给它。最后它会把invoker对象注册到通讯处理类的map中,供选用。
public void afterPropertiesSet() throws Exception { System.out.println("【serviceBean】afterPropertiesSet..."); System.out.println("【serviceBean】interfaceName..."+interfaceName); InvokerHolder invokerHolder=new InvokerHolder(); invokerHolder.methodName=interfaceName; invokerHolder.parameterTypes=new Class[]{String.class}; invokerHolder.proxy=ref;//上面的代码中有置这个值,正被的实现类。在spring的IOC中。 System.out.println("【serviceBean】ref..."+(ref==null?"is null":ref)); MsgHandler.invoderMap.put(interfaceName, invokerHolder);//放入MAP中。 }
3. InvokerHolder类,它是被serviceBean生成的,serviceBean生了它,并传递给它实现类后,serviceBean也就没啥用了。这个InvokerHolder类就是根据参数值,进行反射调用。等通讯层找到它,用通讯层拿到的值设置好这个对象的值,就可以调用了。
public class InvokerHolder {
Object proxy;
String methodName;
Class<?>[] parameterTypes;
Object[] arguments;
protected Object doInvoke(Object[] arguments) throws Exception
{
Method method = proxy.getClass().getMethod(methodName, parameterTypes);
return method.invoke(proxy, arguments);
}
}
4.MsgHandler类,这个是低层通讯用的,从通讯中拿到需要的数据,找到invoker,并调用,再处理返回值写入nettyChannel。
public class MsgHandler implements MsgServiceHandler { public static Map<String, InvokerHolder> invoderMap = new java.util.HashMap<String, InvokerHolder>(); @Override public MiddleMsg handleMsgEvent(MsgEvent dm, MiddleMsg msg) { // TODO Auto-generated method stub System.out.println("invoderMap.size():"+invoderMap.size()); Iterator<Entry<String, InvokerHolder>> it=invoderMap.entrySet().iterator(); while(it.hasNext()){ Entry aa= it.next(); System.out.println("map:"+aa.getKey()+"|"+aa.getValue()); } String action = msg.getHeader().getAction(); String msgBody=msg.getBody().toString(); try { System.out.println("action:"+action); System.out.println("msgBody:"+msgBody); //从请求参数中,找到invoker对象并设置好值。类型啥都舍弃了。 InvokerHolder ref = invoderMap.get(action); Object[] arguments=new Object[]{msgBody};//参数值 ResultObject resultObject = (ResultObject) ref.doInvoke(arguments); String body = "" + resultObject.getStatus() + resultObject.getStatusMsg(); msg.setBody(body); } catch (Exception e) { e.printStackTrace(); } return msg; } }
5. 接口类与接口实现类,这个就不说明了。客户端接口与服务端接口应该是同一个包的。客户端没有实现,服务端有。
下图为服务端工程,类不是很多,上面都介绍过:
下图为客户端运行结果,客户端controller中autowired了接口,服务端invoker了调用。只有最后一句“我从服务端实现了这个接口”是接口方法的返回值。
四、总结
上述功能已经在测试中很快实现了。以前看过些源码,但真正写出来,写之前还是思考不少东西。代码虽然不多,平时就要有空想想,看资料还是花了些时间的。碰巧平时处理项目中的mybatis问题中发现了另一种处理方式,所以快速就写出来。
之前看过些dubbo与druid的源码,但直到近来在项目中使用,才真正感觉到源码的价值了。不久前写一个业务数据内存中的处理,用到了druid中的连接池的线程管理方式。写另一业务依次处理中,用到了我前面一个文章写的过滤链模式。dubbo中的主要的代理与技术目前还没有在实际中用过,但对于类的设计与关系处理更透彻了,看其它代码也很快。有些人喜欢看java的源码,但我建议看这两个项目的源码,这是更系统化的解决问题的代码,而不仅是什么arrayList,Hashmap里面一些技巧的学习。有点象设计模式与具体代码技巧的区别。
如果你写基于netty的通讯架构,可以模仿dubbo协议。如果用到集群,用到SPI,用到策略,用到与zookeeper, redis连接,如果让系统集成多种实现需要写抽象层的代码,也可以学习它的思路。
现在dubbo与spring cloud是微服务的两个主流,不知道spring cloud中有啥可以借鉴的,过一阵有空用用它。
发表评论
-
spring-jms的接收消息功能的设计思考
2020-03-30 23:40 6221、前言 JMS即Java ... -
springboot的aop自动代理实现分析笔记
2020-03-22 23:08 840# 目的 aop功能是spring ... -
spring与tomcat的关系逆袭前后的源码设计分析
2019-12-25 00:15 714补: 最近回看了 ... -
spring中service事务配置传播隔离等关系
2017-09-30 16:03 1183工作中正好碰到这个问题,于是学习以下两个文章。先介绍了 ... -
WebApplicationContext、DispatcherServlet与web容器的ServletContext关系
2017-09-13 10:48 2017用spring框架开发web ... -
dubbo注册部分源码分析、集群策略、负载均衡算法
2016-07-29 21:41 3456前面分别写了二篇文章,介绍dubbo的源码与模拟现场场 ... -
抗战时期延安与上海地下党的远程通信
2016-07-16 22:58 896当ZHUXI(敏感词)在 ... -
Dubbo源码学习之知识点分析(配原理图)
2016-07-01 13:44 12474Dubbo是阿里巴巴公司实现SOA治理的工具,最 ...
相关推荐
springcloud:Dubbo集成RPC框架demo源码案例演示
DUBBO与Spring Cloud的集成通讯示例
分布式服务治理soa面向服务架构 dubbo与spring集成的最小例子
【标题】"dubbo spring4.1集成demo"是一个示例项目,展示了如何将流行的Java微服务框架Dubbo与Spring 4.1版本整合。这个项目涵盖了Dubbo 2.5.3,一个高性能、轻量级的服务治理框架,以及Zookeeper 3.4.5,一个分布式...
spring_dubbo spring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_dubbospring_...
在IT行业中,Zookeeper、Dubbo和Spring是三个非常重要的组件,它们在分布式系统和微服务架构中扮演着核心角色。下面将详细解释这三个技术及其相互间的整合。 **Zookeeper** 是一个分布式的,开放源码的分布式应用...
标题 "springboot2.0.x+dubbo-spring-boot-starter" 涉及的是将流行的微服务框架 Dubbo 集成到 Spring Boot 2.0.x 的项目实践中。这个集成使得开发者能够利用 Spring Boot 的便利性和 Dubbo 的高性能远程服务调用...
Dubbo侧重于高性能RPC通信,并提供了一套完整的服务治理体系;而Spring Cloud则构建在Spring Boot之上,拥有更为广泛的生态系统支持和更加灵活的服务治理方式。开发者可以根据项目的具体需求和技术背景选择合适的...
dubbo与spring4集成maven pom文件 此pom为我生产项目中的配置,开始想省事使用spring boot,结果与springmvc不兼容,导致tomcat启动失败,后来找了maven shade来打包,解决了xsd兼容问题 另外注意,dubbo阿里的分支...
基于Dubbo实现的RPC框架,是Java开发中常见的一种高效率、高性能的服务治理方案,尤其在微服务架构中广泛应用。 Dubbo是由阿里巴巴开源的高性能RPC框架,它提供了服务注册、服务发现、负载均衡、流量控制、熔断降级...
Apache Dubbo(以下简称“Dubbo”)是一款高性能、轻量级的开源Java RPC框架。它提供了三个核心功能:面向接口代理、服务自动注册与发现以及高透明化的负载均衡,这些功能极大地简化了分布式系统之间的调用过程。...
3. **Spring集成**:Spring框架可以与Dubbo无缝集成,通过Spring的XML配置或注解方式,可以方便地声明服务提供者和服务消费者,实现服务的发布和引用。Spring的DI特性使得服务实例化更加灵活,AOP支持则能方便地添加...
Dubbo作为RPC框架,提供了一套完整的解决方案,包括服务注册与发现、负载均衡、故障转移、服务治理等特性。 **Dubbo核心组件** 1. **服务提供者(Provider)**:服务提供者是提供服务的实体,它会暴露服务,并将服务...
在IT行业中,`Dubbo` 是一款非常知名的分布式服务框架,由阿里巴巴开源并维护,它致力于提供高性能、透明化的RPC(远程过程调用)服务,以及服务治理功能。本实例结合了 `Spring` 框架,使得服务的创建、管理和调用...
基于Maven的dubbo集成spring简单实例.网上有不少例子均无法运行,只好自已整理一个可运行的例子.这是最简单可运行的例子,方便大家交流学习.在zookeeper-3.4.8和zookeeper-3.4.9下均可正常运行.
在IT行业中,Dubbo是一个广泛使用的高性能Java RPC框架,它由阿里巴巴开源并维护。Spring则是一个功能强大的企业级应用开发框架,提供了丰富的依赖注入、事务管理等功能。将Dubbo与Spring整合,可以充分利用两者的...
Dubbo可以无缝集成Spring,使得配置和服务治理变得极其简单,通过XML配置或注解方式即可实现服务的声明式管理。 10. **异步调用** Dubbo支持同步和异步调用模式,异步调用能够提高系统的响应速度和并发处理能力,...
本示例"DUBBO_SPRING_DEMO"旨在演示如何将Dubbo与Spring进行深度整合,以便更好地管理和调用服务。这个项目名为"DubboDemo-master",通常包含以下几个关键部分: 1. **服务提供者(Provider)**:提供服务的模块,...
Dubbo 是一个高性能、轻量级的 Java RPC 框架,用于构建分布式服务。Spring 是一个广泛使用的 Java 应用框架,提供依赖注入(DI)和面向切面编程(AOP)等功能,极大地简化了应用开发。MyBatis 是一个持久层框架,它...