代理模式

静态代理 基于组合 和 接口

public interface ProxyInterface {
    void apply();
}
public class Base implements ProxyInterface {
    @Override
    public void apply() {
        System.out.println("做一些主线任务");
    }
}
public class Proxy implements ProxyInterface {
    public ProxyInterface proxyInterface;
    
    public Proxy(ProxyInterface proxyInterface) {
        this.proxyInterface = proxyInterface;
    }

    @Override
    public void apply() {
        System.out.println("start");
        proxyInterface.apply();
        System.out.println("end");
    }
}
public class Proxy2 implements ProxyInterface {
    public ProxyInterface proxyInterface;

    public Proxy2(ProxyInterface proxyInterface) {
        this.proxyInterface = proxyInterface;
    }

    @Override
    public void apply() {
        System.out.println("Proxy2 start");
        proxyInterface.apply();
        System.out.println("Proxy2 end");
    }
}
public class Main {
    public static void main(String[] args) {
        new Proxy2(new Proxy(new Base()));
    }
}
  • base 做了主线的任务。
  • Proxy 持有接口 类 调用其方法,并附加一些自己的操作,并且代理类可以持有代理类。
  • 如Main class Proxy 代理类Proxy,proxy 代理了base。类图在下方。
  • 缺点:每有一个类需要被代理,就要有这么一个代理类。
    image-1650374869603

java动态代理

public class JdkProxy implements InvocationHandler {
    public ProxyInterface proxyInterface;

    public JdkProxy(ProxyInterface proxyInterface) {
        this.proxyInterface = proxyInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理start");
        final Object invoke = method.invoke(proxyInterface);
        System.out.println("动态代理end");
        return invoke;
    }
}
public static void main(String[] args) {
        Proxy p = new Proxy(new Base());
            ProxyInterface proxyInterface = (ProxyInterface) java.lang.reflect.Proxy.newProxyInstance(Proxy.class.getClassLoader(), new Class[]{ProxyInterface.class},
                   new JdkProxy(new Base()));
            proxyInterface.apply();
        }
  • 注册jdk 动态代理必须实现 InvocationHandler 接口 并声明 代理的动作
  • 在实际调用的地方 传入 classLoader,以及需要代理的类
  • 当接口里的方法被执行时,jdk 会自动调用代理方法。
  • 当 调用 newProxyInstance 会传递 InvocationHandler实现类,jdk 会在内存中生成 代理类的动作。
  • 当调用被代理类时,实际会调用jdk 生成的class,在这个class 里jdk 会去调用被代理类的方法。
  • 是使用 asm 操作二进制,直接修改java 字节码。
    image-1650380114655

cglib代理

  • 引入依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
  • 代码实现
// 需要被代理的类
public class Base {
    public Integer apply() {
        System.out.println("执行主线任务");
        return 1;
    }
}
public class CallbackImpl implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("cglib 代理 start");
        var res = proxy.invokeSuper(obj, args);
        System.out.println("cglib 代理 end");
        return res;
    }
}
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Base.class);
        enhancer.setCallback(new CallbackImpl());
        var o = (Base) enhancer.create();
        o.apply();
    }
}
  • cglib 生成的类是代理类的子类 从Superclass 就能看出。
  • 所以如果代理的类是final 时,则无法代理该类。
  • 踩了个坑 当使用jvm 为 java16/java17 时会报错 superClass 为为一个final 的类
  • 这里是 issue

spring-aop动态代理

  • 用声明式的方式通过xml/注解 方式声明切点,动态生成代码。
  • 我觉得和其他动态代理相比优点是启动spring时就会把代码生成好,以达到真正调用时 代码已经load 到内存里了。

  • 谈一下个人对动态代理的看法
  • 优点:它非常的灵活,可以解耦合你的代码,让你的方法里只有业务代码,把一些实现隐藏,比如事务控制,日志管理,耗时统计,业务埋点
  • 缺点:它也有他的缺点,因为代理返回的Object 所以它并不是类型安全的,在多人协作时很容易出差错,并且他不容易调试,如果你一个项目里有上百个自定义的动态代理那绝对是灾难性的存在,所以他也不应该被滥用,反射算是一个black magic。