Java中如何動(dòng)態(tài)創(chuàng)建接口的實(shí)現(xiàn)方法
有很多應(yīng)用場(chǎng)景,用到了接口動(dòng)態(tài)實(shí)現(xiàn),下面舉幾個(gè)典型的應(yīng)用:
1、mybatis / jpa 等orm框架,可以在接口上加注解進(jìn)行開發(fā),不需要編寫實(shí)現(xiàn)類,運(yùn)行時(shí)動(dòng)態(tài)產(chǎn)生實(shí)現(xiàn)。
2、dubbo等分布式服務(wù)框架,消費(fèi)者只需要引入接口就可以調(diào)用遠(yuǎn)程的實(shí)現(xiàn),分析源代碼,其實(shí)在消費(fèi)端產(chǎn)生了接口的代理實(shí)現(xiàn),再由代理調(diào)用遠(yuǎn)程接口。
3、spring aop 這是最典型的動(dòng)態(tài)代理了。
創(chuàng)建接口的動(dòng)態(tài)實(shí)現(xiàn),有二種最常用的方式:JDK動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理。
代理模式是一種常用的設(shè)計(jì)模式,其目的就是為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)真實(shí)對(duì)象的訪問。
代理類負(fù)責(zé)為委托類預(yù)處理消息,過濾消息并轉(zhuǎn)發(fā)消息,以及進(jìn)行消息被委托類執(zhí)行后的后續(xù)處理。

通過代理層這一中間層,有效的控制對(duì)于真實(shí)委托類對(duì)象的直接訪問,同時(shí)可以實(shí)現(xiàn)自定義的控制策略(spring的AOP機(jī)制),設(shè)計(jì)上獲得更大的靈活性。
下面用JDK動(dòng)態(tài)代理加一點(diǎn)簡(jiǎn)單的代碼來演示這個(gè)過程:
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、運(yùn)行時(shí)調(diào)用接口的方法時(shí)的實(shí)現(xiàn)(這一過程也稱為接口的方法實(shí)現(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 {
//如果傳進(jìn)來是一個(gè)已實(shí)現(xiàn)的具體類(本次演示略過此邏輯)
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
t.printStackTrace();
}
//如果傳進(jìn)來的是一個(gè)接口(核心)
} else {
return run(method, args);
}
return null;
}
/**
* 實(shí)現(xiàn)接口的核心方法
* @param method
* @param args
* @return
*/
public Object run(Method method,Object[] args){
//TODO
//如遠(yuǎn)程http調(diào)用
//如遠(yuǎn)程方法調(diào)用(rmi)
//....
return "method call success!";
}
}
4、測(cè)試
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());
}
}
在這段測(cè)試代碼中,并沒有接口的任何實(shí)現(xiàn),大家猜猜會(huì)是什么結(jié)果?
控制臺(tái)打?。?/p>

說明接口在調(diào)用時(shí),把實(shí)現(xiàn)委托給了代理,最后具體要做的就是這個(gè)代理里面的處理:

在上面這段代碼當(dāng)中,可以看出,拿到了接口的method以及args,那么就可以做很多的事情,如根據(jù)方法名或者配合方法上面的注解來實(shí)現(xiàn)比較豐富的功能。
一個(gè)簡(jiǎn)單的例子只是用來說明這個(gè)原理,下面再舉一個(gè)遠(yuǎn)程接口動(dòng)態(tài)調(diào)用的例子來加深理解。
1、創(chuàng)建代理類和目標(biāo)類需要實(shí)現(xiàn)共同的接口Service
package com.markliu.remote.service;
/**
* Service接口。代理類和被代理類抖需要實(shí)現(xiàn)該接口
*/
public interface Service {
public String getService(String name, int number);
}
2、服務(wù)器端創(chuàng)建RemoteService類,實(shí)現(xiàn)了Service 接口。
package com.markliu.remote.serviceimpl;
import com.markliu.remote.service.Service;
/**
* 服務(wù)器端目標(biāo)業(yè)務(wù)類,被代理對(duì)象
*/
public class RemoteService implements Service {
@Override
public String getService(String name, int number) {
return name + ":" + number;
}
}
3、創(chuàng)建封裝客戶端請(qǐng)求和返回結(jié)果信息的Call類
為了便于按照面向?qū)ο蟮姆绞絹硖幚砜蛻舳伺c服務(wù)器端的通信,可以把它們發(fā)送的信息用 Call 類來表示。一個(gè) Call 對(duì)象表示客戶端發(fā)起的一個(gè)遠(yuǎn)程調(diào)用,它包括調(diào)用的類名或接口名、方法名、方法參數(shù)類型、方法參數(shù)值和方法執(zhí)行結(jié)果。
package com.markliu.local.bean;
import java.io.Serializable;
/**
* 請(qǐng)求的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í)傳入的參數(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)建動(dòng)態(tài)代理模式中實(shí)際的業(yè)務(wù)處理類,實(shí)現(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 {
// 封裝請(qǐng)求信息
Call call = new Call(classType.getName(), method.getName(), method.getParameterTypes(), args);
// 創(chuàng)建鏈接
Connector connector = new Connector();
connector.connect(host, port);
// 發(fā)送請(qǐng)求
connector.sendCall(call);
// 獲取封裝遠(yuǎn)程方法調(diào)用結(jié)果的對(duì)象
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;
/**
* 動(dòng)態(tài)創(chuàng)建RemoteService代理類的工廠
*/
public class RemoteServiceProxyFactory {
public static Object getRemoteServiceProxy(InvocationHandler h) {
Class<?> classType = ((ServiceInvocationHandler) h).getClassType();
// 獲取動(dòng)態(tài)代理類
Object proxy = Proxy.newProxyInstance(classType.getClassLoader(),
new Class[]{classType}, h);
return proxy;
}
}
6、創(chuàng)建底層Socket通信的Connector類,負(fù)責(zé)創(chuàng)建攔截、發(fā)送和接受Call對(duì)象
package com.markliu.local.service;
// 省略import
/**
* 負(fù)責(zé)創(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ā)送請(qǐng)求call對(duì)象
*/
public void sendCall(Call call) throws IOException {
objOut.writeObject(call);
}
/**
* 獲取請(qǐng)求對(duì)象
*/
public Call receive() throws ClassNotFoundException, IOException {
return (Call) objIn.readObject();
}
/**
* 簡(jiǎn)單處理關(guān)閉鏈接
*/
public void close() {
try {
linksocket.close();
objIn.close();
objOut.close();
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7、創(chuàng)建遠(yuǎn)程服務(wù)器
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("遠(yuǎn)程服務(wù)器啟動(dòng)......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);
// 對(duì)象輸入流讀取請(qǐng)求的call對(duì)象
Call call = (Call) objIn.readObject();
System.out.println("客戶端發(fā)送的請(qǐng)求對(duì)象:" + call);
call = getCallResult(call);
// 發(fā)送處理的結(jié)果回客戶端
objOut.writeObject(call);
objIn.close();
in.close();
objOut.close();
out.close();
socket.close();
}
}
/**
* 通過反射機(jī)制調(diào)用call中指定的類的方法,并將返回結(jié)果設(shè)置到原call對(duì)象中
*/
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)用遠(yuǎn)程方法getService的結(jié)果:" + result);
}
}
控制臺(tái)打印結(jié)果:

這個(gè)過程可以簡(jiǎn)單的歸納為:本地接口調(diào)用(客戶端)--->本地接口代理實(shí)現(xiàn)(客戶端)---->遠(yuǎn)程實(shí)現(xiàn)(服務(wù)器端)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java中List<T> Map與Map List<T>的區(qū)別小結(jié)
本文主要介紹了Java中List<T> Map與Map List<T>的區(qū)別小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
java實(shí)現(xiàn)識(shí)別二維碼圖片功能方法詳解與實(shí)例源碼
這篇文章主要介紹了java實(shí)現(xiàn)識(shí)別二維碼圖片,java無法識(shí)別二維碼情況下對(duì)二維碼圖片調(diào)優(yōu)功能方法與實(shí)例源碼,需要的朋友可以參考下2022-12-12
Java IO流學(xué)習(xí)總結(jié)之文件傳輸基礎(chǔ)
這篇文章主要介紹了Java IO流學(xué)習(xí)總結(jié)之文件傳輸基礎(chǔ),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java io流的小伙伴們有很好的幫助,需要的朋友可以參考下2021-04-04
Java堆空間爆滿導(dǎo)致宕機(jī)的問題分析及解決
團(tuán)隊(duì)有一個(gè)服務(wù),一直運(yùn)行的好好的,突然訪問異常了,先是請(qǐng)求超時(shí),然后直接無法訪問,本文將給大家介紹Java堆空間爆滿導(dǎo)致宕機(jī)的問題分析及解決,需要的朋友可以參考下2024-02-02
springboot創(chuàng)建的web項(xiàng)目整合Quartz框架的項(xiàng)目實(shí)踐
本文主要介紹了springboot創(chuàng)建的web項(xiàng)目整合Quartz框架的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06

