这篇文章上次修改于 862 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

动态代理

1. 有接口 JDK动态代理
public interface work {
    void web();
}
public class Server implements work {
    @Override
    public void web() {
        System.out.println("server really work");
    }
}
//  -----------------------接口和实现类     


class ProxyServerFactory {
    Server ser = new Server();

    public work getObject() {
        //返回将方法调用分派到指定调用处理程序的指定接口的代理实例。
        //如果违反以下任何限制,将抛出IllegalArgumentException :
        //给定interfaces数组中的所有Class对象都必须表示接口,而不是类或原始类型。
        //interfaces数组中的任何两个元素都不能引用相同的Class对象。
        //所有接口类型都必须通过指定的类加载器按名称可见。 换句话说,对于类加载器cl和每个接口i ,以下表达式必须为真:
        //Class.forName(i.getName(), false, cl) == i
        //指定接口的所有公共方法签名引用的所有类型以及由它们的超接口继承的类型必须通过指定的类加载器按名称可见。
        //所有非公共接口必须在同一个包和模块中,由指定的类加载器定义,并且非公共接口的模块可以访问所有的接口类型; 否则,代理类不可能实现所有接口,无论它定义在哪个包中。
        //对于具有相同签名的指定接口的任何成员方法集:
        //如果任何方法的返回类型是原始类型或 void,则所有方法都必须具有相同的返回类型。
        //否则,其中一个方法必须具有可分配给其余方法的所有返回类型的返回类型。
        //生成的代理类不得超过虚拟机对类施加的任何限制。 例如,VM 可能会限制一个类可以实现的接口数量为 65535; 在这种情况下, interfaces数组的大小不得超过 65535。
        //请注意,指定代理接口的顺序很重要:对具有相同接口组合但顺序不同的代理类的两个请求将导致两个不同的代理类。
        //参形:
        //loader – 定义代理类的类加载器
        //interfaces – 代理类要实现的接口列表
        //h - 将方法调用分派到的调用处理程序
        //返回值:
        //具有代理类的指定调用处理程序的代理实例,该代理类由指定的类加载器定义并实现指定的接口
        //抛出:
        //IllegalArgumentException – 如果违反了对参数的任何限制
        //SecurityException – 如果存在安全管理器s并且满足以下任何条件:
        //给定的loader为null并且调用者的类加载器不为null并且使用RuntimePermission("getClassLoader")权限调用s.checkPermission拒绝访问;
        //对于每个代理接口intf ,调用者的类加载器与intf的类加载器的祖先不同,并且调用s.checkPackageAccess()拒绝访问intf ;
        //任何给定的代理接口都是非公共的,并且调用者类与非公共接口不在同一个运行时包中, s.checkPermission使用ReflectPermission("newProxyInPackage.{package name}")权限调用s.checkPermission拒绝访问。
        //NullPointerException – 如果interfaces数组参数或其任何元素为null ,或者调用处理程序h为null
        //请参阅:
        //代理类的包和模块成员资格
        //    @CallerSensitive
        //    public static Object newProxyInstance(ClassLoader loader,
        //                                          Class<?>[] interfaces,
        //                                          InvocationHandler h)

        return (work) Proxy.newProxyInstance(ser.getClass().getClassLoader(), ser.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("proxychains working");
                return method.invoke(ser, args);
            }
        });
    }

    @Test
    void test() {
        var proxy = new ProxyServerFactory().getObject();
        proxy.web();

    }
}
2. cglib动态代理

class CGLibProxyFactory implements MethodInterceptor {
    Server ser = new Server();

    public Server getObject() {
        //创建一个新的Enhancer 。 每个生成的对象都应该使用一个新的Enhancer对象,并且不应跨线程共享。 要创建生成类的其他实例,请使用Factory接口。
        Enhancer enhancer = new Enhancer();

        //设置生成的类将扩展的类。 为方便起见,如果提供的超类实际上是一个接口,则将使用适当的参数调用setInterfaces 。 非接口参数不得声明为 final,并且必须具有可访问的构造函数。
        enhancer.setSuperclass(Server.class);

        //设置要使用的单个Callback 。 如果您使用createClass()则忽略。
        enhancer.setCallback(this);

        //如有必要,生成一个新类并使用指定的回调(如果有)来创建一个新的对象实例。 使用超类的无参数构造函数
        Server newser = (Server) enhancer.create();

        return newser;
    }

    /**
     * 所有生成的代理方法都调用此方法而不是原始方法。 原始方法可以使用 Method 对象通过正常反射调用,也可以使用 MethodProxy(更快)调用。
     *
     * @param o           “this”,增强对象
     * @param method      拦截方法
     * @param objects     参数数组; 原始类型被包装
     * @param methodProxy 用于调用 super(非拦截方法); 可以根据需要多次调用
     * @return 与代理方法的签名兼容的任何值。 返回 void 的方法将忽略此值。
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("这是 intercept ");
        return methodProxy.invoke(ser, objects);
    }

    @Test
    void test() {
        Server ser = new CGLibProxyFactory().getObject();
        ser.web();
    }
}