用图说话Java 中的代理模式

文章来自我的个人笔记,可以移步到 -> 创造者MC的个人笔记 继续浏览

前言

在使用 SpingAOP 时接触到了代理,觉得非常神奇,明明没有在目标类中添加代码,却能在方法执行前后编写额外代码。

多数文章没有把代理真正讲明白,我看了一些文章后进行了汇总,明白了代理的本质。

图解代理模式

代理

静态代理和动态代理的区别和联系

区别

静态代理:自己写代理类,自己创建代理类的对象。

动态代理:利用 ASM 动态生成代理类并创建代理类的对象。

联系

静态代理和动态代理的原理都是组合继承

JDK 动态代理使用组合方式实现。

CGLIB 动态代理使用继承方式实现。

测试代码

把图中的测试代码贴到下方,大家可以自己去尝试。

静态代理

继承

class Eat {
    public void eat() {
        System.out.println("吃饭");
    }
}

public class EatProxy extends Eat {
    @Override
    public void eat() {
        System.out.println("饭前");
        super.eat();
        System.out.println("饭后");
    }

    public static void main(String[] args) {
	// 这里的类型与图里的不一样,改成父类了,大家可以好好感受一下多态。
        Eat eatProxy = new EatProxy();
        eatProxy.eat();
    }
}

组合

interface Fly {
    void fly();
}

class Bird implements Fly {

    @Override
    public void fly() {
        System.out.println("鸟在飞");
    }
}

class Plane implements Fly {

    @Override
    public void fly() {
        System.out.println("飞机在飞");
    }
}

public class FlyProxy implements Fly{

    private Fly fly;

    public FlyProxy(Fly fly) {
        this.fly = fly;
    }

    @Override
    public void fly() {
        System.out.println("飞之前");
        fly.fly();
        System.out.println("飞之后");
    }
}

class Test {
    public static void main(String[] args) {
        Fly fly = new FlyProxy(new Bird());
        fly.fly();

        fly = new FlyProxy(new Plane());
        fly.fly();
    }
}

动态代理

JDK 动态代理

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

interface Work {
    void work();
}

class JavaEngineer implements Work {
    @Override
    public void work() {
        System.out.println("IDEA 启动!");
    }
}

class WorkProxy implements InvocationHandler {

    private Work work;

    public WorkProxy(Work work) {
        this.work = work;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法:" + method.getName() + "() 执行前");
        Object invoke = method.invoke(work, args);
        System.out.println("方法:" + method.getName() + "() 执行后");
        return invoke;
    }
}

public class JDKProxy {
    public static void main(String[] args) {
// 把下方注释掉的代码去掉注释,可以在执行后得到生成类的 .class 文件,用 IDEA 打开即可通过 IDEA 的反编译查看生成的具体类的内容(JDK 1.8)。
//        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        JavaEngineer javaEngineer = new JavaEngineer();
        /*
          参数一: 被代理类的 CLass 对象
          参数二:接口类的 Class 对象。被代理对象所实现的接口
          参数三:调用处理器。被调用对象的那个方法被调用后该如何处理
         */
        Work work = (Work) Proxy.newProxyInstance(JavaEngineer.class.getClassLoader(), new Class[]{Work.class}, new WorkProxy(javaEngineer));

        work.work();
    }
}

CGLIB 动态代理

注意:需要先引入依赖!

	<dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

class MinecraftPlayer {
    public void play() {
        System.out.println("玩 Minecraft");
    }
}

class PlayerProxy implements MethodInterceptor {

    // 代理对象
    private Object object;

    public PlayerProxy(Object target) {
        // 创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        // 为加强器指定要代理的业务类(为下面要生成的代理类指定父类)
        enhancer.setSuperclass(target.getClass());
        // 设置回调对象,对于代理类上的所有的方法调用,都会调用 CallBack,而 CallBack 需要实现 intercept 方法进行拦截
        enhancer.setCallback(this);
        // 创建动态代理类的对象
        object = enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法:" + method.getName() + "() 执行之前");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("方法:" + method.getName() + "() 执行之后");
        return result;
    }

    public Object getObject() {
        return object;
    }
}

public class CGLIBProxy {
    public static void main(String[] args) {
        PlayerProxy playerProxy = new PlayerProxy(new MinecraftPlayer());
        MinecraftPlayer minecraftPlayer = (MinecraftPlayer) playerProxy.getObject();
        minecraftPlayer.play();
    }
}