Java中如何动态创建接口的实现

时间:2023-02-04 23:19:30

有很多应用场景,用到了接口动态实现,下面举几个典型的应用:

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中如何动态创建接口的实现

说明接口在调用时,把实现委托给了代理,最后具体要做的就是这个代理里面的处理:

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中如何动态创建接口的实现

这个过程可以简单的归纳为:本地接口调用(客户端)--->本地接口代理实现(客户端)---->远程实现(服务器端)

Java中如何动态创建接口的实现的更多相关文章

  1. Java中Comparable和Comparator接口区别分析

    Java中Comparable和Comparator接口区别分析 来源:码农网 | 时间:2015-03-16 10:25:20 | 阅读数:8902 [导读] 本文要来详细分析一下Java中Comp ...

  2. java中的动态代理机制

    java中的动态代理机制 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现 ...

  3. Java 反射 Array动态创建数组

    Java 反射 Array动态创建数组 @author ixenos 注:java.lang.reflect.Array 是个反射工具包,全是静态方法,创建数组以多维数组为基准,一维数组只是特殊实现 ...

  4. 十分钟理解Java中的动态代理

    十分钟理解 Java 中的动态代理   一.概述 1. 什么是代理 我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品.关于微商代理,首先我们从他们那里买东西时通常不知道 ...

  5. 深度剖析java中JDK动态代理机制

    https://www.jb51.net/article/110342.htm 本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定 ...

  6. 一文读懂Java中的动态代理

    从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...

  7. 使用Java中的动态代理实现数据库连接池

    2002 年 12 月 05 日 作者通过使用JAVA中的动态代理实现数据库连接池,使使用者可以以普通的jdbc连接的使用习惯来使用连接池. 数据库连接池在编写应用服务是经常需要用到的模块,太过频繁的 ...

  8. Java开发知识之Java中的集合Set接口以及子类应用

    ---恢复内容开始--- Java开发知识之Java中的集合Set接口以及子类应用 一丶Set接口以及作用 在上一讲.我们熟悉了接口的实现图.以及自己各有的子类. List接口主要存储的数据是可以重复 ...

  9. C&num;动态创建接口的实现实例对象

    本文简单介绍如何动态创建接口interface的实现实例对象,包含两个知识点: 1.如何获取接口interface的所有实现实例对象? 2.如何判断实例对象的构造函数是否有参数? 准备工作 首先新建一 ...

随机推荐

  1. DataGridView

    一.实现CheckBox列. 1.1 增加CheckBox列: 在DataGridView中增加CheckBox列: 注意:设置ColumnType类型和设置FalseValue为0,TrueValu ...

  2. 目前流行的源程序版本管理软件和项目管理软件都有哪些?各有什么优缺点?以及Github的注册过程

    一.目前流行的源程序版本管理软件和项目管理软件有Microsoft TFS.GitHub.SVN.Coding等. 二.各有什么优缺点 三.在GitHub注册账户的过程 Microsoft TFS的优 ...

  3. JS中call、apply、bind使用指南,带部分原理。

    为什么需要这些?主要是因为this,来看看this干的好事. box.onclick = function(){ function fn(){ alert(this); } fn();}; 我们原本以 ...

  4. Mac os壁纸提取

    想必用过Mac os系统的朋友都知道mac壁纸,提取出来用作壁纸是十分好看的 下面给出壁纸的位置,自己用文件管理器打开然后复制出来即可! 系统盘:/Library/DesktopPictures

  5. POJ3415 Common Substrings&lpar;后缀数组 单调栈&rpar;

    借用罗穗骞论文中的讲解: 计算A 的所有后缀和B 的所有后缀之间的最长公共前缀的长度,把最长公共前缀长度不小于k 的部分全部加起来.先将两个字符串连起来,中间用一个没有出现过的字符隔开.按height ...

  6. 认识到了x64程序的必要性

    假如我做一个程序,在运行过程中需要使用一个Map,然而这个Map存储了超多信息的话,系统内存不够就会崩溃了.以前的解决方案可能是把内容存储在一个文件/数据库里,但是有内存岂不是更方便.更直截了当!

  7. 将Xml文件递归加载到TreeView中

    #region [通过XDocument的方式将Xml文件递归到TreeView控件中] //读取Xml文件(XDocument) //1.加载Xml文件 XDocument  document=XD ...

  8. Python序列化操作与反序列操作

    一.概念 序列化:转向一个字符串数据类型序列:字符串 二.需要做序列化操作的情况1.数据存储2.网络上数据传输 从数据类型到字符串的过程叫序列化从字符串到数据类型的过程叫反序列化 三.现有序列化模块1 ...

  9. SQLSERVER 免费对比数据库结构和数据的工具支持&colon;SQL Server 2012&comma; SQL Server 2008 and SQL Server 2005

    New xSQL Schema Compare - version 5 Compare the schemas of two SQL Server databases, review differen ...

  10. linux 常用清理或备份文件命令

    find /data/tmp/confluence/backups/ -type f -mtime +7 -exec rm -f {} \; ##查找创建超过7天的文件并强制删除 cp /data/a ...