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

 更新时间:2017年09月20日 15:20:28   作者:虾米&老黄牛  
这篇文章主要介绍了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实现贪吃蛇大作战小游戏(附源码)

    今天给大家带来的是小项目是 基于Java+Swing+IO流实现 的贪吃蛇大作战小游戏。实现了界面可视化、基本的吃食物功能、死亡功能、移动功能、积分功能,并额外实现了主动加速和鼓励机制,需要的可以参考一下
    2022-07-07
  • Java获取客户端真实IP地址过程解析

    Java获取客户端真实IP地址过程解析

    这篇文章主要介绍了Java获取客户端真实IP地址过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Java使用Collections.sort()排序的示例详解

    Java使用Collections.sort()排序的示例详解

    这篇文章主要介绍了Java使用Collections.sort()排序的示例详解,Collections.sort(list, new PriceComparator());的第二个参数返回一个int型的值,就相当于一个标志,告诉sort方法按什么顺序来对list进行排序。对此感兴趣的可以了解一下
    2020-07-07
  • spring kafka @KafkaListener详解与使用过程

    spring kafka @KafkaListener详解与使用过程

    这篇文章主要介绍了spring-kafka @KafkaListener详解与使用,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • SpringBoot属性注入的多种方式实例

    SpringBoot属性注入的多种方式实例

    在 SpringBoot中,提供了一种新的属性注入方式,支持各种java基本数据类型及复杂类型的注入,下面这篇文章主要给大家介绍了关于SpringBoot属性注入的多种方式,需要的朋友可以参考下
    2021-10-10
  • Java资源缓存 之 LruCache

    Java资源缓存 之 LruCache

    LruCache (此类在android-support-v4的包中提供) 这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
    2016-08-08
  • spring boot org.junit.jupiter.api不存在的解决

    spring boot org.junit.jupiter.api不存在的解决

    这篇文章主要介绍了spring boot org.junit.jupiter.api不存在的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 详解Java token主流框架之JWT

    详解Java token主流框架之JWT

    JWT(JSON Web Token)是一种基于JSON格式的轻量级的、用于身份认证的开放标准,它通过在用户和服务器之间传递一个安全的、可靠的、独立的JSON对象来进行身份验证和授权,本文将详细给大家介绍Java token主流框架之JWT,需要的朋友可以参考下
    2023-05-05
  • 完美解决docx4j变量替换的问题

    完美解决docx4j变量替换的问题

    这篇文章主要介绍了完美解决docx4j变量替换的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Java 限制前端重复请求的实例代码

    Java 限制前端重复请求的实例代码

    这篇文章主要介绍了Java 限制前端重复请求,文中给大家提到了JAVA利用自定义本地锁解决重复提交的问题,通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-08-08

最新评论