When it comes to dynamically generating a class in Java, there are two main methods, one is dynamic proxy, the other is ASM. Today we are going to go through the process of the first method, which is dynamic proxy generation.

To understand dynamic proxies, we must first ask why dynamic proxies are needed. Are static proxies not enough?

First of all, consider a scenario. Does the leader review code have to approve all git submissions before they are actually uploaded to the master branch?

For this scenario, we can write the following code:

Start by defining the team member interface

/**
 * Created by admin on 2018/12/8.
 */
public interface TeamMember {
    public void reviewCode();

}

Copy the code

And then define the members of team A

/** * Created by admin on 2018/12/8. */ public class TeamAMember implements TeamMember { public TeamAMember(String name)  { this.name = name; } String name; @Override public voidreviewCode() {
        System.out.println("Team A" + name + "The code was reviewed."); }}Copy the code

Then define our agent class, that is, leader, to review everyone’s code

/** * Created by admin on 2018/12/8. */ public class TeamLeaderProxy implements TeamMember { public TeamLeaderProxy(TeamMember TeamMember) {// Only group A member code will be reviewedif (teamMember instanceof TeamAMember) {
            this.teamMember = teamMember;
        }
    }

    TeamMember teamMember;


    @Override
    public void reviewCode() {
        if(teamMember ! = null) { teamMember.reviewCode(); }}}Copy the code

The final call is as follows:

public class Main {

    public static void main(String[] args) {
        TeamAMember teamAMember=new TeamAMember("wuyue"); TeamLeaderProxy teamLeaderProxy=new TeamLeaderProxy(teamAMember); teamLeaderProxy.reviewCode(); }}Copy the code

The output is also simple:

So what’s the good of that? For example, if we find that team A’s code seems to have fewer comments recently, we can modify our code in the proxy class

/** * Created by admin on 2018/12/8. */ public class TeamLeaderProxy implements TeamMember { public TeamLeaderProxy(TeamMember TeamMember) {// Only group A member code will be reviewedif (teamMember instanceof TeamAMember) {
            this.teamMember = teamMember;
        }
    }

    TeamMember teamMember;


    @Override
    public void reviewCode() {
        if(teamMember ! = null) { System.out.println("Too little comment code. Be careful. Write more comments."); teamMember.reviewCode(); }}}Copy the code

This is essentially an AOP idea of adding operations to the process of proxying, either before or after the actions of the proxy

This is static proxy, static proxy means that our proxy class is a Java code that we defined and then compiled. What’s the flaw here?

Imagine that in addition to reviewing team members’ code, as a leader, we have todo many other things, such as checking the number of lines of code submitted per week, checking the recent commute time of team members, checking the todo list completed by team members, and so on.

If ONE day I want to add a timestamp to all of these things, don’t I need to change them in sequence? And then compile it again, right?

What if there are other leaders on the team? That’s a lot more to fix!

Too much trouble!!

Dynamic proxies allow us to generate a proxy class dynamically, so that we do not need to modify the methods in turn! And you can generate different proxies in different scenarios as needed! Isn’t it easier and more convenient?

Suppose we now have a requirement that every time the leader review should be printed out and shown to CTO. The CTO wants to see the working status of the leaders.

So we can use dynamic proxy to achieve this demand:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by admin on 2018/12/8.
 */
public class TeamMemberInvocation<T> implements InvocationHandler {

    T target;

    public TeamMemberInvocation(T target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("Prepare for dynamic proxy execution" + method.getName() + "Method");
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // Set the date format system.out.println ("Execution time is" + df.format(new Date()));
        Object result = method.invoke(target, args);
        returnnull; }}Copy the code
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Main {

    public static void main(String[] args) {
        TeamAMember teamAMember = new TeamAMember("wuyue");

        InvocationHandler teamMemberHandler = new TeamMemberInvocation<TeamMember>(teamAMember);

        TeamMember dynamicProxy = (TeamMember) Proxy.newProxyInstance(TeamMember.class.getClassLoader(), new Class<?>[]{TeamMember.class}, teamMemberHandler);

        dynamicProxy.reviewCode();


    }


}

Copy the code

Then take a look at our output:

Aye? You see what we want, but we didn’t write an actual proxy class in the code? Where exactly is this proxy class hiding?

How is dynamic proxy implemented?

The first thing we want to know is where the proxy class is hidden and what it looks like, right? So you can add a line of code

        System.out.println(DynamicProxy actual class ==+dynamicProxy.getClass().getName());

Copy the code

DynamicProxy ==com.sun.proxy.$Proxy0

View the important parts of our code

Continue to follow:

Then look at the ProxyClassFactory code

The truth came out. But because the class is dynamically generated, we need to get the class file if we want to see its bytecode decomcompiled code, which we obviously can’t get. But I came here because I was looking for the source code

We know how this dynamic class is generated, so we can write some test code to see if we can generate this dynamic class, and then export the bytecode class file of this class to our hard disk, and then decompile it to see the actual contents of this class.

public static void writeClassToDisk() {
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{TeamMember.class});
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("D:\\wuyue\\out\\production\\$Proxy0.class");
            fos.write(classFile);
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos ! = null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); }}}}Copy the code

Then we run our code, and sure enough, we finally generate this dynamic bytecode-like file:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements TeamMember {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

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

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void reviewCode() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("TeamMember").getMethod("reviewCode", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); }}}Copy the code

Now that we know how the dynamic class is generated, one last question is why does the method execution of the dynamic proxy class end up executing our Invoke method of InvocationHandler?

So what’s h?

H is our InvocationHandler instance.

Then look at M3

This is where the truth comes out, and it’s not that hard, but to sum it up:

The dynamically generated class is usually stored in memory, and when it is actually called, its constructor is called by reflection, and then the object of the proxy class is obtained. Note that the invoke method of the invocationHandler mediation class will eventually be called when the invoke method of the InvocationHandler mediation class is called

Use proxies to do some fun things on Android

Requirement: If I want to do something bad, any activity my colleague starts will end up on my activity.

To do this you should first have a general understanding of the activity launch process. I’m not going to go into detail here because of space and I’m just going to mention it.

In fact, the activity start process is based on the following code:

ContextImpl this method:

@Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();

        // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
        // generally not allowed, except if the caller specifies the task id the activity should
        // be launched in.
        if((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && options ! = null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) { throw new AndroidRuntimeException("Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }
Copy the code

Can see out. This method is also made by mMainThread getInstrumentation () method to perform the final. So here we just need to make the getInstrumentation return to our own Instrumentation. Note that the idea here is similar to static proxies

So we can construct a fake Instrumentation first

package com.longfor.dynamicproxyexample; import android.app.Activity; import android.app.Application; import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.IBinder; import android.util.Log; public class FakeInstrumentation extends Instrumentation { @Override public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {// Switch to my own activity if a colleague wants to start itif ("com.longfor.dynamicproxyexample.TestOneActivity".equals(className)) {
            className = "com.longfor.dynamicproxyexample.TestTwoActivity";
        }
        returnsuper.newActivity(cl, className, intent); }}Copy the code

All that remains is to hook into the ActivityThread object and replace the Instrumentation object.

package com.longfor.dynamicproxyexample;

import android.app.Application;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MyApplication extends Application {
    static Object activityThreadInstance;

    @Override
    public void onCreate() {try {// It is important to understand that class.forname does not return an object but a class <? > activityThread = Class.forName("android.app.ActivityThread"); // The currentActivityThread method is a static method. It does not need to use the class to call a static method. Method currentActivityThread = (CurrenTactiTactiVityThread =, currenTactiTactiVityThread =, currenTactiTactiVityThread =, currenTactiTactiVityThread =, currenTactiTactiVityThread = activityThread.getDeclaredMethod("currentActivityThread"); activityThreadInstance = currentActivityThread.invoke(null); // Replace the mInstrumentation variable Field field_instrumentation = with the sCurrentActivityThread instance activityThreadInstance.getClass() .getDeclaredField("mInstrumentation");
            field_instrumentation.setAccessible(true); FakeInstrumentation fakeInstrumentation = new FakeInstrumentation(); field_instrumentation.set(activityThreadInstance, fakeInstrumentation); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } super.onCreate(); }}Copy the code

Take a look at the effect:

Then someone will ask, you this is static proxy ah, dynamic proxy in Android how to use? One of the most basic elements of dynamic proxies is that the methods and classes you want to delegate must be derived from interface. This has higher requirements for our program design.

For example, there is a requirement that you make a logging SDK, and then the logging SDK needs to be used by six different teams. After using the logging SDK for a period of time, the feedback from each party is that they need to output a special timestamp in their own place. How do you do that? Some people say that the SDK can be upgraded to provide a new method ah, according to different parameters, the SDK output different timestamp ah. So I have to ask, what if we have n teams? You can’t write n if else logic in the SDK

In fact, here can use the dynamic proxy, can write the output log method to an interface, and then who wants to use the timestamp and other features, you can add their own dynamic proxy, so that can ensure the purity of the SDK, and can meet the requirements, but also very simple, why not?

So after reading this article, can you take a look at where else in your project you can design with dynamic proxies? Interested students can leave a message to share with me.