arthas揭露JDK动态代理的真面目

463 阅读1分钟

今天给大家带来的是使用arthas的jad命令来反编译字节码,查看JDK代理的产物。话不多说,直接进入正题。 首先有如下一些简单的java代码 UserService

public interface UserService {
    public boolean login(String username, String password);
}

UserServiceImpl

@Slf4j
public class UserServiceImpl implements UserService {

    @Override
    public boolean login(String username, String password) {
        log.info("username:{}, password:{}", username, password);
        return false;
    }
}

MyInvocationHandler

@Slf4j
public class MyInvocationHandler implements InvocationHandler {

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info("before invoke");
        Object res = method.invoke(target, args);
        log.info("after invoke");
        return res;
    }
}

ProxyMain

@Slf4j
public class ProxyMain {
    public static void main(String[] args) throws InterruptedException {
        UserService target = new UserServiceImpl();
        UserService userService = (UserService) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new MyInvocationHandler(target));
        userService.login("zhq123", "123456");
        log.info("proxy class:{}", userService.getClass());
        Thread.sleep(Long.MAX_VALUE);
    }
}

pom.xml

<dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.32</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.9</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
    </dependencies>

接下来直接运行代码,日志输出如下:

20:27:20.529 [main] INFO com.zhq.proxy.MyInvocationHandler - before invoke
20:27:20.530 [main] INFO com.zhq.proxy.UserServiceImpl - username:zhq123, password:123456
20:27:20.531 [main] INFO com.zhq.proxy.MyInvocationHandler - after invoke
20:27:20.531 [main] INFO com.zhq.proxy.ProxyMain - proxy class:class com.sun.proxy.$Proxy0

接下来我们打开arthas工具,没有的小伙伴可以前往arthas官网下载arthas.aliyun.com/doc/install… 执行如下命令

java -jar .\arthas-boot.jar

在终端输出的结果中找到我们需要连接的java程序的编号,我这里是2,直接输入2即可 Snipaste_2024-11-04_20-39-15.png 接下来执行jad命令,获取反编译后的java代码

jad com.sun.proxy.$Proxy0

得到如下java代码

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

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.zhq.proxy.UserService").getMethod("login", Class.forName("java.lang.String"), Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }

    public final boolean equals(Object object) {
        try {
            return (Boolean)this.h.invoke(this, m1, new Object[]{object});
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString() {
        try {
            return (String)this.h.invoke(this, m2, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)this.h.invoke(this, m0, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final boolean login(String string, String string2) {
        try {
            return (Boolean)this.h.invoke(this, m3, new Object[]{string, string2});
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
}

通过这样的操作,希望能够让大家更好的理解动态代理。