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);
    }
}

控制台打印结果:

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

原文地址:https://www.cnblogs.com/jiangzhaowei/p/9880937.html

时间: 2024-10-15 01:14:10

Java中如何动态创建接口的实现的相关文章

Java语言中反射动态代理接口的解释与演示

Java语言中反射动态代理接口的解释与演示 Java在JDK1.3的时候引入了动态代理机制.可以运用在框架编程与平台编程时候捕获事件.审核数据.日志等功能实现,首先看一下设计模式的UML图解: 当你调用一个接口API时候,实际实现类继承该接口,调用时候经过proxy实现. 在Java中动态代理实现的两个关键接口类与class类分别如下: java.lang.reflect.Proxy java.lang.reflect.InvocationHandler 我们下面就通过InvocationHan

java中的动态代理机制

在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的.下面通过代码来学习java中的动态代理技术. 首先定义一个接口: package com.aop.spring; /** * Created by xinfengyao on 16-2-29. */ public interface Perform { public void play(); } 实

Java 反射Array动态创建数组

Java 反射Array动态创建数组 @author ixenos 注:java.lang.reflect.Array 是个反射工具包,全是静态方法,创建数组以多维数组为基准,一维数组只是特殊实现 创建一个具有指定的组件类型和长度的新数组(一维数组) newInstance public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException 创建一个具有指定

Java中Comparable和Comparator接口区别分析

Java中Comparable和Comparator接口区别分析 来源:码农网 | 时间:2015-03-16 10:25:20 | 阅读数:8902 [导读] 本文要来详细分析一下Java中Comparable和Comparator接口的区别,两者都有比较的功能,那么究竟有什么区别呢,感兴趣的Java开发者继续看下去吧.Comparable 简介Comparable 是排序接口.若一个类实现了Comparab 本文要来详细分析一下Java中Comparable和Comparator接口的区别,

十分钟理解Java中的动态代理

十分钟帮助大家理解Java中的动态代理,什么是动态代理?感兴趣的小伙伴们可以参考一下 若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. 一.概述1. 什么是代理我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家"委托"代理为其销售商品.关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,"委托

使用Java中的动态代理实现数据库连接池

2002 年 12 月 05 日 作者通过使用JAVA中的动态代理实现数据库连接池,使使用者可以以普通的jdbc连接的使用习惯来使用连接池. 数据库连接池在编写应用服务是经常需要用到的模块,太过频繁的连接数据库对服务性能来讲是一个瓶颈,使用缓冲池技术可以来消除这个瓶颈.我们可以在 互联网上找到很多关于数据库连接池的源程序,但是都发现这样一个共同的问题:这些连接池的实现方法都不同程度地增加了与使用者之间的耦合度.很多的连接池 都要求用户通过其规定的方法获取数据库的连接,这一点我们可以理解,毕竟目前

java中内部类的创建四种情况,三种方式,及内部数据访问权限

内部类和外部类的关系,及内部静态类的数据访问权限,声明方式. 第一种,在外部类内部声明使用内部类,内部类的类型为static和非 static类型,内部类数据类型为private,protected,public 类型的访问权限.外部类为非静态声明和权限如下: package com; public class Test1 { //@author 张春蕾 private class Name{ private int i =1; public int s = 2; protected int m

java 中的2个接口 Comparable和Comparator

像Integer.String这些类型的数据都是已经实现Comparable接口的,所以对这些类型可以直接通过Arrays.sort(...)和Collections.sort(...)方法进行排序.但是对于一些自己new出来的对象而言,如果想使用sort这种方法,必须要实现Comparable接口,这是由sort方法底层决定的,具体看一下实例: 定义一个Bean public class Employee implements Comparable{ private int age;  pri

Java中通过类名创建一个类的实例

Java中通过类名创建类的实例,此类必须是默认的构造方法,不能自己写构造方法. 方法1: UserDao userDao=null; Properties prop=new Properties();    InputStream inStream=DaoFactory.class.getClassLoader().getResourceAsStream("daoconfig.properties");    prop.load(inStream);    String userDao