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

时间:2022-11-21 00:06:36

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

1、mybatis / jpa 等orm框架,可以在接口上加注解进行开发,不需要编写实现类,运行时动态产生实现。

2、dubbo等分布式服务框架,消费者只需要引入接口就可以调用远程的实现,分析源代码,其实在消费端产生了接口的代理实现,再由代理调用远程接口。

3、spring aop 这是最典型的动态代理了。

创建接口的动态实现,有二种最常用的方式:jdk动态代理和cglib动态代理。

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。

代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

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

通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(spring的aop机制),设计上获得更大的灵活性。

下面用jdk动态代理加一点简单的代码来演示这个过程:

1、接口

?
1
2
3
4
5
package com.yhouse.modules.daos;
 
public interface iuserdao {
  public string getusername();
}

2、创建代理

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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、运行时调用接口的方法时的实现(这一过程也称为接口的方法实现)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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、测试

?
1
2
3
4
5
6
7
8
9
10
11
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

?
1
2
3
4
5
6
7
package com.markliu.remote.service;
/**
 * service接口。代理类和被代理类抖需要实现该接口
 */
public interface service {
  public string getservice(string name, int number);
}

2、服务器端创建remoteservice类,实现了service 接口。

?
1
2
3
4
5
6
7
8
9
10
11
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 对象表示客户端发起的一个远程调用,它包括调用的类名或接口名、方法名、方法参数类型、方法参数值和方法执行结果。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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对象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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、创建远程服务器

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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、创建本地客户端

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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