Java中如何動態(tài)創(chuàng)建接口的實現(xiàn)方法
有很多應用場景,用到了接口動態(tài)實現(xiàn),下面舉幾個典型的應用:
1、mybatis / jpa 等orm框架,可以在接口上加注解進行開發(fā),不需要編寫實現(xiàn)類,運行時動態(tài)產(chǎn)生實現(xiàn)。
2、dubbo等分布式服務框架,消費者只需要引入接口就可以調(diào)用遠程的實現(xiàn),分析源代碼,其實在消費端產(chǎn)生了接口的代理實現(xiàn),再由代理調(diào)用遠程接口。
3、spring aop 這是最典型的動態(tài)代理了。
創(chuàng)建接口的動態(tài)實現(xiàn),有二種最常用的方式:JDK動態(tài)代理和CGLIB動態(tài)代理。
代理模式是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個真實對象的訪問。
代理類負責為委托類預處理消息,過濾消息并轉(zhuǎn)發(fā)消息,以及進行消息被委托類執(zhí)行后的后續(xù)處理。
通過代理層這一中間層,有效的控制對于真實委托類對象的直接訪問,同時可以實現(xiàn)自定義的控制策略(spring的AOP機制),設計上獲得更大的靈活性。
下面用JDK動態(tài)代理加一點簡單的代碼來演示這個過程:
1、接口
package com.yhouse.modules.daos; public interface IUserDao { public String getUserName(); }
2、創(chuàng)建代理
package com.yhouse.modules.daos; import java.lang.reflect.Proxy; /** * 創(chuàng)建代理 * @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、運行時調(diào)用接口的方法時的實現(xiàn)(這一過程也稱為接口的方法實現(xiàn))
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 { //如果傳進來是一個已實現(xiàn)的具體類(本次演示略過此邏輯) if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { t.printStackTrace(); } //如果傳進來的是一個接口(核心) } else { return run(method, args); } return null; } /** * 實現(xiàn)接口的核心方法 * @param method * @param args * @return */ public Object run(Method method,Object[] args){ //TODO //如遠程http調(diào)用 //如遠程方法調(diào)用(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()); } }
在這段測試代碼中,并沒有接口的任何實現(xiàn),大家猜猜會是什么結(jié)果?
控制臺打印:
說明接口在調(diào)用時,把實現(xiàn)委托給了代理,最后具體要做的就是這個代理里面的處理:
在上面這段代碼當中,可以看出,拿到了接口的method以及args,那么就可以做很多的事情,如根據(jù)方法名或者配合方法上面的注解來實現(xiàn)比較豐富的功能。
一個簡單的例子只是用來說明這個原理,下面再舉一個遠程接口動態(tài)調(diào)用的例子來加深理解。
1、創(chuàng)建代理類和目標類需要實現(xiàn)共同的接口Service
package com.markliu.remote.service; /** * Service接口。代理類和被代理類抖需要實現(xiàn)該接口 */ public interface Service { public String getService(String name, int number); }
2、服務器端創(chuàng)建RemoteService類,實現(xiàn)了Service 接口。
package com.markliu.remote.serviceimpl; import com.markliu.remote.service.Service; /** * 服務器端目標業(yè)務類,被代理對象 */ public class RemoteService implements Service { @Override public String getService(String name, int number) { return name + ":" + number; } }
3、創(chuàng)建封裝客戶端請求和返回結(jié)果信息的Call類
為了便于按照面向?qū)ο蟮姆绞絹硖幚砜蛻舳伺c服務器端的通信,可以把它們發(fā)送的信息用 Call 類來表示。一個 Call 對象表示客戶端發(fā)起的一個遠程調(diào)用,它包括調(diào)用的類名或接口名、方法名、方法參數(shù)類型、方法參數(shù)值和方法執(zhí)行結(jié)果。
package com.markliu.local.bean; import java.io.Serializable; /** * 請求的javabean */ public class Call implements Serializable{ private static final long serialVersionUID = 5386052199960133937L; private String className; // 調(diào)用的類名或接口名 private String methodName; // 調(diào)用的方法名 private Class<?>[] paramTypes; // 方法參數(shù)類型 private Object[] params; // 調(diào)用方法時傳入的參數(shù)值 /** * 表示方法的執(zhí)行結(jié)果 如果方法正常執(zhí)行,則 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、創(chuàng)建動態(tài)代理模式中實際的業(yè)務處理類,實現(xiàn)了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); // 創(chuàng)建鏈接 Connector connector = new Connector(); connector.connect(host, port); // 發(fā)送請求 connector.sendCall(call); // 獲取封裝遠程方法調(diào)用結(jié)果的對象 connector.close(); Object returnResult = call.getResult(); return returnResult; } }
5、創(chuàng)建獲取代理類的工廠RemoteServiceProxyFactory
package com.markliu.local.service; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * 動態(tài)創(chuàng)建RemoteService代理類的工廠 */ public class RemoteServiceProxyFactory { public static Object getRemoteServiceProxy(InvocationHandler h) { Class<?> classType = ((ServiceInvocationHandler) h).getClassType(); // 獲取動態(tài)代理類 Object proxy = Proxy.newProxyInstance(classType.getClassLoader(), new Class[]{classType}, h); return proxy; } }
6、創(chuàng)建底層Socket通信的Connector類,負責創(chuàng)建攔截、發(fā)送和接受Call對象
package com.markliu.local.service; // 省略import /** * 負責創(chuàng)建鏈接 */ public class Connector { private Socket linksocket; private InputStream in; private ObjectInputStream objIn; private OutputStream out; private ObjectOutputStream objOut; public Connector(){} /** * 創(chuàng)建鏈接 */ 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); } /** * 發(fā)送請求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、創(chuàng)建遠程服務器
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("客戶端發(fā)送的請求對象:" + call); call = getCallResult(call); // 發(fā)送處理的結(jié)果回客戶端 objOut.writeObject(call); objIn.close(); in.close(); objOut.close(); out.close(); socket.close(); } } /** * 通過反射機制調(diào)用call中指定的類的方法,并將返回結(jié)果設置到原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); // 獲取所要調(diào)用的方法 Method method = classType.getMethod(methodName, paramsTypes); Object result = method.invoke(remoteService, params); call.setResult(result); return call; } }
8、創(chuàng)建本地客戶端
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("調(diào)用遠程方法getService的結(jié)果:" + result); } }
控制臺打印結(jié)果:
這個過程可以簡單的歸納為:本地接口調(diào)用(客戶端)--->本地接口代理實現(xiàn)(客戶端)---->遠程實現(xiàn)(服務器端)
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
詳解Java弱引用(WeakReference)的理解與使用
這篇文章主要介紹了Java弱引用(WeakReference)的理解與使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-04-04Java單線程程序?qū)崿F(xiàn)實現(xiàn)簡單聊天功能
這篇文章主要介紹了Java單線程程序?qū)崿F(xiàn)實現(xiàn)簡單聊天功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-10-10SpringBoot中時間類型 序列化、反序列化、格式處理示例代碼
這篇文章主要介紹了SpringBoot中時間類型 序列化、反序列化、格式處理示例代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08Java實現(xiàn)Twitter的分布式自增ID算法snowflake
這篇文章主要介紹了Java實現(xiàn)Twitter的分布式自增ID算法snowflake,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08