引言
我们都知道,数据库连接是很珍贵的资源,频繁的开关数据库连接是非常浪费服务器的CPU资源以及内存的,所以我们一般都是使用数据库连接池来解决这一问题,即创造一堆等待被使用的连接,等到用的时候就从池里取一个,不用了再放回去,数据库连接在整个应用启动期间,几乎是不关闭的,除非是超过了最大闲置时间。
但是在程序员编写程序的时候,会经常使用connection.close()这样的方法,去关闭数据库连接,而且这样做是对的,所以你并不能告诉程序员们说,你们使用连接都不要关了,去调用一个其他的类似归还给连接池的方法吧。这是不符合程序员的编程思维的,也很勉强,而且具有风险性,因为程序员会忘的。
解决这一问题的办法就是使用代理模式,因为代理模式可以替代原有类的行为,所以我们要做的就是替换掉connection的close行为。
什么是代理模式
代理模式——就是给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理对象在客户端和目标对象之间起到中介作用。在一些情况下,一个客户不想或者不能直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。例如电脑桌面的快捷方式就是一个代理对象,快捷方式是它所引用的程序的一个代理。
代理模式的结构
1、Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
2、Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。
3、RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。
代理模式和装饰模式的异同
代理模式和装饰模式的代码实现方式很相同,主要不同点是代理模式关注与被代理对象行为的控制,然而装饰模式关注于在一个对象上动态的添加方法。
代理模式可以对客户端隐藏被代理对象的具体实现,代理模式的时候常常是在一个代理类中创建一个对象的实例,当使用装饰模式的时候,将原始对象转为一个参数传递给装饰者的构造器中
代理模式强调的是限制,装饰模式强调的是增强
代理模式和委托
代理:是把一些事情交给某人帮忙去完成。
委托:是当某件事情发生的时候,顺便干某件事情。委托就相当于一个触发器罢了。
代理模式的种类
在实际开发过程中,代理类的实现比上述代码要复杂很多,代理模式根据其目的和实现方式不同可分为很多种类,其中常用的几种代理模式简要说明如下:
- 1、远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。
- 2、虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
- 3、保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
- 4、缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
- 5、智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。
代理模式的应用场景
代理模式的类型较多,不同类型的代理模式有不同的优缺点,它们应用于不同的场合:
- 1、当客户端对象需要访问远程主机中的对象时可以使用远程代理。
- 2、当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
- 3、当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
- 4、当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
- 5、当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。
代理模式的优缺点
优点:
- 1、代理模式能将代理对象与真实被调用的目标对象分离。
- 2、能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
- 3、客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
- 4、保护目标对象
- 5、增强目标对象
缺点:
- 1、代理模式会造成系统设计中类的数目增加。
- 2、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
- 3、实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。
- 4、增加系统的复杂度。
代理-扩展
- 1、静态代理
- 2、动态代理
- 3、CGLib代理
Spring代理选择-扩展
- 当Bean有实现接口时,Spring就会用JDK的动态代理
- 当Bean没有实现接口时,Spring使用CGLib
- 可以强制使用CGLib,在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
代理模式的实现
静态代理
接口(Subject)
public interface OrderService {
int saveOrder(Order order);
}
委托类
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao;
@Override
public int saveOrder(Order order) {
//spring会自己注入,这里就直接new
orderDao = new OrderDaoImpl();
System.out.println("service调用dao添加order");
return orderDao.insert(order);
}
}
代理类
public class OrderServiceStaticProxy {
private OrderService orderService;
public OrderServiceStaticProxy(OrderService orderService) {
this.orderService = orderService;
}
public int saveOrder(Order order){
beforeMethod();
int i = orderService.saveOrder(order);
afterMethod();
return i;
}
private void beforeMethod(){
System.out.println("静态代理 before code");
}
private void afterMethod(){
System.out.println("静态代理 after code");
}
}
测试类
public class Test {
public static void main(String[] args) {
Order order = new Order();
OrderServiceStaticProxy staticProxy = new OrderServiceStaticProxy(new OrderServiceImpl());
staticProxy.saveOrder(order);
}
}
动态代理
动态代码涉及了一个非常重要的类 Proxy。正是通过 Proxy 的静态方法 newProxyInstance 才会动态创建代理。
Proxy
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
InvocationHandler
InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。
- proxy:代理对象
- method:代理对象调用的方法
- args:调用的方法中的参数
因为,Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。
接口(Subject)
public interface OrderService {
int saveOrder(Order order);
}
委托类
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao;
@Override
public int saveOrder(Order order) {
//spring会自己注入,这里就直接new
orderDao = new OrderDaoImpl();
System.out.println("service调用dao添加order");
return orderDao.insert(order);
}
}
代理类
public class OrderServiceDynamicProxy implements InvocationHandler {
private Object target;
public OrderServiceDynamicProxy(Object target) {
this.target = target;
}
public Object bind(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object argObject = args[0];
beforeMethod();
Object object = method.invoke(target,args);
afterMethod();
return object;
}
private void beforeMethod(){
System.out.println("JDK动态代理 before code");
}
private void afterMethod(){
System.out.println("JDK动态代理 after code");
}
}
测试类
public class Test {
public static void main(String[] args) {
Order order = new Order();
OrderService orderServiceDynamicProxy = (OrderService)new OrderServiceDynamicProxy(new OrderServiceImpl()).bind();
orderServiceDynamicProxy.saveOrder(order);
}
}
总结
- 静态代理实现较简单,代理类在编译期生成,效率高。缺点是会生成大量的代理类。
- JDK动态代理不要求代理类和委托类实现同一个接口,但是委托类需要实现接口,代理类需要实现InvocationHandler接口。
- 动态代理要求代理类InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。