zoukankan      html  css  js  c++  java
  • 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中如何动态创建接口的实现

    在上面这段代码当中,可以看出,拿到了接口的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中如何动态创建接口的实现

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

      转自https://blog.csdn.net/u013322876/article/details/70339518
  • 相关阅读:
    unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type.
    Go的学习笔记之Channel发送和接收
    聊聊 PC 端自动化最佳方案
    一文彻底搞懂快速幂(原理实现、矩阵快速幂)
    一次core.<number> 文件占满磁盘空间的处理过程
    博文目录
    备忘录:C#获取微信小程序的云数据库中数据
    T-SQL——关于跨库连接查询
    .NET异步程序设计——给线程传递数据
    自研 Pulsar Starter:winfun-pulsar-spring-boot-starter
  • 原文地址:https://www.cnblogs.com/tiancai/p/14219381.html
Copyright © 2011-2022 走看看