博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java中如何动态创建接口的实现
阅读量:6532 次
发布时间:2019-06-24

本文共 9237 字,大约阅读时间需要 30 分钟。

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

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

 控制台打印结果:

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

转载地址:http://cnqbo.baihongyu.com/

你可能感兴趣的文章
ubuntu samba服务器多用户配置【转】
查看>>
母线的种类与作用是什么(转)
查看>>
【Xamarin 挖墙脚系列:IOS 开发界面的3种方式】
查看>>
Atitit.工作流系统的本质是dsl 图形化的dsl 4gl
查看>>
I.MX6 Android USB Touch eGTouchA.ini文件存放
查看>>
4-5-创建索引表-串-第4章-《数据结构》课本源码-严蔚敏吴伟民版
查看>>
java 操作 RabbitMQ 发送、接受消息
查看>>
go run main.go undefined? golang main包那点事
查看>>
前端进阶(13) - 搭建自己的前端脚手架
查看>>
数据挖掘(二):认识数据
查看>>
从零开始写一个npm包,一键生成react组件(偷懒==提高效率)
查看>>
Golang中的路由
查看>>
【期末考试季】JAVA进阶复习提纲
查看>>
Volley(二)—— 基本Request对象 & RequestQueue&请求取消
查看>>
2017中国系统架构师大会“盛装”来袭
查看>>
Google插件switchysharp的用法
查看>>
中国最强的人工智能学术会议来了
查看>>
Metasploit的射频收发器功能 | Metasploit’s RF Transceiver Capabilities
查看>>
Osmocom-BB中cell_log的多种使用姿势
查看>>
主库 归档 删除策略
查看>>