Reflection and dynamic proxy

The proxy pattern

Proxy schema definition:

The proxy pattern provides a proxy or placeholder for another object to control access to that object.

Why proxy mode is used:

The proxy pattern is used to create representative objects that control access to real objects, which can be remote objects, expensive objects to create, or objects that require security control.

In simple terms, we use proxy objects instead of real objects to provide additional functionality and extend the functionality of the target object without modifying the real object.

Take real life as an example: when a defendant is interviewed by a judge, he commissions a lawyer to take his place. The representative object here is the lawyer, the real object is the defendant. The lawyer asked the defendant privately after further explaining the judge’s question in accordance with his professional knowledge. After the suspect talks privately to his lawyer, the lawyer relies on his answering skills to explain to the judge.

The proxy mode has two implementation modes: static proxy and dynamic proxy.

Static agent

Static proxies prepare interfaces, delegate classes, and proxy classes before the program runsThe bytecodeFile).

To print “Hello, world!” For example, the static proxy implementation steps are as follows:

  1. Define an interface
interface HelloWordService {
    void printHelloWorld(a);
}
Copy the code
  1. The delegate class implements this interface
class HelloWorldServiceImpl implements HelloWordService {
    @Override
    public void printHelloWorld(a) {
        System.out.println("Hello, world!"); }}Copy the code
  1. Proxy classes also implement this interface
class HelloWorldProxy implements HelloWordService {
    private final HelloWordService helloWorldService;

    public HelloWorldProxy(HelloWordService helloWorldService) {
        this.helloWorldService = helloWorldService;
    }

    @Override
    public void printHelloWorld(a) {
        System.out.println("I'm here to do something as an agent.");
        helloWorldService.printHelloWorld();
        System.out.println("One last act as agent."); }}Copy the code
  1. The delegate object is injected into the proxy object and the corresponding method of the delegate object is called in the corresponding method of the proxy object
public class Main {
    public static void main(String[] args) {
        HelloWordService helloWordService = new HelloWorldServiceImpl();
        HelloWordService helloWordProxy = newHelloWorldProxy(helloWordService); helloWordProxy.printHelloWorld(); }}Copy the code

After running, the console prints output

As your agent, I’ll do something first. To do one last thing as an agent

In static proxies, a separate proxy class is written for each delegate class, and enhancements to each method of the delegate object are done manually. Once new methods are added to the interface, both the delegate and proxy classes are modified. So we need more flexible dynamic proxies.

JDK dynamic proxy

Dynamic proxies dynamically generate proxy class bytecodes while the program is running and load them into the JVM.

Dynamic proxies are more flexible than static proxies. We only need to define interfaces and implementation classes; we don’t need to write proxy classes.

The JDK provides the InvocationHandler interface Proxy class to implement dynamic proxies.

The Proxy class

The most common methods in the Proxy class are:

public static Object newProxyInstance(ClassLoader loader, Class
       [] interfaces, InvocationHandler h)
Copy the code

The static method takes the following parameters:

  1. Loader: class loader used to load proxy objects
  2. Interfaces: Interfaces implemented by a delegate class and passed in at least one interface
  3. H: Implements an instance of the InvocationHandler interface for logical processing

InvocationHandler interface

From the third argument, we must implement the InvocationHandler interface. When we invoke a method on a dynamic proxy object, the JVM invokes the Invoke method of the implementation class to handle the corresponding logic.

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
Copy the code

The parameters of this method are:

  1. Proxy: dynamically generated proxy class instance by the JVM
  2. Method: The method class instance that corresponds to the method calling the proxy class instance
  3. Args: arguments to invoke the proxy class instance method

Use of JDK dynamic proxies

Continue to print “Hello, world!” For example, the implementation steps of dynamic proxy are as follows:

  1. Defines the interface
interface HelloWordService {
    void printHelloWorld(a);
}
Copy the code
  1. The delegate class implements this interface
class HelloWorldServiceImpl implements HelloWordService {
    @Override
    public void printHelloWorld(a) {
        System.out.println("Hello, world!"); }}Copy the code
  1. Define a JDK dynamic proxy class that calls native methods in the Invoke method and customizes the logic handling
class SimpleInvocationHandler implements InvocationHandler {
    private final Object target;

    public SimpleInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("I'm here to do something as an agent.");
        Object result = method.invoke(target, args);
        System.out.println("One last act as agent.");
        returnresult; }}Copy the code
  1. Create a Proxy object using the proxy. newProxyInstance method
// Define a static factory class that produces proxy objects
class ProxyFactory {
    @SuppressWarnings("unchecked")
    public static <T> T getProxy(T target) {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                newSimpleInvocationHandler(target) ); }}public class Main {
    public static void main(String[] args) {
        // Through the proxy factory, produce the proxy class instance
        HelloWordService serviceProxy = 
                ProxyFactory.getProxy(newHelloWorldServiceImpl()); serviceProxy.printHelloWorld(); }}Copy the code

After running, the console prints output

As your agent, I’ll do something first. To do one last thing as an agent

conclusion

  • The proxy mode allows you to extend the functionality of the target object by providing additional functionality without modifying the real object
  • Static proxies prepare interfaces, delegate classes, and proxy classes before the program runsThe bytecodeFile)
  • Dynamic proxies dynamically generate proxy class bytecodes while the program is running and load them into the JVM