代理模式是软件设计模式中的一种,它提供了一个代理对象来代表另一个对象。这样做的目的是为了控制对原始对象的访问,即在不直接暴露原始对象的情况下,通过代理对象间接操作原始对象。代理模式有多种实现方式,其中最常见的是静态代理和动态代理。
静态代理
在静态代理中,代理类是预先定义好的,它与被代理类实现相同的接口,并且在内部持有被代理类的引用。由于代理类和被代理类需要实现相同的接口,因此如果接口有很多方法,会导致代理类有大量重复的代码。
动态代理
动态代理不需要在代码中显式编写代理类,而是在程序运行时,通过反射机制动态创建。Java 中的 java.lang.reflect.Proxy
类和 java.util.function.Function
接口可以用来创建动态代理对象。动态代理允许在运行时指定一个或多个接口,然后为这些接口的方法创建代理方法。
区别:动态代理更加灵活,可以在运行时动态地创建代理类,适应不断变化的需求。静态代理由于是编译时生成的,可能在性能上略优于动态代理,后者需要在运行时动态生成代理类。
目标类把要代理的方法放到接口中,代理类和目标类实现同一个接口,然后代理类通过构造方法和目标类建立联系,在业务中通过目标类对象调用目标方法的同时,代理类可以执行自己的逻辑。
提供公共接口 Calculator
public interface Calculator {
int add(int i,int j);
}
准备目标类,可以看到目标类仅仅做了加法运算。
public class CalculatorImpl implements Calculator{
@Override
public int add(int i, int j) {
return i + j;
}
}
准备代理类,实现同一个接口 Calculator 并且引用目标类
public class StaticProxyCalculator implements Calculator {
private Calculator calculator;
public StaticProxyCalculator(Calculator calculator) { // 与目标类建立联系
this.calculator = calculator;
}
@Override
public int add(int i, int j) {
System.out.println("i = " + i + ", j = " + j);
int result = calculator.add(i, j);
System.out.println("result = " + result);
return result;
}
}
现在我们创建客户端
public class Client {
public static void main(String[] args) {
Calculator calculator = new CalculatorImpl(); // 创建目标类
Calculator proxy = new StaticProxyCalculator(calculator); // 创建代理类
proxy.add(1, 1); // 代理去做事
}
}
输出结果:从输出的结果可以发现,代理类做的事情不仅完成了目标类的功能,还在此的基础上做出了打印输出,起到一定程度上的增强效果。
i = 1, j = 1
result = 2
总结:一句话说静态代理的优缺点就是简单,但是麻烦。
动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。我们这里就是用 Java 反射包下原生的代理工具类即可。其他的还有 CGLIB 代理。
示例:假设我们有一位女明星,她负责唱歌和跳舞,现在她要把这两项工作代理给助手负责准备话筒和场地。
准备一个公用接口,该接口暴露出去的是准备代理的方法。
public interface Star {
String sing(String singName);
void dance();
}
准备女明星类,负责唱歌和跳舞
public class BigStar implements Star {
private String name;
public BigStar(String name) {
this.name = name;
}
@Override
public String sing(String singName) {
System.out.println(this.name + "正在唱:" + singName);
return "谢谢";
}
@Override
public void dance() {
System.out.println(this.name + "正在跳优美的舞蹈");
}
}
准备代理的工具类,负责代理 Star 中的方法
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("sing")) {
System.out.println("准备话筒"); // 代理唱歌
}
if (method.getName().equals("dance")) {
System.out.println("准备跳舞"); // 代理跳舞
}
return method.invoke(bigStar, args); // 女明星自己去唱歌跳舞
}
});
return starProxy; // 返回代理好的结果
}
}
客户端:
public class Client {
public static void main(String[] args) {
BigStar bigStar = new BigStar("杨超越"); // 创建女明星对象
Star starProxy = ProxyUtil.createProxy(bigStar); // 代理后的结果
// 代理后的唱歌
System.out.println(starProxy.sing("燃烧卡路里"));
// 代理后的跳舞
starProxy.dance();
}
}
输出结果:
准备话筒
杨超越正在唱:燃烧卡路里
谢谢
准备跳舞
杨超越正在跳优美的舞蹈
静态代理比动态代理更符合OOP原则,在日常开发中使用也较多。动态代理在开发框架时使用较多,例如大名鼎鼎的 Spring
。
动态代理在AOP中运用比较广泛,比如日志记录,性能统计,安全控制,事务处理,异常处理等场景下。