有很多應用場景,用到了接口動態實現,下面舉幾個典型的應用:
1、mybatis / jpa 等orm框架,可以在接口上加注解進行開發,不需要編寫實現類,運行時動態產生實現。
2、dubbo等分布式服務框架,消費者只需要引入接口就可以調用遠程的實現,分析源代碼,其實在消費端產生了接口的代理實現,再由代理調用遠程接口。
3、spring aop 這是最典型的動態代理了。
創建接口的動態實現,有二種最常用的方式:JDK動態代理和CGLIB動態代理。
代理模式是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個真實對象的訪問。
代理類負責為委托類預處理消息,過濾消息並轉發消息,以及進行消息被委托類執行后的后續處理。
通過代理層這一中間層,有效的控制對於真實委托類對象的直接訪問,同時可以實現自定義的控制策略(spring的AOP機制),設計上獲得更大的靈活性。
下面用JDK動態代理加一點簡單的代碼來演示這個過程:
1、接口
package com.yhouse.modules.daos; public interface IUserDao { public String getUserName; }
2、創建代理
package
com.yhouse.modules.daos; import java.lang.reflect.Proxy; /** * 創建代理 *
@author clonen.cheng * */ public class Invoker { public Object
getInstance(Class<?> cls){ MethodProxy invocationHandler = new
MethodProxy; Object newProxyInstance = Proxy.newProxyInstance(
cls.getClassLoader, new Class { cls }, invocationHandler); return
(Object)newProxyInstance; } }
3、運行時調用接口的方法時的實現(這一過程也稱為接口的方法實現)
package
com.yhouse.modules.daos; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class MethodProxy implements
InvocationHandler { @Override public Object invoke(Object proxy, Method
method, Object[] args) throws Throwable { //如果傳進來是一個已實現的具體類(本次演示略過此邏輯)
if (Object.class.equals(method.getDeclaringClass)) { try { return
method.invoke(this, args); } catch (Throwable t) { t.printStackTrace; }
//如果傳進來的是一個接口(核心) } else { return run(method, args); } return null; }
/** * 實現接口的核心方法 * @param method * @param args * @return */ public Object
run(Method method,Object[] args){ //TODO //如遠程http調用 //如遠程方法調用(rmi)
//.... return "method call success!"; } }
4、測試
package
com.yhouse.modules.daos; public class ProxyTest { public static void
main(String[] args) { IUserDao invoker=(IUserDao)new
Invoker.getInstance(IUserDao.class);
System.out.println(invoker.getUserName); } }
在這段測試代碼中,並沒有接口的任何實現,大家猜猜會是什么結果?控制台打印:
說明接口在調用時,把實現委托給了代理,最后具體要做的就是這個代理里面的處理:
在上面這段代碼當中,可以看出,拿到了接口的method以及args,那么就可以做很多的事情,如根據方法名或者配合方法上面的注解來實現比較豐富的功能。
一個簡單的例子只是用來說明這個原理,下面再舉一個遠程接口動態調用的例子來加深理解。
1、創建代理類和目標類需要實現共同的接口Service
package
com.markliu.remote.service; /** * Service接口。代理類和被代理類抖需要實現該接口 */ public
interface Service { public String getService(String name, int number); }
2、服務器端創建RemoteService類,實現了Service 接口。
package
com.markliu.remote.serviceimpl; import
com.markliu.remote.service.Service; /** * 服務器端目標業務類,被代理對象 */ public
class RemoteService implements Service { @Override public String
getService(String name, int number) { return name + ":" + number; } }
3、創建封裝客戶端請求和返回結果信息的Call類
為了便於按照面向對象的方式來處理客戶端與服務器端的通信,可以把它們發送的信息用 Call 類來表示。一個 Call 對象表示客戶端發起的一個遠程調用,它包括調用的類名或接口名、方法名、方法參數類型、方法參數值和方法執行結果。
package
com.markliu.local.bean; import java.io.Serializable; /** * 請求的javabean
*/ public class Call implements Serializable{ private static final long
serialVersionUID = 5386052199960133937L; private String className; //
調用的類名或接口名 private String methodName; // 調用的方法名 private Class<?>
paramTypes; // 方法參數類型 private Object params; // 調用方法時傳入的參數值 /** *
表示方法的執行結果 如果方法正常執行,則 result 為方法返回值, * 如果方法拋出異常,那么 result 為該異常。 */
private Object result; public Call {} public Call(String className,
String methodName, Class<?>[] paramTypes, Object[] params) {
this.className = className; this.methodName = methodName;
this.paramTypes = paramTypes; this.params = params; } // 省略了get和set方法 }
4、創建動態代理模式中實際的業務處理類,實現了InvocationHandler 接口
package
com.markliu.local.service; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import com.markliu.local.bean.Call;
public class ServiceInvocationHandler implements InvocationHandler {
private Class<?> classType; private String host; private Integer
port; public Class<?> getClassType { return classType; } public
ServiceInvocationHandler(Class<?> classType, String host, Integer
port) { this.classType = classType; this.host = host; this.port = port; }
@Override public Object invoke(Object proxy, Method method, Object[]
args) throws Throwable { // 封裝請求信息 Call call = new
Call(classType.getName, method.getName, method.getParameterTypes, args);
// 創建鏈接 Connector connector = new Connector; connector.connect(host,
port); // 發送請求 connector.sendCall(call); // 獲取封裝遠程方法調用結果的對象
connector.close; Object returnResult = call.getResult; return
returnResult; } }
5、創建獲取代理類的工廠RemoteServiceProxyFactory
package
com.markliu.local.service; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; /** * 動態創建RemoteService代理類的工廠 */ public
class RemoteServiceProxyFactory { public static Object
getRemoteServiceProxy(InvocationHandler h) { Class<?> classType =
((ServiceInvocationHandler) h).getClassType; // 獲取動態代理類 Object proxy =
Proxy.newProxyInstance(classType.getClassLoader, new Class{classType},
h); return proxy; } }
6、創建底層Socket通信的Connector類,負責創建攔截、發送和接受Call對象
package
com.markliu.local.service; // 省略import /** * 負責創建鏈接 */ public class
Connector { private Socket linksocket; private InputStream in; private
ObjectInputStream objIn; private OutputStream out; private
ObjectOutputStream objOut; public Connector{} /** * 創建鏈接 */ public void
connect(String host, Integer port) throws UnknownHostException,
IOException { linksocket = new Socket(host, port); in =
linksocket.getInputStream; out = linksocket.getOutputStream; objOut =
new ObjectOutputStream(out); objIn = new ObjectInputStream(in); } /** *
發送請求call對象 */ public void sendCall(Call call) throws IOException {
objOut.writeObject(call); } /** * 獲取請求對象 */ public Call receive throws
ClassNotFoundException, IOException { return (Call) objIn.readObject; }
/** * 簡單處理關閉鏈接 */ public void close { try { linksocket.close;
objIn.close; objOut.close; in.close; out.close; } catch (IOException e) {
e.printStackTrace; } } }
7、創建遠程服務器
package
com.markliu.remote.main; // 省略import public class RemoteServer {
private Service remoteService; public RemoteServer { remoteService = new
RemoteService; } public static void main(String[] args) throws
Exception { RemoteServer server = new RemoteServer;
System.out.println("遠程服務器啟動......DONE!"); server.service; } public void
service throws Exception { @SuppressWarnings("resource") ServerSocket
serverSocket = new ServerSocket(8001); while (true) { Socket socket =
serverSocket.accept; InputStream in = socket.getInputStream;
ObjectInputStream objIn = new ObjectInputStream(in); OutputStream out =
socket.getOutputStream; ObjectOutputStream objOut = new
ObjectOutputStream(out); // 對象輸入流讀取請求的call對象 Call call = (Call)
objIn.readObject; System.out.println("客戶端發送的請求對象:" + call); call =
getCallResult(call); // 發送處理的結果回客戶端 objOut.writeObject(call);
objIn.close; in.close; objOut.close; out.close; socket.close; } } /** *
通過反射機制調用call中指定的類的方法,並將返回結果設置到原call對象中 */ private Call
getCallResult(Call call) throws Exception { String className =
call.getClassName; String methodName = call.getMethodName; Object params
= call.getParams; Class<?> paramsTypes = call.getParamTypes;
Class<?> classType = Class.forName(className); // 獲取所要調用的方法 Method
method = classType.getMethod(methodName, paramsTypes); Object result =
method.invoke(remoteService, params); call.setResult(result); return
call; } }
8、創建本地客戶端
package
com.markliu.local.main; import java.lang.reflect.InvocationHandler;
import com.markliu.local.service.RemoteServiceProxyFactory; import
com.markliu.local.service.ServiceInvocationHandler; import
com.markliu.remote.service.Service; public class LocalClient { public
static void main(String[] args) { String host = "127.0.0.1"; Integer
port = 8001; Class<?> classType =
com.markliu.remote.service.Service.class; InvocationHandler h = new
ServiceInvocationHandler(classType, host, port); Service serviceProxy =
(Service) RemoteServiceProxyFactory.getRemoteServiceProxy(h); String
result = serviceProxy.getService("SunnyMarkLiu", 22);
System.out.println("調用遠程方法getService的結果:" + result); } }
控制台打印結果:
這個過程可以簡單的歸納為:本地接口調用(客戶端)--->本地接口代理實現(客戶端)---->遠程實現(服務器端)
