Maison  >  Article  >  Java  >  Comment créer dynamiquement des interfaces en Java

Comment créer dynamiquement des interfaces en Java

黄舟
黄舟original
2017-09-21 10:10:032964parcourir

Cet article présente principalement des informations pertinentes sur la façon de créer dynamiquement des méthodes d'implémentation d'interface en Java. Les amis qui en ont besoin peuvent s'y référer

Il existe de nombreux scénarios d'application qui utilisent l'implémentation dynamique d'interfaces. exemples typiques Applications :

1. Les frameworks ORM tels que mybatis/jpa peuvent être développés en ajoutant des annotations à l'interface. Il n'est pas nécessaire d'écrire des classes d'implémentation et l'implémentation est générée dynamiquement au moment de l'exécution.

2. Dans les frameworks de services distribués tels que dubbo, les consommateurs n'ont qu'à introduire l'interface pour appeler l'implémentation distante. Après avoir analysé le code source, il s'avère que l'implémentation proxy de l'interface est effectivement générée sur le. côté consommateur, puis le proxy appelle l'interface distante.

3. spring aop C'est le proxy dynamique le plus typique.

Il existe deux manières les plus courantes de créer une implémentation dynamique d'une interface : le proxy dynamique JDK et le proxy dynamique CGLIB.

Le modèle de proxy est un modèle de conception couramment utilisé. Son but est de fournir un proxy à d'autres objets pour contrôler l'accès à un objet réel.

La classe proxy est responsable du prétraitement des messages pour la classe déléguée, du filtrage des messages et du transfert des messages, et d'effectuer le traitement ultérieur une fois les messages exécutés par la classe déléguée.

Grâce à la couche intermédiaire de la couche proxy, l'accès direct aux objets de classe délégués réels peut être contrôlé efficacement et des stratégies de contrôle personnalisées (mécanisme AOP de Spring) peuvent être mises en œuvre. une plus grande flexibilité dans la conception.

Ce qui suit utilise le proxy dynamique JDK et ajoute du code simple pour démontrer ce processus :

1 Interface


package com.yhouse.modules.daos;

public interface IUserDao {
  public String getUserName();
}

2. . Créer un proxy


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. Implémentation lors de l'appel de la méthode d'interface au moment de l'exécution (ce processus est également appelé implémentation de la méthode d'interface)


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. Test


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

}

Dans ce code de test, il n'y a pas d'implémentation de l'interface. résultat?

Impression de console :

Cela signifie que lorsque l'interface est appelée, l'implémentation est déléguée à l'agent, et la dernière chose à faire est dans cet agent Traitement :

Dans le code ci-dessus, on peut voir qu'après avoir obtenu la méthode et les arguments de l'interface, vous pouvez faire beaucoup de choses, comme comme basé sur le nom de la méthode ou Utilisez les annotations au-dessus de la méthode pour implémenter des fonctions plus riches.

Un exemple simple est juste pour illustrer ce principe. Voici un autre exemple d'appel dynamique d'une interface distante pour approfondir la compréhension.

1. La création de la classe proxy et de la classe cible doit implémenter le service d'interface commun


package com.markliu.remote.service;
/**
 * Service接口。代理类和被代理类抖需要实现该接口
 */
public interface Service {
  public String getService(String name, int number);
}

Créez la classe RemoteService sur le serveur. côté et implémenter l'interface de 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. Créez une classe d'appel qui encapsule les demandes des clients et les informations de résultat renvoyées

Afin de faciliter le traitement des demandes des clients dans un manière orientée objet Pour la communication côté serveur, les informations qu'ils envoient peuvent être représentées par la classe Call. Un objet Call représente un appel à distance lancé par le client, qui inclut le nom de la classe appelante ou le nom de l'interface, le nom de la méthode, le type de paramètre de méthode, la valeur du paramètre de méthode et le résultat de l'exécution de la méthode.


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. Créez la classe de traitement métier réelle en mode proxy dynamique et implémentez l'interface 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. Créez une usine pour obtenir des classes proxy, 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. Créez une classe Connector pour la communication Socket sous-jacente, responsable de la création de l'interception et de l'envoi. et recevoir des objets d'appel


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 Créer un serveur distant


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. client local


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

Résultat de l'impression de la console :

Ce processus peut être simplement résumé comme suit : appel d'interface locale (client) --->Implémentation du proxy d'interface locale (client)---->Implémentation à distance (serveur)

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn