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中如何动态创建接口的实现的更多相关文章
- Java中Comparable和Comparator接口区别分析
Java中Comparable和Comparator接口区别分析 来源:码农网 | 时间:2015-03-16 10:25:20 | 阅读数:8902 [导读] 本文要来详细分析一下Java中Comp ...
- java中的动态代理机制
java中的动态代理机制 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现 ...
- Java 反射 Array动态创建数组
Java 反射 Array动态创建数组 @author ixenos 注:java.lang.reflect.Array 是个反射工具包,全是静态方法,创建数组以多维数组为基准,一维数组只是特殊实现 ...
- 十分钟理解Java中的动态代理
十分钟理解 Java 中的动态代理 一.概述 1. 什么是代理 我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品.关于微商代理,首先我们从他们那里买东西时通常不知道 ...
- 深度剖析java中JDK动态代理机制
https://www.jb51.net/article/110342.htm 本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定 ...
- 一文读懂Java中的动态代理
从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...
- 使用Java中的动态代理实现数据库连接池
2002 年 12 月 05 日 作者通过使用JAVA中的动态代理实现数据库连接池,使使用者可以以普通的jdbc连接的使用习惯来使用连接池. 数据库连接池在编写应用服务是经常需要用到的模块,太过频繁的 ...
- Java开发知识之Java中的集合Set接口以及子类应用
---恢复内容开始--- Java开发知识之Java中的集合Set接口以及子类应用 一丶Set接口以及作用 在上一讲.我们熟悉了接口的实现图.以及自己各有的子类. List接口主要存储的数据是可以重复 ...
- C#动态创建接口的实现实例对象
本文简单介绍如何动态创建接口interface的实现实例对象,包含两个知识点: 1.如何获取接口interface的所有实现实例对象? 2.如何判断实例对象的构造函数是否有参数? 准备工作 首先新建一 ...
随机推荐
- Troubleshooting OpenStack 瘫痪 - 每天5分钟玩转 OpenStack(160)
这是 OpenStack 实施经验分享系列的第 10 篇.是软件就会有 bug,OpenStack 也不例外,只要用它就一定会遇到故障.Troubleshooting(故障排除)是运维 OpenSta ...
- 【JS】JavaScript中的执行环境与作用域
JavaScript中的执行环境定义了变量或函数有权访问的数据(每个函数都有自己的执行环境),全局执行环境是最外围的执行环境,在浏览器中,全局执行环境就是window对象,所以所有的全局变量和函数都是 ...
- Docker存储驱动之ZFS简介
ZFS是下一代的文件系统,支持了很多存储高级特性,如卷管理.快照.和校验.压缩和重复删除技术.拷贝等. ZFS由Sun公司创建,现属于Oracle,ZFS是开源的,并基于CDDL license.因为 ...
- Gradle之恋-命令行详解
虽然很多童鞋都必须要用IDE,但有些命令是IDE很难表现出来的,还是命令行返璞归真.本文就带你来剖析Gradle的命令行. 本文不包括试验性(incubating)的选项,如--configure-o ...
- 取出关联数组的key值和values值
取出关联数组的key值,可用 array_keys()取出; <?php $a=array("Volvo"=>"XC90","BMW&qu ...
- linux 私房菜 CH5 笔记
知识点 linux 大小写敏感 接口的切换 [Ctrl] + [Alt] + [F1] ~ [F6] :文字接口登入 tty1 ~ tty6 终端机: [Ctrl] + [Alt] + [F7] :图 ...
- 20144306《网络对抗》MAL_逆向与Bof基础
实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时包含另一个代码片段,getShe ...
- 【Android】TextView文字长度测量和各种Paddding解析
老规矩,先上张图 o,这篇好像是分析篇,没有效果图.不管了,位置占着,老规矩不能坏,下面开始正文. *** 这篇博客会讲得比较杂: TextView里各部分的大小该怎么测量? 如何计算每行文字的长度? ...
- 时间相关库<ctime>解析
原创作品,转载请注明来源:http://www.cnblogs.com/shrimp-can/p/5649487.html 一.定义的类型 1.clock_t:时钟类型 2.size_t:unsign ...
- 隐式的处理SOAPHeader消息
先用一下比较基础的隐式方式处理我的SOAPHeader消息,注意的是QName的使用,代码如下: public static void main(String[] args) { try { //创建 ...