软件编程
位置:首页>> 软件编程>> java编程>> Java中如何动态创建接口的实现方法

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

作者:虾米&老黄牛  发布时间:2023-11-25 15:13:02 

标签:java,动态,接口

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

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

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

来源:http://www.cnblogs.com/clonen/p/6735011.html

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com