代理模式-MySql代理

334 阅读3分钟

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战


1. MySql代理

后端开发人员避免不了要和数据库打交道,MySQL就是时下非常流行的关系型数据库。 假设现有一个SQL语句执行器,它支持两种操作:查询和写入。 在这里插入图片描述

interface SqlExecutor {

	// 读操作
	Object query(String sql);

	// 写操作
	int update(String sql);
}

class MySqlExecutor implements SqlExecutor{
    ......
}

现在有一个新的需求,需要将SqlExecutor执行的SQL语句输出到控制台,以便调试。 你要怎么做?直接修改MySqlExecutor吗?这不符合开闭原则。

正确的做法应该是创建一个代理类LogProxy,由它来完成SQL语句的输出。 在这里插入图片描述

public class LogProxy implements SqlExecutor {
	private SqlExecutor target;
	public LogProxy(SqlExecutor executor) {
		this.target = executor;
	}

	@Override
	public Object query(String sql) {
		System.out.println("查询sql:" + sql);
		return target.query(sql);
	}

	@Override
	public int update(String sql) {
		System.out.println("修改sql:" + sql);
		return target.update(sql);
	}
}

现在又有一个新的需求,对于重复的SQL查询,可以将查询结果缓存起来,下次再查询时直接从缓存中获取结果。 又去改SqlExecutor源码?别忘了,那不符合开闭原则,通过代理对象来增强即可。 在这里插入图片描述

class CacheProxy implements SqlExecutor {
	private Map<String, Object> cache = new ConcurrentHashMap<>();
	private SqlExecutor target;

	public CacheProxy(SqlExecutor executor) {
		this.target = executor;
	}

	@Override
	public Object query(String sql) {
		if (cache.containsKey(sql)) {
			System.out.println("命中缓存...");
			return cache.get(sql);
		}
		Object result = target.query(sql);
		cache.put(sql, result);
		return result;
	}

	@Override
	public int update(String sql) {
		System.out.println("缓存失效...");
		cache.clear();
		return target.update(sql);
	}
}

这就是代理模式!

2. 代理模式的定义

为其他对象提供一种代理以控制对这个对象的访问。

在这里插入图片描述

代理模式通用类图
  • Subject:主题抽象,RealSubject和Proxy负责实现。
  • RealSubject:具体主题,业务的真正执行者,也是被代理对象。
  • Proxy:代理类,负责对RealSubject做增强,原有逻辑还是让RealSubject去执行。

一个代理类具体要代理哪个对象,是由场景类来确定的,一般是通过构造函数来指定被代理对象。

3. 代理模式的优点

  1. 职责非常清晰,被代理对象只负责自己的业务逻辑,不用关心的或非本职责的事情都交给代理类去完成,符合单一职责原则。
  2. 扩展性非常好,只需创建一个代理类就可以增强功能,代理对象还可以代理另一个代理对象,以实现增强功能的传递。
  3. 对于功能增强,代理有着比继承更好的灵活性。

如果你要对类进行功能增强,但是这部分功能又不是该类本身的职责范围内的事情,就可以通过创建一个代理类的方式来做。

4. JDK动态代理

上述例子属于普通代理实现,它需要开发者编写代理类,实现和被代理类实现的接口,并重写所有方法,然后将方法的执行转发到被代理对象,如果需要实现的方法非常多,还是比较麻烦的。 JDK提供了动态代理的支持,使用ProxyInvocationHandler就可以为被代理对象动态的生成一个代理对象,使用非常方便。

新建一个类实现InvocationHandler接口,重写invoke方法,通过Proxy.newProxyInstance()创建代理对象。

JDK动态代理应用场景非常的广泛,在许多优秀的开源框架中都能看到它的身影,例如MyBatis通过接口操作数据库,就用到了JDK动态代理。

注:JDK动态代理要求类必须实现接口!!!

5. 总结

代理模式应用的非常广泛,最经典的例子就是Spring的AOP技术了,它可以在不修改源码的情况下对系统进行功能的增强,底层就是通过生成代理对象来完成的,只要你调试的时候发现代理类的类名为$Proxy0就说明它是一个代理对象。 一句话总结就是,如果你需要对类进行增强,但是增强的功能不属于类本身的职责范畴,就可以考虑使用代理模式来实现。