這篇文章主要介紹了Java中如何動態創建接口的實現方法的相關資料,需要的朋友可以參考下
有很多應用場景,用到了接口動態實現,下面舉幾個典型的應用:
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); } }
控制台列印結果:
這個過程可以簡單的歸納為:本機介面呼叫(客戶端)--->本機介面代理實作(客戶端) ---->遠端實作(伺服器端)
以上是Java中動態建立介面的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!