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.如何判断实例对象的构造函数是否有参数? 准备工作 首先新建一 ...
随机推荐
- Jquery之JSON的用法
今天讲了Jquery里面JSON的用法,下面是今天讲课给的例子: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" &quo ...
- jQuery的动态绑定事件的应用
注意:bind()的事件绑定是只对当前页面选中的元素有效.如果你想对动态创建的元素bind()事件,是没有办法达到效果的 <script src="jquery-1.11.2.min. ...
- Mditor 发布「桌面版」了 - http://mditor.com
简单说明 Mditor 最早只有「组件版」,随着「桌面版」的发布,Mditor 目前有两个版本: 可嵌入到任意 Web 应用的 Embed 版本,这是一桌面版的基础,Repo: https://git ...
- 【js数据结构】栈解决佩兹糖果盒问题
现实生活中栈的一个例子是佩兹糖果盒. 想象一下你有一盒佩兹糖果, 里面塞满了红色. 黄色和白色的糖果, 但是你不喜欢黄色的糖果. 使用栈( 有可能用到多个栈) 写一段程序, 在不改变盒内其他糖果叠放顺 ...
- Linux supervisord配置使用
supervisor官方网站 http://supervisord.org 1.安装supervisord Ubuntu: $sudo apt-get install python-setuptool ...
- Spring-boot中使用@ConditionalOnExpression注解,在特定情况下初始化bean
想要实现的功能: 我想在配置文件中设置一个开关,enabled,在开关为true的时候才实例化bean,进行相关业务逻辑的操作. 具体实现: 1:要实例化的bean 2. 配置类 代码: 想要实例化的 ...
- NDK(三方库引入、Mk文件)
NDK笔记-----第三方库引入 一.字符操作: 1 二.NDK*(JNI)对象操作: 2 1.C++调用java对象 3 三.Android.mk说明: 3 四.Application.mk说明 3 ...
- Struts2之OGNL表达式
OGNL(Object-Graph Navigation Language的简称),对象图导航语言,它是一门表达式语言,除了用来设置和获取Java对象的属性之外,另外提供诸如集合的投影和过滤以及lam ...
- 【转】如何成为一位优秀的创业CEO
编者按:本文来自 Ryan Allis,是一位来自旧金山的创业者和投资人.在 2003 年创立了 iContact,并任 CEO. 做创业公司的 CEO 可以说是世界上最有挑战性的事情之一.你得让客户 ...
- JavaScript 方法调用模式和函数调用模式
这两天在读<JavaScript语言精粹>关于第4章函数调用的几种模式琢磨了半天. 这里就说一下方法调用模式跟函数调用模式. 方法调用模式: 当一个函数被保存为对象的一个属性时,我们称它为 ...