Home  >  Article  >  Java  >  How to dynamically create interfaces in Java

How to dynamically create interfaces in Java

黄舟
黄舟Original
2017-09-21 10:10:033036browse

This article mainly introduces relevant information on how to dynamically create interface implementation methods in Java. Friends who need it can refer to it

There are many application scenarios that use dynamic implementation of interfaces. Here are a few typical examples Applications:

1, mybatis/jpa and other ORM frameworks, you can add annotations to the interface for development. There is no need to write implementation classes, and the implementation is dynamically generated at runtime.

2. In distributed service frameworks such as dubbo, consumers only need to introduce the interface to call the remote implementation. After analyzing the source code, it is found that the proxy implementation of the interface is actually generated on the consumer side, and then the proxy calls the remote interface.

3. spring aop This is the most typical dynamic proxy.

There are two most commonly used ways to create a dynamic implementation of an interface: JDK dynamic proxy and CGLIB dynamic proxy.

The proxy pattern is a commonly used design pattern. Its purpose is to provide a proxy for other objects to control access to a real object.

The proxy class is responsible for preprocessing messages for the delegate class, filtering messages and forwarding messages, and performing subsequent processing after the messages are executed by the delegate class.

Through the middle layer of the proxy layer, direct access to real delegate class objects can be effectively controlled, and customized control strategies (spring's AOP mechanism) can be implemented at the same time. Gain greater flexibility in design.

The following uses JDK dynamic proxy and adds some simple code to demonstrate this process:

1. Interface


package com.yhouse.modules.daos;

public interface IUserDao {
  public String getUserName();
}

2. Create 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. Implementation when calling the interface method at runtime (this process is also called interface method implementation)


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

}

In this test code, there is no implementation of the interface. Guess what the result will be?

Console printing:

It means that when the interface is called, the implementation is delegated to the agent, and the final thing to do is in this agent Processing:

In the above code, it can be seen that after getting the method and args of the interface, you can do a lot of things, such as based on the method name or Use the annotations above the method to implement richer functions.

A simple example is just to illustrate this principle. Here is another example of dynamic calling of a remote interface to deepen understanding.

1. Creating the proxy class and the target class need to implement the common interface Service


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

2. Create the RemoteService class on the server side and implement the Service interface.


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. Create a Call class that encapsulates client requests and return result information

In order to facilitate the handling of client and server-side in an object-oriented manner For communication, the information they send can be represented by the Call class. A Call object represents a remote call initiated by the client, which includes the calling class name or interface name, method name, method parameter type, method parameter value and method execution result.


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. Create the actual business processing class in the dynamic proxy mode and implement the InvocationHandler interface


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 , Create a factory to obtain the proxy class 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. Create a Connector class for underlying Socket communication, responsible for creating interception, sending and accepting Call objects


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. Create a remote server


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


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

Console printing results:

This process can be simply summarized as: local interface call (client) --->Local interface proxy implementation (client) ---->Remote implementation (server side)

The above is the detailed content of How to dynamically create interfaces in Java. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn