最近在看Dubbo的用户手册,看到了回声测试一小节的说明如下,请注意图片中的红框中的部分:
红色部分说:只需要将任意服务引用强制转换为EchoService,即可使用。
看到这里我想起了java中关于强制转换的一个限制:必须有继承关系,就是说两个类之间要能够进行类型转换,必须有继承关系才可以。 可是很明显,我们写的Dubbo服务接口是与EchoService接口没有任何集成关系的,这是如何实现的呢?
带着这个问题,去群里问了一下群主,群主给了如下截图:
1、
2、
3、
4、
可以看到,Dubbo在为我们写的服务创建动态代理的时候,是在传入的接口中人为的增加了“EchoService.class”接口的,也就是说,通过创建动态代理的时候向接口中增加一个接口,来保证强制转换的合法性。
这样就解决了强制转换的问题,但是又一个问题来了,EchoService接口中的方法是怎么实现的呢?因为我们写的接口是没有实现这个接口的,所以EchoService接口类中定义的方法也必然没有实现,虽然通过动态代理让我们的实现类实现了这个接口,但实际运行的时候还是会报错。
EchoService是这样定义的:
很奇怪的是,这里面的方法名字是写成了 $echo 的形式。
这个方法我们没有实现,那么如何保证在执行的时候能够执行呢?这是因为Dubbo提供了很多Filter,针对这个EchoService提供了一个EchoFilter实现:
可以看到,在方法里,直接判断当前执行的方法是不是$echo,如果是,则直接将参数返回,结束执行过程,否则继续执行后面的Filter。
为了验证一下,我也写了一点代码:
首先,也有一个EchoService接口,与Dubbo的一模一样。
public interface EchoService { Object $echo(Object message); }
然后自己定义了一个MyService接口和它的一个实现类MyServiceImpl。
接口类:
public interface MyService { public void sayHello(); }
实现类:
public class MyServiceImpl implements MyService { @Override public void sayHello() { System.out.println("MyServiceImpl:hello"); } }
我要实现的功能是,将我的MyService实例对象转为EchoService对象 ,并调用其$echo方法。
运行代码如下:
public static void main(String[] args) { //首先定义一个MyService实例 MyService myservice = new MyServiceImpl(); //获取该实例所实现的接口 Class<?>[] interfaces2 = myservice.getClass().getInterfaces(); //将EchoService接口添加到Class<?>数组中 Class<?>[] interfaces = new Class<?>[interfaces2.length + 1]; interfaces[0] = EchoService.class; for (int i = 0; i < interfaces2.length; i++) { interfaces[i + 1] = interfaces2[i]; } //创建代理对象 Object obj = Proxy.newProxyInstance(Thread.currentThread() .getContextClassLoader(), interfaces, new MyInvocationHandler( myservice)); //正常调用 MyService ms = (MyService) obj; ms.sayHello(); //强制转为EchoServic对象并调用方法 EchoService ec = (EchoService) obj; Object data = ec.$echo("aaaaa"); System.out.println(data); }
注意,我实现了一个MyInvocationHandler:这个类是java动态代理必须的。
public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName()); //处理EchoService方法,从而实现回声定位功能 if(method.getName().equals("$echo")){ return args[0]; } return method.invoke(target, args); } }
运行一下,发现没有问题!