If there are dynamic proxies, there must be static proxies. Static proxies refer to static proxies in design mode

Function: Same as static proxy, but dynamic proxy solves the problem of one static proxy, only one type of target object

The following roles are involved in JDK dynamic proxies:

  • Interface of the target class
  • The target class target
  • Handle template handlers
  • Dynamic proxy classes that are generated in memory
  • java.lang.reflect.Proxy

Let’s cut the crap and start with the code

The instance

The interface of the target class

To have JDK dynamic proxies, your classes must implement interfaces

/** * public interface UserService {Boolean login(String userName, String password); }Copy the code

The target class

Note that methods to be proxied must be implemented from the interface

/** * UserServiceImpl is the target class, Public class UserServiceImpl implements UserService {@override public Boolean. */ UserServiceImpl implements UserService login(String userName, String password) { System.out.println("Check account");
        return false; }}Copy the code

Handle template classes

This is an implementation class for InvocationHandler

Here, we define a template that prints logs before and after the target method.

LogHandler’s target Object can be of any type, not just UserService. This way LogHandler can output logs before and after any method of any type, so it is called a processing template

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * This is a custom processing template that will be printed before and after the target methodlog*/ private UserService implements InvocationHandler {private UserService implements InvocationHandler; private Object target; Public LogHandler(Object target) {this.target = target; } /** * defines an output before and after the target method is executedlog@override Public Object Invoke (Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(target, args); after();return result;
    }

    private void before() {
        System.out.println("Log output before calling target method");
    }

    private void after() {
        System.out.println("Log output after calling target method"); }}Copy the code

There is a question here, who calls the invoke() method and what are the three arguments in the method (although I have already written that down)?

The test class

The three classes above make up the smallest unit of JDK dynamic proxies. Next, write test classes that use JDK dynamic proxies

import java.lang.reflect.Proxy; Public static void main(String[] args) {// Create a target object UserService userServiceImpl = new userServiceImpl (); LogHandler userServiceLogHandler = new LogHandler(userServiceImpl); /** * proxy. newProxyInstance dynamically generates a Proxy Class based on the Class loader of the target object, the Class of the interface, and the processing template of the target object. It implements the login method of the UserService interface */ UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userServiceImpl.getClass().getClassLoader(), new Class[]{UserService.class}, userServiceLogHandler); userServiceProxy.login("wqlm"."123");
    System.out.println("\n The name of the proxy class is + userServiceProxy.getClass().getName());
}
Copy the code

The output

/ Library/Java/jdk1.8.0 _201 / Contents/Home/bin/Java... Output before calling the target methodlogOutput after the verification account calls the target methodlogThe proxy class is named com.sun.proxy.$Proxy0

Process finished with exit code 0
Copy the code

java.lang.reflect.Proxy

This class is used in test methods to generate proxy objects!

This is the most important class in the JDK dynamic proxy, and we can generate a proxy class with the newProxyInstance method

public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h) throws IllegalArgumentExceptionCopy the code

As above, newProxyInstance takes three parameters

  • ClassLoader, the ClassLoader for target class
  • Class
    [], the Class of the target Class Interface
  • InvocationHandler, which handles the template class Handler

The process for generating a proxy class is as follows

  1. The proxy.newProxyinstance method, passed to it with the Class
    [] interfaces, InvocationHandler h generates the bytecode of the proxy class
  2. The Proxy loads the bytecode of the generated Proxy class using arguments passed to it (ClassLoader)
  3. Returns the object of the loaded proxy class

Dynamically generated proxy classes

Since the proxy classes are dynamically generated at run time, there are no.java files, and there are no.class files. $Proxy0; com.sun. Proxy.$Proxy0; com.sun

byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class<? >[]{userServiceProxy.getClass()}); String pathDir ="/Users/wqlm/Desktop";
String path = "/$Proxy0.class";
File f = new File(pathDir);
path = f.getAbsolutePath() + path;
f = new File(path);
if (f.exists()) {
    f.delete();
}

try {
    f.createNewFile();
} catch (IOException e) {
    e.printStackTrace();
}

try (FileOutputStream fos = new FileOutputStream(path)) {
    fos.write(bytes, 0, bytes.length);
} catch (Exception e) {
    e.printStackTrace();
} 
Copy the code

Get the bytecode stream and, after decompilation, get the approximate.java file. The following is the processed. Java file for the proxy class

import com.example.demo.service.UserService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; /** * The Proxy class inherits the Proxy and implements the target interface UserService */ public Final class$Proxy0 extends Proxy implements UserService {

    private static Method m3;

    static {
        try {
            m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("login",
                    new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.String")}); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); }} // When initialized, the processing template is passed to the parent class public$Proxy0(InvocationHandler var1) { super(var1); } /** * This is the proxy method, as you can see, it is the same as the invoke method that calls the parent's processing template, and then calls itself, */ public final Boolean login(String var1, String var2) {try {return((Boolean) super.h.invoke(this, m3, new Object[]{var1, var2})).booleanValue(); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); }}}Copy the code
  • The dynamic Proxy class implements the interface of the target class and implements the method in the interface by invoking the invoke () method of the parent class — Proxy H
  • NewProxyInstance (ClassLoader loader, Class
    [] interfaces, InvocationHandler h) was introduced into the third parameter of InvocationHandler object
  • The target object is invoked by reflection in InvocationHandler.invoke ()

Remember the question left in the handling template section — “Who calls invoke() and what are the three arguments in the method?” Now we know that it is the proxy class that calls Invoke () and passes three arguments: itself (the proxy object itself), the target method, and the argument list

conclusion

JDK dynamic proxy

Features: Instead of explicitly implementing the same interface as the target class, this implementation is deferred to the JVM when the program is called.

  • That is, create dynamic proxy classes and instances as they are used
  • Static proxies specify the same interface as the target when the proxy class is implemented

Advantages: A single proxy class can proxy multiple target classes, avoiding duplicate and redundant code

disadvantages

  • Compared with static proxy, the efficiency is low. Static proxies call methods of the target object directly, while dynamic proxies require classes and objects to call methods of the target object indirectly through Java reflection
  • Application scenarios are limited. Each Proxy class inherits java.lang.Reflect. Proxy, and Java supports only single inheritance, so you cannot create a Proxy class for a class, but only for an interface. That is, dynamic proxies can only proxy classes that implement the interface.

Application scenarios

  • A large number of objects need to be propped up. If the number of proxies is small, you are advised to use static proxies
  • When programming for facets

Differences from static proxies

Design patterns When the proxy class is created The principle of The efficiency of Number of target classes that can be proxied
A dynamic proxy Dynamically created at runtime reflection low Can proxy multiple target classes
Static agent Before running, show created / high Only one target class can be proxy