Java中如何動態創建接口的實現


有很多應用場景,用到了接口動態實現,下面舉幾個典型的應用:

1、mybatis / jpa 等orm框架,可以在接口上加注解進行開發,不需要編寫實現類,運行時動態產生實現。

2、dubbo等分布式服務框架,消費者只需要引入接口就可以調用遠程的實現,分析源代碼,其實在消費端產生了接口的代理實現,再由代理調用遠程接口。

3、spring aop 這是最典型的動態代理了。

創建接口的動態實現,有二種最常用的方式:JDK動態代理和CGLIB動態代理。

代理模式是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個真實對象的訪問。

代理類負責為委托類預處理消息,過濾消息並轉發消息,以及進行消息被委托類執行后的后續處理。

Java中如何動態創建接口的實現

通過代理層這一中間層,有效的控制對於真實委托類對象的直接訪問,同時可以實現自定義的控制策略(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); } }

在這段測試代碼中,並沒有接口的任何實現,大家猜猜會是什么結果?控制台打印:

說明接口在調用時,把實現委托給了代理,最后具體要做的就是這個代理里面的處理:

Java中如何動態創建接口的實現

在上面這段代碼當中,可以看出,拿到了接口的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); } }

控制台打印結果:

Java中如何動態創建接口的實現

這個過程可以簡單的歸納為:本地接口調用(客戶端)--->本地接口代理實現(客戶端)---->遠程實現(服務器端)

  轉自https://blog.csdn.net/u013322876/article/details/70339518


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM