代理模式(Proxy Pattern)
代理模式是一种结构型设计模式,它为其他对象提供一个代理以控制对该对象的访问。代理是一个具有与原始对象相同的接口的对象,客户端不必知道它与原始对象交互的方式。代理可以拦截对原始对象的访问,并在某些情况下将请求传递给原始对象。 代理模式有两种主要形式:
-
静态代理:在编译时就已经确定了代理类和被代理类之间的关系,通常需要为每个被代理类都编写一个对应的代理类,并实现相同的接口。 静态代理的优点是简单易懂,缺点是不灵活,代码冗余。
-
动态代理:在运行时动态生成代理对象,并根据反射机制调用被代理类的方法。 动态代理可以使用Java原生API或者第三方框架来实现,如JDK Proxy、CGLIB、等。 动态代理的优点是灵活高效,缺点是复杂难懂。
使用场景
代理模式的主要目的是通过代理对象来控制对原始对象的访问,并提供一些额外的功能。比如:
-
当我们想要隐藏某个类时,可以为其提供代理类。例如,我们想要访问一个远程对象,但是不想暴露其网络细节,就可以使用代理类来封装网络通信
-
当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现。例如,我们想要限制某些用户对某些方法的访问,就可以在代理类中进行权限检查。
-
当我们要扩展某个类的某个功能时,可以使用代理模式。 例如,我们想要在调用某个方法之前或之后添加日志、缓存、事务等功能,就可以在代理类中实现
代码实现
静态代理
一个简单的 Java 静态代理例子,它模拟了一个银行账户的操作:
// 定义一个账户接口
public interface Account {
void deposit(double amount);
void withdraw(double amount);
}
// 实现账户接口的类
public class BankAccount implements Account {
private double balance;
public BankAccount(double balance) {
this.balance = balance;
}
public void deposit(double amount) {
balance += amount;
System.out.println("Deposited " + amount + ", balance is now " + balance);
}
public void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
System.out.println("Withdrew " + amount + ", balance is now " + balance);
} else {
System.out.println("Sorry, insufficient balance");
}
}
}
// 定义一个账户代理类
public class AccountProxy implements Account {
private BankAccount bankAccount;
public AccountProxy(BankAccount bankAccount) {
this.bankAccount = bankAccount;
}
public void deposit(double amount) {
bankAccount.deposit(amount);
}
public void withdraw(double amount) {
bankAccount.withdraw(amount);
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
// 创建一个银行账户实例
BankAccount account = new BankAccount(1000.0);
// 创建一个账户代理实例
Account accountProxy = new AccountProxy(account);
// 使用账户代理进行操作
accountProxy.deposit(500.0);
accountProxy.withdraw(200.0);
accountProxy.withdraw(2000.0);
}
}
在上面的例子中,BankAccount 是账户接口 Account 的实现类,它负责实际的存款和取款操作。而 AccountProxy 则是账户代理类,它实现了账户接口,并在其中持有一个 BankAccount 实例。在 AccountProxy 的 deposit 和 withdraw 方法中,它会将操作转发给 BankAc。 在 Main 方法中,客户端使用代理来执行操作,而无需直接操作实际对象。
动态代理
** 使用 Java 内置的 java.lang.reflect.Proxy 类来创建动态代理**
// 定义一个账户接口
public interface Account {
void deposit(double amount);
void withdraw(double amount);
}
// 实现账户接口的类
public class BankAccount implements Account {
private double balance;
public BankAccount(double balance) {
this.balance = balance;
}
public void deposit(double amount) {
balance += amount;
System.out.println("Deposited " + amount + ", balance is now " + balance);
}
public void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
System.out.println("Withdrew " + amount + ", balance is now " + balance);
} else {
System.out.println("Sorry, insufficient balance");
}
}
}
// 定义一个账户代理类
public class AccountProxy implements InvocationHandler {
private BankAccount bankAccount;
public AccountProxy(BankAccount bankAccount) {
this.bankAccount = bankAccount;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("deposit")) {
System.out.println("Before deposit");
method.invoke(bankAccount, args);
System.out.println("After deposit");
} else if (method.getName().equals("withdraw")) {
System.out.println("Before withdraw");
method.invoke(bankAccount, args);
System.out.println("After withdraw");
}
return null;
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
// 创建一个银行账户实例
BankAccount account = new BankAccount(1000.0);
// 创建一个账户代理实例
AccountProxy accountProxy = new AccountProxy(account);
// 使用动态代理创建一个代理对象
Account proxy = (Account) Proxy.newProxyInstance(
Account.class.getClassLoader(),
new Class<?>[] { Account.class },
accountProxy
);
// 使用代理对象进行操作
proxy.deposit(500.0);
proxy.withdraw(200.0);
proxy.withdraw(2000.0);
}
}
BankAccount 和 AccountProxy 类的定义与静态代理例子相同。不同之处在于,我们创建了一个实现了 InvocationHandler 接口的 AccountProxy 类,它的 invoke 方法用来处理代理对象的方法调用。在 invoke 方法中,我们可以根据被调用的方法名称来添加一些额外的功能。在这个例子中,我们为 deposit 和 withdraw 方法添加了前置和后置操作。 在 Main 类中,创建了一个银行账户实例,并将它传递给了一个账户代理实例。然后,我们使用 Proxy.newProxyInstance 方法创建一个动态代理对象,该对象实现了 Account 接口,并在其方法调用时会调用 AccountProxy 的 invoke 方法。最后,我们使用代理对象进行存款和取款操作。
使用小结
代理模式在Java中的应用比较广泛,比如Spring的AOP实现、远程RPC调用等。代理模式可以在不修改原始接口的情况下,对目标对象进行增强或者替换
评论( 0 )