Java 代理模式是一种常见的设计模式,它可以在不改变原有代码的情况下,通过代理对象来控制访问原有对象。代理模式常用于添加额外的功能或者限制对原有对象的访问。
在 Java 中,代理模式可以通过接口代理和类代理两种方式实现。
接口代理也称为动态代理,它是基于 Java 反射机制实现的。通过代理工厂类创建一个实现了原有接口的代理类,当调用代理类的方法时,会被重定向到实际对象的方法中。
示例代码如下:
public interface ISubject {
void request();
}
public class RealSubject implements ISubject {
@Override
public void request() {
System.out.println("RealSubject request.");
}
}
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("before...");
Object returnValue = method.invoke(target, args);
System.out.println("after...");
return returnValue;
}
);
}
}
public class Main {
public static void main(String[] args) {
ISubject realSubject = new RealSubject();
ISubject proxy = (ISubject) new ProxyFactory(realSubject).getProxyInstance();
proxy.request();
}
}
在上面的示例代码中,RealSubject
类实现了 ISubject
接口,它是代理的实际对象。ProxyFactory
类是代理工厂类,它创建代理对象并返回。
当调用代理对象的 request()
方法时,会先输出 “before…”,然后调用实际对象的 request()
方法,最后输出 “after…”。这样,我们就可以在代理对象中添加额外的功能,而不会改变实际对象的行为。
类代理也称为静态代理,它是通过代理类继承或实现原有类或接口实现的。代理类重写原有类或接口的方法,并在方法中调用实际对象的方法,从而实现代理。
示例代码如下:
public interface ISubject {
void request();
}
public class RealSubject implements ISubject {
@Override
public void request() {
System.out.println("RealSubject request.");
}
}
public class ProxySubject implements ISubject {
private ISubject realSubject;
public ProxySubject(ISubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
System.out.println("before...");
realSubject.request();
System.out.println("after...");
}
}
public class Main {
public static void main(String[] args) {
ISubject realSubject = new RealSubject();
ISubject proxy = new ProxySubject(realSubject);
proxy.request();
}
}
上面这段代码实现了代理模式的类代理一个示例,其具体含义如下:
ISubject
是一个接口,其中定义了一个 request()
方法,代表某个操作。RealSubject
是 ISubject
接口的实现类,实现了 request()
方法,具体实现为输出 “RealSubject request.”。ProxySubject
是 ISubject
接口的代理类,它包含了一个 realSubject
成员变量,该变量指向一个 ISubject
类型的真实主题(即被代理的对象)。在 ProxySubject
中,重写了 request()
方法,在真实主题的操作前后添加了一些额外的操作,具体实现为输出 “before…” 和 “after…”。Main
类中,创建了一个 RealSubject
对象 realSubject
,并使用它创建了一个代理对象 proxy
,即 ProxySubject
。在调用 proxy
的 request()
方法时,会先输出 “before…”,然后调用真实主题的 request()
方法,即 RealSubject
的 request()
方法,最后输出 “after…”。因此,通过代理对象可以在不修改真实主题的情况下,对其进行增强或控制。代理模式常用于控制对一个对象的访问,它可以使得代理对象控制对真实主题的访问,并在访问前后执行一些额外的操作,如安全检查、性能优化、日志记录等。
是的,动态代理通常都是基于 Java 反射机制实现的。
在 Java 中,我们可以使用 java.lang.reflect.Proxy
类来创建代理对象,该类提供了一个 newProxyInstance()
方法,用于创建一个实现了指定接口的代理对象。
该方法接收三个参数:
invoke()
方法中。在 invoke()
方法中,我们可以通过反射机制调用实际对象的方法,并添加一些额外的功能。
因此,动态代理通常都是通过反射机制实现的。
JDK 动态代理和 CGLIB 动态代理都是常见的 Java 动态代理方式,它们的区别主要有以下几点:
JDK 动态代理是基于接口的代理方式,它要求代理类实现与目标类相同的接口。代理类在运行时通过反射机制动态生成,它只能代理接口,而不能代理类。
CGLIB 动态代理是基于继承的代理方式,它要求目标类不能被 final 修饰,代理类会继承目标类,并重写其中的方法。代理类在运行时通过 ASM 字节码生成框架动态生成,它既可以代理接口,也可以代理类。
JDK 动态代理在生成代理类时,需要实现接口并通过反射机制调用目标方法,因此在处理速度和效率上比 CGLIB 动态代理慢一些。但是,JDK 动态代理不会生成代理类的字节码,因此占用内存更小。
CGLIB 动态代理在生成代理类时,直接继承目标类并重写其中的方法,因此在处理速度和效率上比 JDK 动态代理快一些。但是,CGLIB 动态代理会生成代理类的字节码,并在内存中保存,因此占用内存更大。
JDK 动态代理只能代理实现了接口的类,不能代理没有实现接口的类。而 CGLIB 动态代理可以代理任何没有被 final 修饰的类。
总的来说,JDK 动态代理和 CGLIB 动态代理各有优缺点,选择哪种代理方式取决于具体应用场景。如果目标类实现了接口,建议使用 JDK 动态代理;如果目标类没有实现接口或者需要代理的方法比较多,建议使用 CGLIB 动态代理。