代理模式
是什么?
代理模式为某对象创建一个代理对象,让代理对象控制该对象的访问。被代理对象可以使远程的对象、创建开销大的对象或需要安全控制的对象。
结构组成及作用
下图是代理模式的UML类图:
作用
《head first Design Patterns》和 Gang of Four的《设计模式》 [1] 两本书中,都描述了三种代理形式:远程代理、虚拟代理、保护代理。 从这三种代理可窥探出代理模式的作用:
- 提供一种不同空间的局部代理。
- 根据需要创建开销大的对象。
- 控制对ConcreteSubject对象(如图,即被代理对象)的访问。
其实这三种形式的类图结构都有所不同,事实上,是存在很多代理模式的变体的,但是,其实我们要记得的是他们行为的共通点是: 将客户(Caller)对ConcreteSubject对象(如图,即被代理对象)的直接访问拦截下来,变为间接。那么这个行为将提供给我们在其中很大的操作空间。
示例实现
下面我将提供 远程代理、虚拟代理、保护代理三种形式的实现。
虚拟代理
觉得《head first设计模式》的例子挺好,本来想试着用javaFx模拟实现一下,后来做了下发现javaFx的Image加载图片时是不会假死,所以就照这书里的swing实现做了一次。
代码不贴了,贴两个效果图,可以看到通过代理模式,图片实现延迟及异步加载了。
保护代理
保护代理有两种:一种是按照上面类图实现的静态代理,一种是我们喜闻乐见的Java提供Proxy API的作为Spring AOP原理的动态代理。
下面模拟一般业务后端给DAO或Service的代理。
公用类
User类及其Builder
public class User {
private String id;
private String name;
private String description;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
public class UserBuilder {
private String id;
private String name;
private String description;
public static UserBuilder create() {
return new UserBuilder();
}
public UserBuilder id(String id) {
this.id = id;
return this;
}
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder description(String description) {
this.description = description;
return this;
}
public UserBuilder defaultValue() {
id = "default";
name = "default_name";
description = "default_description";
return this;
}
public User build() {
User user = new User();
user.setId(id);
user.setName(name);
user.setDescription(description);
return user;
}
}
DAO实现类
public class UserDAOImpl implements UserDAO {
@Override
public void save(User user) {
System.out.println("user saved!!");
}
@Override
public void delete() {
System.out.println("user deleted!!!");
}
}
静态代理
import com.tea.proxy.protectProxy.User;
import com.tea.proxy.protectProxy.UserDAO;
public class ProtectProxy implements UserDAO {
private UserDAO userDAO;
public ProtectProxy(UserDAO userDAO) {
this.userDAO = userDAO;
}
@Override
public void save(User user) {
System.out.println("before saving user!some Operation!");
userDAO.save(user);
}
@Override
public void delete() {
System.out.println("before deletion!some Operation!");
userDAO.delete();
}
}
import com.tea.proxy.protectProxy.User;
import com.tea.proxy.protectProxy.UserBuilder;
import com.tea.proxy.protectProxy.UserDAO;
import com.tea.proxy.protectProxy.UserDAOImpl;
public class TestMain {
public static void main(String[] args) {
UserDAO userDAO = new UserDAOImpl();
UserDAO userDAOProxy = new ProtectProxy(userDAO);
User user = UserBuilder.create().defaultValue().build();
userDAOProxy.save(user);
userDAOProxy.delete();
}
}
动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProtectedProxy implements InvocationHandler {
private Object target;
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public void beforeMethod(Method m) {
System.out.println(m.getName() + " start");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod(method);//对特定方法做一些类似事务,验证等的操作
method.invoke(target, args);
return null;
}
}
import com.tea.proxy.protectProxy.User;
import com.tea.proxy.protectProxy.UserBuilder;
import com.tea.proxy.protectProxy.UserDAO;
import com.tea.proxy.protectProxy.UserDAOImpl;
import java.lang.reflect.Proxy;
public class TestMain {
public static void main(String[] args) {
UserDAO userDAO = new UserDAOImpl();
ProtectedProxy pp = new ProtectedProxy();
pp.setTarget(userDAO);
UserDAO userDAOProxy = (UserDAO) Proxy.newProxyInstance(userDAO.getClass().getClassLoader(), userDAO.getClass().getInterfaces(), pp);
User user = UserBuilder.create().build();
userDAOProxy.save(user);
userDAOProxy.delete();
}
}
远程代理
把远程代理放到这几个代理之后是因为远程代理还有我没能解决的rmi的报错问题。放在这里希望有熟悉rmi的有缘人帮下忙。
我在github的remoteProxy目录里分别放了三个demo:
- 深入浅出设计模式里的demo
- 一个小demo
- oracle java文档 rmi getting Started的helloworld demo, oracle doc|Getting Started Using Java RMI。
翻了不少资料,也debug了好几个最后几个demo基本都卡在了这个exception(stackoverflow 问题 | java.rmi.ConnectException: Connection refused to host: 127.0.1.1; )上,但是问题下的解答并不能解决我的问题。我在网上也发了问题,望有人能解答。
效果及应用场景
见作用中的说明。
references
[1] Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides.设计模式:可复用面向对象软件的基础[M].中国:机械工业出版社,2000