Java 静态代理与动态代理全面讲解
一、代理模式的作用
Java中代理模式是一种常见的设计模式,代理模式可以在不改变原有代码的情况下增强类的功能。代理模式包括静态代理和动态代理两种形式,AOP的底层机制就是动态代理。
代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
下面将对两种代理模式进行全面讲解。
二、静态代理
静态代理是在编译期间已经确定了代理类的代码,代理类和被代理类都实现了同一个接口或者是继承自同一个父类。静态代理的优点是编写简单,易于理解和维护。但是,如果需要代理多个类,代理类的数量会增加,并且在代理类和被代理类的方法增加时需要手动维护代理类的代码。
下面是一个简单的静态代理示例:
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject:request()");
}
}
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
System.out.println("ProxySubject:before request()");
realSubject.request();
System.out.println("ProxySubject:after request()");
}
}
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.request();
}
}
在上述示例中,RealSubject
是被代理类,ProxySubject
是代理类,代理类实现了 Subject
接口,并且持有被代理类的实例 RealSubject
。在代理类的 request()
方法中,调用了被代理类的 request()
方法,并在前后添加了自己的处理逻辑。
三、动态代理
动态代理是在运行时动态生成代理类的代码,不需要事先知道代理类的代码。Java中的动态代理主要有两种形式,一种是基于接口的动态代理,另一种是基于类的动态代理。
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法
基于接口的动态代理
基于接口的动态代理是指代理类和被代理类实现了同一个接口,代理类在运行时动态生成,不需要手动编写代理类的代码。
在 Java 动态代理机制中 InvocationHandler
接口和 Proxy
类是核心。
Proxy
类中使用频率最高的方法是:newProxyInstance()
,这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
}
这个方法一共有 3 个参数:
- loader :类加载器,用于加载代理对象。
- interfaces : 被代理类实现的一些接口;
- h : 实现了
InvocationHandler
接口的对象;
要实现动态代理的话,还必须需要实现InvocationHandler
来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler
接口类的 invoke
方法来调用。
public interface InvocationHandler {
/**
* 当你使用代理对象调用方法的时候实际会调用到这个方法
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
invoke()
方法有下面三个参数:
- proxy :动态生成的代理类
- method : 与代理类对象调用的方法相对应
- args : 当前 method 方法的参数
也就是说:你通过Proxy
类的 newProxyInstance()
创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler
接口的类的 invoke()
方法。 你可以在 invoke()
方法中自定义处理逻辑,比如在方法执行前后做什么事情。
下面是一个基于接口的动态代理示例:
public interface Subject {
void request();
}
// 被代理类
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject:request()");
}
}
// 解决问题二
public class DynamicProxySubject implements InvocationHandler {
private Object realSubject;
public DynamicProxySubject(Object realSubject) {
this.realSubject = realSubject;
}
//当我们通过代理类的对象,调用方法request时,就会自动的调用如下的方法: invoke()
//将被代理类要执行的方法的功能就言明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method: 即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//realSubject: 被代理类的对象
System.out.println("DynamicProxySubject:before " + method.getName() + "()");
Object result = method.invoke(realSubject, args);
System.out.println("DynamicProxySubject:after " + method.getName() + "()");
//上述方法的返回值就作为当前类中的invoke()的返回值。
return result;
}
}
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
DynamicProxySubject dynamicProxySubject = new DynamicProxySubject(realSubject);
//调用此方法,返回一个代理类的对象。解决问题一
Subject proxySubject = (Subject)Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), dynamicProxySubject);
proxySubject.request();
}
}
在上述示例中,`RealSubject` 是被代理类,`DynamicProxySubject` 是代理类,代理类实现了 `InvocationHandler` 接口,并在代理类的 `invoke()` 方法中调用了被代理类的相应方法,并在前后添加了自己的处理逻辑。在客户端代码中,通过调用 `Proxy.newProxyInstance()` 方法动态生成代理类的实例。
基于类的动态代理
基于类的动态代理是指代理类和被代理类都是类,代理类在运行时动态生成,不需要手动编写代理类的代码。Java中的基于类的动态代理主要是通过字节码生成框架来实现的,比较常用的字节码生成框架有CGLIB和ASM。 下面是一个基于类的动态代理示例(使用CGLIB实现):
public class RealSubject {
public void request() {
System.out.println("RealSubject:request()");
}
}
public class DynamicProxySubject implements MethodInterceptor {
private Object realSubject;
public DynamicProxySubject(Object realSubject) {
this.realSubject = realSubject;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("DynamicProxySubject:before " + method.getName() + "()");
Object result = proxy.invoke(realSubject, args);
System.out.println("DynamicProxySubject:after " + method.getName() + "()");
return result;
}
}
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new DynamicProxySubject(new RealSubject()));
RealSubject proxySubject = (RealSubject) enhancer.create();
proxySubject.request();
}
}
在上述示例中,RealSubject
是被代理类,DynamicProxySubject
是代理类,代理类实现了 MethodInterceptor
接口,并在代理类的 intercept()
方法中调用了被代理类的相应方法,并在前后添加了自己的处理逻辑。在客户端代码中,通过调用 CGLIB 提供的 Enhancer
类来生成代理类的实例。
四、总结
静态代理和动态代理都是代理模式的具体实现,它们都可以增强类的功能,但是实现方式不同。静态代理在编译期间已经确定了代理类的代码,适合代理少量的类;动态代理在运行时动态生成代理类的代码,适合代理大量对象或者不确定代理哪个类的情况。
Java中的静态代理需要手动编写代理类的代码,不够灵活,但是效率较高;动态代理不需要手动编写代理类的代码,比较灵活,但是效率较低。
Java中的静态代理和动态代理都是基于接口的代理,因此被代理类必须实现接口。
动态代理在Java中是通过反射机制实现的,反射机制会带来一定的性能开销。另外,由于动态代理是在运行时动态生成的代理类,因此不易调试。
在实际应用中,可以根据具体的情况选择静态代理或动态代理,比如需要代理的类数量、代理类的结构、代理类的复杂度、代理类的生命周期等因素都可以影响选择。