代理模式有两种
静态代理:需要为每个目标类写个代理类,代理类中需要引用目标类来编码实线
1)定义一个接口及其实现类;
2)创建一个代理类同样实现这个接口将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。
这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
动态代理:jdk api和cglib两种
jdk动态代理
jdk api就是写一个代理类,这个代理类,使用jdk提供的java.lang.reflect Proxy和java.lang.reflect InvocationHandler来获取代理以及传入被代理的方法,这样就是不用直接引用被代理对象,可以在使用处以参数的方式传入被代理对象的方法和参数。
动态代理的实现方式
JDK的动态代理是通过Proxy类和InvocationHandler接口来实现的。InvocationHandler接口中定义了一个invoke方法,用于处理代理方法的调用。当目标对象实现了接口时,可以通过Proxy类的newProxyInstance方法动态生成代理类,并将InvocationHandler对象传入,从而实现对目标对象方法的动态代理。
为什么jdk动态代理只能代理实现接口的类,不能代理普通类
动态代理实际上是程序在运行中
- 根据被代理的接口来动态生成代理类的 class 文件,
- 并加载 class 文件运行的过程,
- 通过反编译被生成的 $Proxy0.class 文件发现
public final class $Proxy0 extends Proxy implements Interface { public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } // 该方法为被代理接口的业务方法,代理类都会自动生成相应的方法,里面去执行invocationHandler 的invoke方法。 public final void sayHello(String paramString) { try { this.h.invoke(this, m3, new Object[] { paramString }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } }
java 是单继承
动态生成的代理类已经继承了 Proxy 类的,就不能再继承其他的类,
所以只能靠实现被代理类的接口的形式,故 JDK 的动态代理必须有接口。
为何调用代理类的方法就会自动进入 InvocationHandler 的 invoke()方法呢?因为 invoke 方法中利用 jdk 反射的方式去调用真正的被代理类的业务方法,
而且还可以在方法的前后去加一些我们自定义的逻辑
JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口(就是一个类它要先定义一个接口再定义一个接口实现才能使用动态代理,没有定义接口的类用不了jdk动态代理)。 如果想代理没有实现接口的类,就可以使用CGLIB实现。
CGLIB动态代理
2)CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。 它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。 CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。’
CGLib 动态代理可以代理普通的类。CGLib 动态代理采用继承方式来实现代理,在运行时动态生成一个被代理类的子类,被代理类的方法作为子类的方法被调用,通过这种方式来实现对被代理对象的代理。被代理的类不能是final类。
-
静态代理在编译时产生class字节码文件,可以直接使用,效率高。
-
动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
-
cglib代理无需被代理类实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。
静态代理和动态代理的对比
-
灵活性:动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!JVM 层面:静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。