深入理解JDK动态代理

余生长醉 提交于 2020-01-08 17:02:53

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

深入理解JDK动态代理

jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方,可以对接口方法进行增强。

创建接口

package com.ej.service;

public interface UserService {
    
    String saveUser(String name,Integer age);

}

创建实现类

package com.ej.service.impl;

import com.ej.service.UserService;

public class UserServiceImpl implements UserService {
    @Override
    public String saveUser(String name, Integer age) {
        String msg = "保存[" + name + "]的个人信息,年龄[" + age + "]岁";
        System.out.println(msg);
        return msg;
    }
}

创建增强类

package com.ej.proxy.jdk;

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

/**
* 必须要实现java.lang.reflect.InvocationHandler
* @author: Evan·Jiang
* @date: 2020/1/8 15:11
*/
public class JdkProxyHandler<T> implements InvocationHandler {

    private T target;

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

    public T getTarget() {
        return target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = null;
        try {
            //执行前增强
            System.out.println("执行目标方法前我可以干点啥呢???");
            //执行目标方法
            invoke = method.invoke(this.target, args);
        } finally {
            //执行后增强
            System.out.println("执行目标方法后我还可以干点啥呢???");
        }
        return invoke;
    }
}

创建代理对象

package com.ej.proxy.jdk;

import com.ej.service.UserService;
import com.ej.service.impl.UserServiceImpl;

import java.lang.reflect.Proxy;

public class JdkProxy {

    public static <T> T getProxy(JdkProxyHandler<T> jdkProxyHandler) {
        return (T) Proxy.newProxyInstance(
                jdkProxyHandler.getTarget().getClass().getClassLoader(),
                jdkProxyHandler.getTarget().getClass().getInterfaces(),
                jdkProxyHandler
        );
    }

    public static void main(String[] args) throws Exception {
        //创建目标对象
        UserService userService = new UserServiceImpl();
        //创建增强对象
        JdkProxyHandler<UserService> jdkProxyHandler = new JdkProxyHandler<>(userService);
        //创建代理对象
        userService = getProxy(jdkProxyHandler);
        //执行代理对象方法
        userService.saveUser("Evan", 66);
        //忽略,为了进程不死
        Thread.sleep(1000000L);
    }
}

先看结果

执行目标方法前我可以干点啥呢???

保存[Evan]的个人信息,年龄[66]岁

执行目标方法后我还可以干点啥呢???

提几个问题

  • 代码userService.saveUser("Evan", 66);中userService到底是什么?
  • 代码userService.saveUser("Evan", 66);如果userService不是UserServiceImpl,那它为什么能调用saveUser方法
  • 为什么被代理的对象一定要实现至少一个接口?
  • 增强类为什么一定要实现java.lang.reflect.InvocationHandler接口?
  • 不需要UserServiceImpl行不行?

看看代理对象的真面目

先debug

当前代码执行到22行,userService为UserServiceImpl的实例 - 很正常的!

继续往下执行

...

当前代码执行到26行,userService为Proxy0的实例(即为代理对象) - 感觉已经和UserServiceImpl没什么关系了,但事实上是有关系的,看看图中的h和target。

看看Proxy0

Q:首先看看Proxy0到底是个什么东西?肯定是一个类,怎么看呢?

A:Arthas可以帮到我们

  • 启动Arthas找到的进程

  • 输入上图中的进程号前面的编号进入进程

  • 查找代理类得到完整类名

  • 反编译代理类

  • 把代码复制出来,写一写注释
package com.sun.proxy;

import com.ej.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
//继承java.lang.reflect.Proxy类 - 看似废话,确回答了一个问题
extends Proxy
//实现com.ej.service.UserService接口 - 废话
implements UserService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    
    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.ej.service.UserService").getMethod("saveUser", Class.forName("java.lang.String"), Class.forName("java.lang.Integer"));
            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 $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    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 String saveUser(String string, Integer n) {
        try {
        	//this.h是父类java.lang.reflect.Proxy的属性
        	//类型为java.lang.reflect.InvocationHandler
        	//是不是回答了一个问题?
        	//m3:java.lang.reflect.InvocationHandler实现类的invoke方法中反射调用目标方法时用
            return (String)this.h.invoke(this, m3, new Object[]{string, n});
        }
        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);
        }
    }
}

Proxy0是怎么来的

  • 给个线程栈

  • 构造类文件

问题解答

Q:代码userService.saveUser("Evan", 66);中userService到底是什么?

A:userService是Proxy0的实例

Q:代码userService.saveUser("Evan", 66);如果userService不是UserServiceImpl,那它为什么能调用

A:代理类实现了被代理对象的方法,所以userService代理实例能调用saveUser方法

Q : 为什么被代理的对象一定要实现至少一个接口?

A:Proxy0想要有save方法只能选择继承UserServiceImpl类或者实现UserService接口,但是Proxy已经继承了java.lang.reflect.Proxy,Java不能多继承,所以只能选择实现接口

Q:增强类为什么一定要实现java.lang.reflect.InvocationHandler接口?

A:看看Proxy0的saveUser方法中this.h是父类java.lang.reflect.Proxy的属性字段,这个字段的类型就是java.lang.reflect.InvocationHandler,这里的值是我们自己定义的com.ej.proxy.jdk.JdkProxyHandler的实例,通过invoke直接调用【注意,这里并不是反射,真正的反射发生在com.ej.proxy.jdk.JdkProxyHandler类的invoke方法里面】

Q:不需要UserServiceImpl行不行?

A:这个问题就简单了,看看com.ej.proxy.jdk.JdkProxyHandler类的invoke方法,我们不调用目标方法不就行了,this.target就没鸟用了【RPC、Mybatis不就是这样吗】。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!