1.SOFA RPC源码解析
1.1 RPC代理机制
在SOFA RPC中,服务引用采用代理模式把本地方法调用转换为远程服务调用,从而使开发者像使用本地Java方法一样,使用远程服务,屏蔽了底层的网络通讯细节,使开发人员把精力集中在业务开发中。
简单回顾一下代理模式,以便大家理解SOFA RPC中代理机制的实现方式。
代理模式是常用的设计模式之一。当无法直接访问某个对象或访问某个对象存在困难时,可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口。
按照代理的创建时期,代理类可以分为两种:
1. 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了;
2. 动态代理:在程序运行时,运用反射机制动态创建而成。
在SOFABoot中,对于JVM服务类型的服务引用,即可以针对某个接口创建代理对象,也可以针对某个类创建代理对象。
在SOFA RPC中,规定只能针对某个接口创建代理对象,所以采用动态代理方式为远程服务创建本地代理对象。如果被代理的对象为Java类,而不是接口,则在创建代理对象时,抛出异常,例如:
1. The value of config consumer.interface[com.alipay.sofa.boot.examples.demo.rpc.bean.PersonServiceImpl2] is illegal,interfaceId must set interface class, not implement class
在SOFA RPC中,主要有两种创建代理的方式:JDK动态代理机制和Javassist动态类库。
在此补充说明一下,为了查看Proxy动态生成的代理对象的源文件,必须先导出字节码文件,然后通过反编译工具(如:jad)生成源文件。对于Proxy,导出字节码文件方式为在应用代码开始位置设置sun.misc.ProxyGenerator.saveGeneratedFiles为true:
1. @SpringBootApplication
2. @ImportResource({"classpath*:rpc-starter-example.xml" })
3. public class SofaBootRpcDemoApplication {
4.
5. public static void main(String[] args)throws InterruptedException {
6.
7. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
8.
9. ConfigurableApplicationContext ac =SpringApplication.run(SofaBootRpcDemoApplication.class, args);
10.
11.
12. }
13. }
注意,需要在工程根目录下,增加 com/sun/proxy目录,否则会报错如下:
1. Exception in thread "main"java.lang.InternalError: I/O exception saving generatedfile:java.io.FileNotFoundException : com\sun\proxy\$Proxy0.class (系统找不到指定的路径。)
2. atsun.misc.ProxyGenerator$1.run(ProxyGenerator.java:336 )
3. atsun.misc.ProxyGenerator$1.run(ProxyGenerator.java:327 )
4. at java.security.AccessController.doPrivileged( NativeMethod)
5. atsun.misc.ProxyGenerator.generateProxyClass( ProxyGenerator.java:326)
6. atjava.lang.reflect.Proxy$ProxyClassFactory.apply( Proxy.java:672)
7. atjava.lang.reflect.Proxy$ProxyClassFactory.apply( Proxy.java:592)
8. atjava.lang.reflect.WeakCache$Factory.get( WeakCache.java:244)
9. atjava.lang.reflect.WeakCache.get(WeakCache.java:141 )
10. atjava.lang.reflect.Proxy.getProxyClass0(Proxy.java:455 )
11. atjava.lang.reflect.Proxy.newProxyInstance( Proxy.java:738)
1.1.1 JDK动态代理
提到JDK动态代理,大家一定想到java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
1. 通过实现InvocationHandler接口,为代理对象提供调用处理器handler。当调用代理对象某个方法时,该方法调用将会被分发到handler的invoke方法,由其在调用目标对象的方法前后,进行一些额外处理。
2. 通过Proxy类newProxyInstance方法,为实现某个接口的类创建代理对象。
首先,看一下JDKProxy类的源码,该类对外提供了为指定接口动态创建代理对象的方法:
1. @Extension("jdk")
2. public class JDKProxy implements Proxy {
3.
4. @Override
5. public <T> T getProxy(Class<T>interfaceClass, Invoker proxyInvoker) {
6. InvocationHandler handler = new JDKInvocationHandler(interfaceClass,proxyInvoker);
7. ClassLoader classLoader =ClassLoaderUtils.getCurrentClassLoader();
8. T result = (T)java.lang.reflect.Proxy.newProxyInstance(classLoader,
9. new Class[] { interfaceClass },handler);
10. return result;
11. }
12.
13. @Override
14. public Invoker getInvoker(ObjectproxyObject) {
15. return parseInvoker(proxyObject);
16. }
17.
18. /**
19. * Parse proxy invoker from proxy object
20. *
21. * @param proxyObject Proxy object
22. * @return proxy invoker
23. */
24. public static Invoker parseInvoker(ObjectproxyObject) {
25. InvocationHandler handler =java.lang.reflect.Proxy.getInvocationHandler(proxyObject);
26. if (handler instanceofJDKInvocationHandler) {
27. return ((JDKInvocationHandler)handler).getProxyInvoker();
28. }
29. return null;
30. }
31. }
getProxy方法主要处理逻辑如下:
1. 根据接口类型和Invoker接口的实现类,创建JDKInvocationHandler实例;
2. 调用Proxy类newProxyInstance方法,创建代理对象;
在SOFA RPC中,com.alipay.sofa.rpc.proxy.jdk.JDKInvocationHandler类实现了InvocationHandler接口。
JDKInvocationHandler类的源代码如下:
1. public class JDKInvocationHandlerimplements InvocationHandler {
2.
3. /**
4. * 代理类
5. */
6. private Class proxyClass;
7.
8. /**
9. * 代理调用器
10. */
11. private Invoker proxyInvoker;
12.
13. /**
14. * Instantiates a new Jdk invocationhandler.
1