别再说你不懂java的动态代理了

330 阅读4分钟

这是我参与更文挑战的第6天,活动详情查看: 更文挑战

所谓动态代理,简单来说,其实就是在程序运行期间,动态地生成一个代理对象来实现对目标对象方法的增强。可以简单粗暴的理解为 代理对象 = 目标对象 + 增强代码

在讲动态代理之前,我们先来看下静态代理。

一、静态代理

我们举一个例子来说,假设每个单身的朋友都有谈恋爱求偶的技能,那么我们可以定义一个单身汉的接口,然后这个接口有一个求偶的方法如下:

public interface SingleDog {

   String courtShip();//恋爱求偶结婚技能
}

然后我们的主角小王毕业五年,虽然至今单身,但是很幸运地也拥有了这项技能。

public class DogWang implements SingleDog {
    @Override
    public String courtShip() {
        return "宝,我今天过马路了,过的什么路,爱你没有退路";
    }
}

我们都知道小王有了谈恋爱的方法,但是平时作为一个社畜,上下班两点一线,一直苦于没有遇到一个合适的女孩子来施展自己的这项技能。同时这个年纪单身未婚,可把家里的王妈急坏了,赶紧介绍了朋友的女儿小宁给儿子认识。好了,这里的王妈就是我们的代理对象

public class WorryMom implements SingleDog {

    private DogWang dogWang;

    public WorryMom(DogWang dogWang) {
        this.dogWang = dogWang;
    }

    @Override
    public String courtShip() {
        //介绍小宁给儿子认识
        System.out.println("this is miss ning's wechat");
        String honeyWord = dogWang.courtShip();
        //耐心嘱咐儿子多联系
        System.out.println("if you fail,get the fu** out of my home");
        return honeyWord;
    }
}

王妈作为代理对象,并不会实际去谈恋爱。而是通过构造方法传入小王,可以理解为带小王去见小宁。最终真正去谈恋爱的还是我们的主角小王。但是妈妈的嘱托一定程度上帮助小王更好地完成这项任务

静态代理的弊端

事实上王妈可能不止小王这一个儿子,那么她每新增一个儿子,都需要去增加硬编码,这之间有很强的耦合性,维护起来肯定是很麻烦的。

二、动态代理

相信通过上面的例子我们都能很清晰地理解静态代理,那么动态代理呢? JDK提供了java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类,这两个类来相互配合实现我们的动态代理。

Proxy中有个静态方法getProxyClass,用来获取接口的代理对象

public static void main(String[] args) throws Exception {
        //获取接口的代理class对象 参数1是对应的类加载器,参数2是对应接口
        Class<?> proxyClass = Proxy.getProxyClass(SingleDog.class.getClassLoader(), SingleDog.class);
        //获取对应的有参构造器
        Constructor<?> constructor =proxyClass.getConstructor(InvocationHandler.class);
        SingleDog dogWang = (SingleDog) constructor.newInstance(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //invoke中没有目标对象,手动new一个
                DogWang dogWang = new DogWang();
                System.out.println("this is miss ning's wechat");
                Object invoke = method.invoke(dogWang, args);
                System.out.println("if you fail,get the fu** out of my home");
                return invoke;
            }
        });
        dogWang.courtShip();
    }

上述代码的invoke方法中没有目标对象,只能手动new一个小王出来,这种硬编码的方式肯定不行。我们可以把目标对象作为参数,写一个方法专门用来获取代理class对象。如下:

public static Object getProxy(final Object target) throws Exception {
        //获取接口的代理class对象
        Class<?> proxyClass = Proxy.getProxyClass(target.getClass().getClassLoader(), target.getClass().getInterfaces());
        //获取对应的有参构造器
        Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
        Object proxy = constructor.newInstance(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("this is miss ning's wechat");
                Object invoke = method.invoke(target, args);
                System.out.println("if you fail,get the fu** out of my home");
                return invoke;
            }
        });
        return proxy;
    }

调用时传入目标对象即可:

public static void main(String[] args) throws Exception {
        SingleDog singleDog = (SingleDog) getProxy(new DogWang());
        singleDog.courtShip();
    }

这样其实就已经完美的实现了jdk的动态代理了,不过在日常使用中,一般是使用Proxy的另外一个方法来实现,Proxy#newProxyInstance。 这种方法更简洁地实现了动态代理。如下:

public static Object getProxy(final Object target) throws Exception {
        Object instance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("this is miss ning's wechat");
                Object invoke = method.invoke(target, args);
                System.out.println("if you fail,get the fu** out of my home");
                return invoke;
            }
        });
        return instance;
    }

今天是和喜欢的人断联的整四个月,看不清将来的路,有时候会觉得很痛苦,害,还是要埋头往前走啊。

希望每个人都可以在冷铁卷刃之前,得以窥见天光吧。

以上,谢谢你看到这。