代理模式主要为其他对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,我们可以在代理类中做一些辅助逻辑,把一些次要逻辑与主分隔逻辑开来,减少重复代码,比如日志记录,事务的管理等等。
静态代理
既然说到代理,自然联想到静态代理。下面我们就先从简单的开始,下面数据库操作为例,用Java代码实现静态代理。
代理模式中涉及到的角色
抽象角色:为真实对象和代理对象提供一个共同的接口,一般是抽象类或者接口。
代理角色:代理角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能够代替真实对象。同时,代理对象可以在执行真实对象的操作时,附加其他操作,相当于对真实对象的功能进行拓展。
真实角色:最终引用的对象。
public interface MYSQLUserDao {
void insert();
void update();
}
public class UserDaoImpl implements IUserDao {
public void insert() {
System.out.println("插入数据");
}
public void update() {
System.out.println("更新数据");
}
}
public class UserDaoProxy implements IUserDao {
private IUserDao userDao;
public UserDaoProxy(IUserDao userDao) {
this.userDao = userDao;
}
public void insert() {
System.out.println("开启事务");
userDao.insert();
System.out.println("关闭事务");
}
public void update() {
System.out.println("开启事务");
userDao.update();
System.out.println("关闭事务");
}
}
public class Client {
public static void main(String[] args) {
IUserDao userDao = new UserDaoImpl();
IUserDao userDaoProxy = new UserDaoProxy(userDao);
userDaoProxy.insert();
}
}
效果
如上所示,开发人员只需要负责数据库的操作(主逻辑),而事务都交由代理类来管理。如果被代理的类都实现IUserDao接口,比如UserDaoImpl2等,可以部分消除公共代码,但是在update()方法中出现了公共代码,并且在实际的开发中,我们的被代理类通常实现不同接口,这时候就要使用动态代理了。
动态代理
静态代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
静态代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为IUserDao类的访问提供了代理,但是如果还要为其他类如IOrderDao类提供代理的话,就需要我们再次添加代理IOrderDao的代理类。
//订单代理
public interface IOrderDao {
void insert();
void update();
}
public class OrderDaoImpl implements IOrderDao {
public void insert() {
System.out.println("插入订单");
}
public void update() {
System.out.println("更新订单");
}
}
public class OrderDaoProxy implements IOrderDao {
private IOrderDao orderDao;
public OrderDaoProxy(IOrderDao orderDao) {
this.orderDao = orderDao;
}
public void insert() {
System.out.println("开启事务");
orderDao.insert();
System.out.println("关闭事务");
}
public void update() {
System.out.println("开启事务");
orderDao.update();
System.out.println("关闭事务");
}
}
这个时候就需要动态代理了。
JDK动态代理的实现关键在于java.lang.reflect.Proxy类,其newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)方法是整个JDK动态代理的核心,用于生成指定接口的代理对象。这个方法有三个参数,
- ClassLoader:表示加载动态生成的代理类的类加载器ClassLoader (因为需要加载的是我们自己写的类所以这里传入的类加载器通常都是系统类加载器)。
- Class<?>[]:代理类需要实现的接口interfaces(我们的类可能实现多个接口)
- InvocationHandler:调用处理器InvocationHandler(JDK规范,代理类必须实现这个接口)
代码如下:
public class TransactionHandler implements InvocationHandler {
private Object target;
public Object getProxyInstance(Object target){
this.target = target;
return Proxy.newProxyInstance(this.getClass().getClassLoader() , target.getClass().getInterfaces() , this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
method.invoke(target , args);
System.out.println("关闭事务");
return null;
}
public class Client {
public static void main(String[] args) {
IUserDao userDao = new UserDaoImpl();
IOrderDao orderDao = new OrderDaoImpl();
TransactionHandler transactionHandler = new TransactionHandler();
IUserDao iUserDao = (IUserDao) transactionHandler.getProxyInstance(userDao);
iUserDao.insert();
IOrderDao iOrderDao = (IOrderDao) transactionHandler.getProxyInstance(orderDao);
iOrderDao.insert();
}
}
效果