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中如何动态创建接口的实现方法 的全部内容, 来源链接: utcz.com/p/214585.html

回到顶部