JAVA 代理模式

代理模式的应用场景主要有四种。

  1. 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中(例:WebService, WCF, RPC 之类的)。
  2. 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建(例:浏览器分阶段载入信息,先文字再图片)。
  3. 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限(例:多级权限系统)。
  4. 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果(例:访问频繁时用的缓冲机制)。
  5. 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数、方法的执行记录下来等。

实际上,各种应用场景的实现方式是类似的,下面以智能引用代理场景为例对代理模式进行介绍。假设我们有一个服务是用户登录到网站的操作。其接口及实现类如下。

public interface UserLogin {
	boolean login(User user);
}

public class UserLoginImpl implements UserLogin {

	@Override
	public boolean login(User user) {
		//validateUser 检查是否是合法用户
		if(validateUser(user)) 
			return true;
		return false;
	}

}

现在需要在用户登录前,打印用户登录日志,那么我们可以创建一个 UserLoginProxy 代理类,在其中添加打印日志的操作。代理类需要实现被代理类的接口,这样代理类才能替代被代理类而被客户端调用。

public class UserLoginProxy implements UserLogin {
	private UserLogin userLogin;
	//1. 控制用户对 UserLoginImpl 的访问权限,在代理类中生成被代理对象
	public UserLoginProxy(){
		userLogin = new UserLoginImpl();
	}
	//2. 采用注入的方式得到被代理的对象,Spring AOP 即用注入获得被代理对象
	public UserLoginProxy(UserLogin userLogin){
		this.userLogin = userLogin;
	}
		
	@Override
	public boolean login(User user) {
		System.out.println("User " + user.getUserId() + " is logging in.");
		return userLogin.login(user);
	}
}

客户端调用 UserLogin 的地方改为调用其代理类 UserLoginProxy ,即实现了代理。

public class UserLoginService {
	public boolean consumer(UserLogin userLogin, User user){
		return userLogin.login(user);
	}	
	
	public static void main(String[] args) {
		UserLoginService userLoginService = new UserLoginService();
		
		User user = new User("John Smith");
		UserLogin userLogin = new UserLoginProxy();

		userLoginService.consumer(userLogin, user);
	}
}

代理类 UserLoginProxy 和被代理类 UserLoginImpl 实现相同的接口,从客户端消费者角度来看,二者并没有区别,因此代理类可以出现在原本需要被代理类的地方,达到了记录执行日志的目的。这种手动写出代理类的方式我们称之为静态代理,其实用性并不高,我们不可能对每个需要记录执行日志的类手动创建一个代理类。
Java 提供了动态代理的方式,使得开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。
首先,我们提供一个统一的代理类生成类(实现 InvocationHandler 接口),并在其中写明代理类需要增加的操作(例如打印执行日志)。注意,DynamicProxy 定义的是代理行为而非代理类本身。实际上代理类及其实例是在运行时通过反射动态创建出来的。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class LoggingDynamicProxy implements InvocationHandler {
	//也可以采用注入的方式得到被代理的对象
	private Object proxied;
	public LoggingDynamicProxy(Object proxied){
		this.proxied = proxied;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if("login".equals(method.getName())){
			User user = (User) args[0];
			System.out.println("User "+ user.getUserId() + " is logging in.");
		}else{
			System.out.println("Before " + proxy.getClass().getSimpleName() + " " + method + " method");
		}
		Object rtn = method.invoke(proxied, args);
		return rtn;
	}
}

在客户端消费者需要使用被代理类时,采用如下的方式进行调用。

public class UserLoginService {
	public boolean consumer(UserLogin userLogin, User user){
		return userLogin.login(user);
	}	
	
	public static void main(String[] args) {
		UserLoginService userLoginService = new UserLoginService();
		
		UserLogin userLogin = (UserLogin) Proxy.newProxyInstance(UserLogin.class.getClassLoader(),
		new Class[]{UserLogin.class}, new LoggingDynamicProxy(new UserLoginImpl())) 
		
		User user = new User("John Smith");
		userLoginService.consumer(userLogin, user);
	}
}

不止 UserLogin ,当需要对其它的方法进行执行日志记录时,我们可以用同样的方式,动态生成其代理类。
从以上的例子可以看出,使用 JDK 生成动态代理类要求被代理类至少实现了一个接口,如果被代理类实现了多个接口,可以在 proxy.newProxyInstance() 时对多个接口同时实现代理,使代理类实现相同的每一个接口,这样在每一个用到了被代理类的地方,都能使用代理类替代。
如果被代理类没有实现任何接口,而又需要对其进行代理时,可以借助一个高性能的代码生成库 cglib 来实现。Spring AOP 也引入了 cglib 工具,当被代理类实现了接口时,Spring AOP 使用 JDK 自带的动态代理方式进行 AOP 操作;如果被代理类没有实现任何接口,Spring AOP 则借助于 cglib 生成目标的动态代理类,实现 AOP 操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java代理模式是一种结构型设计模式,其目的是为其他对象提供一种代理以控制对这个对象的访问。代理对象可以在客户端和目标对象之间充当中介,以便于客户端访问目标对象时,可以在不改变目标对象的情况下添加一些额外的功能,比如安全性、远程访问、缓存等。 在Java中,代理模式可以通过两种方式实现:静态代理和动态代理。静态代理需要手动编写代理类,而动态代理可以在运行时通过反射机制动态生成代理类,更加灵活。 举个例子,假设我们有一个接口`Subject`,其中定义了一些方法。我们希望在调用这些方法时,增加一些额外的日志记录功能。我们可以编写一个代理类`SubjectProxy`,在代理类中实现接口方法并调用目标对象的方法,同时在方法前后添加日志记录的代码。客户端则通过代理类访问目标对象。 静态代理示例代码如下: ```java public interface Subject { void doSomething(); } public class RealSubject implements Subject { @Override public void doSomething() { System.out.println("RealSubject do something."); } } public class SubjectProxy implements Subject { private Subject realSubject; public SubjectProxy(Subject realSubject) { this.realSubject = realSubject; } @Override public void doSomething() { System.out.println("Before do something."); realSubject.doSomething(); System.out.println("After do something."); } } public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); Subject subjectProxy = new SubjectProxy(realSubject); subjectProxy.doSomething(); } } ``` 动态代理示例代码如下: ```java public class SubjectHandler implements InvocationHandler { private Object target; public SubjectHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before " + method.getName()); Object result = method.invoke(target, args); System.out.println("After " + method.getName()); return result; } } public class Client { public static void main(String[] args) { Subject realSubject = new RealSubject(); InvocationHandler handler = new SubjectHandler(realSubject); Subject subjectProxy = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler); subjectProxy.doSomething(); } } ``` 无论是静态代理还是动态代理代理模式都可以在不改变目标对象的情况下,为其添加额外的功能,提高代码的可复用性和灵活性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值