The Jvm loads objects

Before we get to Java dynamic proxies, let’s take a look at how the Jvm loads objects, which is still fundamental to understanding dynamic proxies:

Class files, which are read by the classloader and converted into java.lang.Class objects, describe the data structure of the class in the metadata space, and store the instantiated object information in the heap when the class is instantiated. And find the class by the pointer to the object type data.

Process description: source ->.java file ->.class file -> class object -> instance object

So, after understanding the process of creating objects with New and dictating many of the implementation details behind them, a common design pattern is known as the proxy pattern.

2. Agency mode

1. Basic description

The proxy pattern provides a proxy object to a (target) object and holds a reference to the target object. A proxy is an object that performs an action on behalf of another object. A proxy object can act as an intermediary between the client and the target object.

There are many scenarios of agency mode in real life, such as intermediary, lawyer, purchasing agent and other industries, all of which are simple agency logic. There are two key roles in this mode:

Target object role: The object represented by the proxy object.

The role of the proxy object: contains internal references to the target object and can operate the target object. AOP programming is based on this idea.

2. Static and dynamic mode

  • Static proxy: Before the program runs, the proxy role is determined and the relationship between the proxy class and the target class is clarified.

  • Dynamic Proxy: Proxy objects are dynamically created and generated while the JVM is running, based on Java reflection.

Static proxy

Based on the concept of static proxy, a code is used to describe the implementation, the basic logic is as follows:

  • Specify the target object as the proxied object;
  • Define a proxy object that holds the target object through a constructor;
  • Define pre – and post-enhancement methods in proxy objects;

The target object and the pre-and-post enhancement code make up the proxy object, so you don’t have to directly access the target object, much like in the TV: I’m a lawyer, and my client can’t talk to you.

public class Proxy01 {
    public static void main(String[] args) {
        TargetObj targetObj = new TargetObj() ;
        ProxyObj proxyObj = newProxyObj(targetObj) ; proxyObj.invoke(); }}class TargetObj {
    public void execute (a){
        System.out.println("Target class method executes..."); }}class ProxyObj {
    private TargetObj targetObj ;
    /** * holds the target object */
    public ProxyObj (TargetObj targetObj){
        this.targetObj = targetObj ;
    }
    /** * target object method call */
    public void invoke (a){
        before () ;
        targetObj.execute();
        after () ;
    }
    /** ** before and after processing */
    public void before (a){
        System.out.println("Proxy object preprocessing...");
    }
    public void after (a){
        System.out.println("Proxy object post-processing..."); }}Copy the code

Static agent clearly define the proxy object that has a proxy object. Java file is loaded into the JVM process, apparently a problem, in the actual development process, for each target object defines a proxy class, also can’t let a proxy object to represent multiple target objects, these two approaches of maintenance costs are very high.

The essence of proxy mode is to place enhancement operations before and after the method of the target object, but do not want to modify the target Class. As can be seen from the previous reflection mechanism, the structure information of the object can be obtained during operation, and the proxy object can be dynamically created based on the Class information, which is the dynamic proxy mechanism.

Incidentally: the underlying implementation logic of the technology is notoriously difficult to understand, while the basic knowledge is not complex, such as the basic principle of proxy pattern, but when combined with the actual complex application (AOP pattern), it is difficult to understand the implementation based on reflection and dynamic proxy.

Dynamic proxy

1. Scene Description

To describe the difference between dynamic agents and static agents, a concept that has become popular in recent years, overseas daigou, based on a scenario:

In the early stage of daigou, some people who often go on business trips abroad will meet the needs of daigou, that is, the agent is fixed. Later, a series of products such as overseas purchasing platform and overseas shopping emerged. In other words, users’ purchasing needs (target objects) are realized by the purchasing platform, but the specific operation depends on instant allocation. This scenario is similar to the principle of dynamic agent.

2. Basic API cases

Let’s start with the two core classes. Here’s a brief overview of the concepts, and then go through the basic process:

  • Proxy- Creates a Proxy object. Core parameters:

    • ClassLoader :(target class) loader;
    • Interfaces :(target class) an array of Interfaces;
    • InvocationHandler: proxy invocation mechanism;
  • InvocationHandler- Proxy class call mechanism:

    • Invoke: The principle of reflection described in the previous article;
    • Method: the core API of the reflection library;

Target object and interface

interface IUser {
    Integer update (String name) ;
}
class UserService implements IUser {
    @Override
    public Integer update(String name) {
        Integer userId = 99 ;
        System.out.println("UserId="+userId+"; updateName="+name);
        returnuserId ; }}Copy the code

Proxy object execution mechanism

class UserHandler implements InvocationHandler {
    private Object target ;
    public UserHandler (Object target){
        this.target = target ;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before()...");
        Object result = method.invoke(target, args);
        System.out.println("after()...");
        returnresult; }}Copy the code

Specific combination mode

public class Proxy02 {
    public static void main(String[] args) {
        /* * Generate $Proxy0 class file */
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true");
        /* * Target object information */
        IUser userService = newUserService(); ClassLoader classLoader = userService.getClass().getClassLoader(); Class<? >[] interfaces = UserService.class.getInterfaces() ;/* * Create a proxy object */
        InvocationHandler userHandler = new UserHandler(userService);
        $Proxy0 =com.java.proxy.$Proxy0 */
        String proxyClassName = Proxy.newProxyInstance(classLoader,interfaces,userHandler).getClass().getName();
        System.out.println("proxyClassName="+proxyClassName);
        /* * Specific business implementation simulation */
        IUser proxyUser1 = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
        IUser proxyUser2 = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
        proxyUser1.update("cicada"); proxyUser2.update("smile"); }}Copy the code

The proxy class structure information is generated here because it is not visible from the JVM loading process, and the key information is again arbitrary:

javap -v Proxy02.class
Copy the code

View the proxy class name

/* * proxyClassName=com.java.proxy.$Proxy0 */
String proxyClassName = Proxy.newProxyInstance(classLoader,interfaces,userHandler).getClass().getName();
System.out.println("proxyClassName="+proxyClassName);
Copy the code

Subconsciously output the proxy object name, which in this case corresponds to the JVM mechanism, find the Class object name, and then analyze the structure to see how dynamic proxies work.

Generate the proxy class.class file

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true");
Copy the code

As you can see from the JVM loading mechanism above, the Class object describing the proxy Class must exist, but there is no explicit.class file generated at runtime. Using the syntax above to generate the proxy Class, the file will be created in the /com/java/proxy path of the project directory.

BTW: as a programmer, complexity is always with us. What about simplicity?

3. Proxy class structure

Inheritance and Implementation

class $Proxy0 extends Proxy implements IUser {}
Copy the code

From the function of Proxy class to think, we can think of the need to inherit Proxy and implement the IUser interface, there is a specific implementation class holding the call mechanism, used to do business enhancement.

A constructor

public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
}
Copy the code

The constructor holds the specific execution mechanism object of UserHandler.

Interface implementation

final class $Proxy0 extends Proxy implements IUser {
    private static Method m3;
    public final Integer update(String var1) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw newUndeclaredThrowableException(var4); }}}Copy the code

The basic requirement of the target class is the Update () method, which is carried on through the proxy class and implemented with the specific enhanced business processing based on UserHandler.

Basic method

final class $Proxy0 extends Proxy implements IUser {
    private static Method m0;
    private static Method m1;
    private static Method m2;
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.java.proxy.IUser").getMethod("update", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw newNoClassDefFoundError(var3.getMessage()); }}public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw newUndeclaredThrowableException(var4); }}public final String toString(a) throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}public final int hashCode(a) throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}}Copy the code

Based on the Object class, we define several common Methods in Java: equals(), toString(), and hashCode(). We explained why these methods usually appear together in the Map source code.

JDK source code

The above is the process and principle of case execution, and there is another key point to understand, namely the logic of JDK source code:

IUser proxyUser = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
Copy the code

NewProxyInstance (), a static method provided by Proxy, builds a newProxy Class object, which is the structure of the $Proxy0 Class.

  • ClassLoader: Runs on the JVM, so you need to get the ClassLoader of the target class UserService;

  • Interfaces: Interfaces implemented by the target class UserService. From an object-oriented perspective, Interfaces are separated from implementation. Proxy classes simulate the needs of the target class by implementing the IUser interface.

  • InvocationHandler: The proxy class provides a functional wrapper called UserHandler that can be enhanced before and after the target method is called;

Finally, the core technical points of dynamic proxy implementation are summarized: Jvm loading principle, reflection mechanism, object-oriented thought; Every time I read the JDK source code, I will marvel at the uncanny workmanship of the designers.

Five, source code address

Making address GitEE, https://github.com/cicadasmile/java-base-parent, https://gitee.com/cicadasmile/java-base-parentCopy the code