JDK代理 & CGLib

date
Jul 26, 2021
slug
JavaProxy
status
Published
tags
Java Proxy
summary
JDK静态代理、JDK动态代理、CGLib代理的学习笔记
type
Post
Language

介绍

个人认为 Java 中的代理和 Nginx 的代理从意思上说都差不多,都是把目标转发到代理对象上,隐藏原始目标或对目标进行拦截或增强

准备

public interface helloInterface{
	public void say();
}
public class hello implements helloInterface{
	@Override
	public void say(){
		System.out.println("hello");
	}
}

JDK静态代理

从 Java 语言特性上实现业务层面上的代理
public class helloStationaryProxy implements helloInterface{
	private Hello hello;
	@Override
	public void say(){
		if (hello == null) {
        hello = new Hello();
    }
    hello.say();
	}
}

HelloStaticProxy helloStaticProxy = new HelloStaticProxy();
helloStaticProxy.say();

JDK动态代理

代理类实现 InvocationHandler 接口,通过 Proxy 类的静态方法 newProxyInstance 传入代理类的类加载器和被代理对象的接口动态的创建代理类实现
public class HelloDynamicProxy implements InvocationHandler {

    private Object subject;

    public HelloDynamicProxy(Object subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(subject, args);
        return null;
    }
}
Hello hello = new Hello();
InvocationHandler handler = new HelloDynamicProxy(hello);
// 传入handler的类加载器和被代理类的接口,以此动态创建代理类
HelloInterface helloInterface = (HelloInterface) Proxy.newProxyInstance(
        handler.getClass().getClassLoader(),
        hello.getClass().getInterfaces(),
        handler
);
helloInterface.say();

CGLib代理

底层把方法全部放入数组,通过数组索引直接调用方法
public class HelloMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object object = methodProxy.invokeSuper(o, objects);
        return object;
    }
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
enhancer.setCallback(new HelloMethodInterceptor());
HelloInterface helloInterface1 = (HelloInterface) enhancer.create();
helloInterface1.say();

Spring AOP实现

浅析Spring中AOP的实现原理--动态代理
Spring的 AOP实现原理其实很简单,就是通过 动态代理实现的。如果我们为 Spring的某个 bean配置了切面,那么 Spring在创建这个 bean的时候,实际上创建的是这个 bean的一个代理对象,我们后续对 bean中方法的调用,实际上调用的是代理类重写的代理方法。而 Spring的 AOP使用了两种动态代理,分别是 JDK的动态代理,以及 CGLib的动态代理 。 (一)JDK动态代理    Spring默认使用JDK的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理。熟悉 Java语言的应该会对 JDK动态代理有所了解。 JDK实现动态代理需要两个组件,首先第一个就是 InvocationHandler接口。我们在使用 JDK的动态代理时,需要编写一个类,去实现这个接口,然后重写 invoke方法,这个方法其实就是我们提供的代理方法。然后 JDK动态代理需要使用的第二个组件就是 Proxy这个类,我们可以通过这个类的 newProxyInstance方法,返回一个代理对象。生成的代理类实现了原来那个类的所有接口,并对接口的方法进行了代理,我们通过代理对象调用这些方法时,底层将通过反射,调用我们实现的 invoke 方法。 (二)CGLib动态代理   以上就是 Spring 实现动态的两种方式,下面我们具体来谈一谈这两种生成动态代理的方式。 (一)实现原理    JDK的动态代理是基于 反射实现。 JDK通过反射,生成一个代理类,这个代理类实现了原来那个类的全部接口,并对接口中定义的所有方法进行了代理。当我们通过代理对象执行原来那个类的方法时,代理类底层会通过反射机制,回调我们实现的 InvocationHandler接口的 invoke方法。 并且这个代理类是Proxy类的子类(记住这个结论,后面测试要用)。这就是 JDK 动态代理大致的实现方式。 (二)优点 JDK动态代理是JDK原生的,不需要任何依赖即可使用; 通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快; (三)缺点 如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理; JDK动态代理无法为没有在接口中定义的方法实现代理,假设我们有一个实现了接口的类,我们为它的一个不属于接口中的方法配置了切面,Spring仍然会使用JDK的动态代理,但是由于配置了切面的方法不属于接口,为这个方法配置的切面将不会被织入。 JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低; (一)实现原理
  • Spring 默认使用 JDK动态代理 实现AOP,SpringBoot 使用 CGLib
  • 如果被代理的类实现了接口就使用 JDK动态代理 ,如果被代理的类没有实现接口就使用 CGLib
  • 使用 CGLib 实现 AOP,被代理的类方法为 final 就无法实现代理,被代理类为 final 会报错

实例

 

© chobit blog 2025