三、设计模式
3.代理模式(Proxy pattern)
3.1 简介
在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
通过引入一个新的对象(如小图片和远程代理 对象)来实现对真实对象的操作或者将新的对 象作为真实对象的一个替身,这种实现机制即 为代理模式,通过引入代理对象来间接访问一 个对象,这就是代理模式的模式动机。
代理模式(Proxy Pattern) :给某一个对象提供一个代 理,并由代理对象控制对原对象的引用。代理模式的英 文叫做Proxy或Surrogate,它是一种对象结构型模式。
3.2 结构图
角色对象
- Subject: 抽象主题角色,该类的主要职责是申明真是主题与代理的共同接口方法,该类既可以是个抽象类也可以是个接口(具有抽象方法)。
- Proxy: 代理主题角色,这个类的对象持有一个对真实主题的引用,在这个类所实现的接口方法中调用真实主题类中相应的方法执行,这样就实现了代理的目的。
- RealSubject: 真实主题角色,该类也称为委托类或者被代理类,改类定义了代理所表示的真是对象(也就是实现了抽象方法),由其执行具体的业务逻辑。
- Client:客户类,也就是使用代理类的类型,客户类通过代理类间接地调用了真实主题类中定义的方法。
3.3 实现模式
①静态代理
若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和目标类会实现同一接口或是派生自相同的父类。
公共接口
package com.tcwong;
/**
* 定义公共接口
* [@author](https://my.oschina.net/arthor) Lenovo
*
*/
public interface SomeService {
String doSome();
}
具体实现类
package com.tcwong;
/**
* 静态代理的的实现类
* [@author](https://my.oschina.net/arthor) Lenovo
*
*/
public class SomeServiceImpl implements SomeService {
[@Override](https://my.oschina.net/u/1162528)
public String doSome() {
return "hello";
}
}
代理类
package com.tcwong;
/**
* 代理类
* 增强实现类
* [@author](https://my.oschina.net/arthor) Lenovo
*
*/
public class SomeProxy implements SomeService {
private SomeService target;
public SomeProxy(SomeService target) {
super();
this.target = target;
}
[@Override](https://my.oschina.net/u/1162528)
public String doSome() {
return target.doSome().toUpperCase();
}
}
测试类
package com.tcwong;
import static org.junit.Assert.*;
public class Test {
@org.junit.Test
public void test() {
SomeService some = new SomeServiceImpl();
SomeProxy proxy = new SomeProxy(some);
System.out.println(proxy.doSome());
}
}
控制台打印
②动态代理
代理类在程序运行时创建的代理方式被成为 动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
- JDK动态代理,如果目标对象实现了接口,采用JDK的动态代理
- cglib动态代理,如果目标对象没有实现了接口,必须采用CGLIB动态代理
JDK动态代理
公共接口
package com.tcwong;
/**
* 定义公共接口
* @author Lenovo
*
*/
public interface SomeService {
String doSome();
}
实现类
package com.tcwong;
/**
* 静态代理的的实现类
* @author Lenovo
*
*/
public class SomeServiceImpl implements SomeService {
@Override
public String doSome() {
return "hello";
}
}
代理类实现
package com.tcwong;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
public class Test2 {
private SomeService some = new SomeServiceImpl();
@Test
public void test() {
SomeService proxy = (SomeService)Proxy.newProxyInstance(
some.getClass().getClassLoader(),
some.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String msg = (String)method.invoke(some, args);
return msg.toUpperCase();
}
});
System.out.println(proxy.doSome());
}
}
输出信息
CGLIB动态代理
依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.5</version>
</dependency>
创建Cglib工厂
package com.tcwong;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* 创建Cglib工厂
* @author Lenovo
*
*/
public class CglibProxy implements MethodInterceptor {
private SomeServiceImpl target;
public CglibProxy(SomeServiceImpl target) {
super();
this.target = target;
}
/**
* 创建cglib代理对象的方法
* @return
*/
public SomeServiceImpl createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SomeServiceImpl.class);
enhancer.setCallback(this);
return (SomeServiceImpl) enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String msg = (String) method.invoke(target, args);
return msg.toUpperCase();
}
}
测试
@Test
public void test2() {
SomeServiceImpl someServiceImpl = new SomeServiceImpl();
SomeServiceImpl createProxy = new CglibProxy(someServiceImpl).createProxy();
System.out.println(createProxy.doSome());
}
3.4 优点
- 代理模式能够协调调用者和被调用者,在一定程度上降低了系 统的耦合度。
- 远程代理使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
- 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系 统资源的消耗,对系统进行优化并提高运行速度。
- 保护代理可以控制对真实对象的使用权限。
3.5 缺点
- 由于在客户端和真实主题之间增加了代理对象,因此 有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现 非常复杂。
3.6 适用环境
- 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地 的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在 另一台主机中,远程代理又叫做大使(Ambassador)。
- 虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
- Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟 到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个 开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
- 保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
- 缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
- 防火墙(Firewall)代理:保护目标不让恶意用户接近。
- 同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
- 智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。
3.7 几种常用的代理模式
- 图片代理:一个很常见的代理模式的应用实例就是对大图浏览的控制。
- 用户通过浏览器访问网页时先不加载真实的大图,而是通过代理对象的方法来进行处理,在代理对象的方法中,先使用一个线程向客户端浏览器加载一个小图片,然后在后台使用另一个线程来调用大图片的加载方法将大图片加载到客户端。当需要浏览大图片时,再将大图片在新网页中显示。如果用户在浏览大图时加载工作还没有完成,可以再启动一个线程来显示相应的提示信息。通过代理技术结合多线程编程将真实图片的加载放到后台来操作,不影响前台图片的浏览。
- 远程代理:远程代理可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的远程业务对象是局域的而不是远程的,而远程代理对象承担了大部分的网络通信工作。
- 虚拟代理:当一个对象的加载十分耗费资源的时候,虚拟代理的优势就非常明显地体现出来了。虚拟代理模式是一种内存节省技术,那些占用大量内存或处理复杂的对象将推迟到使用它的时候才创建。