目录
上述代码 GitHub 地址:https://github.com/baicun/designPatterns
代理模式:
为一个对象提供替身,进而可以真正的控制这个对象。
代理模式优点:
(1) 体现了 java 面向对象的编程思想;
(2) 在调用者和被调用者之间降低耦合度;
此外不同代理还有不同的优点:
(1) 动态代理:运行时动态的创建代理对象,通过反射机制,利用JDK提供的Proxy类。并将方法调用转发到指定类。
(2) 保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。
(3) 远程代理:为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
(4) 虚拟代理:创建一个消耗资源较多的对象代理服务,可以在一定程度上节省系统的运行开销。
eg: 微博图片在线加载类(传输图片视频等耗费资源,利用虚拟代理)
(5) 缓存代理:为某一操作的结果提供临时的缓存空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间。
eg:微博内容加载完成以后,会调用缓存代理,使这部分数据能够保存。方便下次使用。
代理模式应用:
AOP;远程代理RMI;数据库连接...
类图:

代理实例:
简单示例:
//抓娃娃机接口
public interface DollMachine {
String dollMachineState();
}
//抓娃娃机器接口实现类
public class DollMachineImpl implements DollMachine{
@Override
public String dollMachineState() {
System.out.println("正在获取机器状态,请等待...");
return "0";
}
}
测试类:
public class MainTest {
public static void main(String[] arg){
//代理前
DollMachine dollMachineA = new DollMachineImpl();
String sa = dollMachineA.dollMachineState();
System.out.println("代理前 机器抓娃娃机状态 = [" + sa + "]");
}
}
开始代理:
创建一个类,实现基础接口,并且代理已经存在的实现类。
//代理实现类
public class ProxyDollMachine implements DollMachine{
private DollMachineImpl dollMachineA;
public ProxyDollMachine(){
super();
this.dollMachineA = new DollMachineImpl();
}
@Override
public String dollMachineState() {
before();
String ret = dollMachineA.dollMachineState();
after();
return ret;
}
public void before(){
System.out.println("代理对象开始");
}
public void after(){
System.out.println("代理对象结束");
}
}
测试:
public class MainTest {
public static void main(String[] arg){
//代理前
DollMachine dollMachineA = new DollMachineImpl();
String sa = dollMachineA.dollMachineState();
System.out.println("代理前 机器抓娃娃机状态 = [" + sa + "]");
//代理后
DollMachine dollMachineB = new ProxyDollMachine();
String sb = dollMachineB.dollMachineState();
System.out.println("代理后 机器抓娃娃机状态 = [" + sb + "]");
}
}
这就是spring项目中常见的一个AOP。通过代理我们可以对整个项目中的一些方法进行控制。
通过上面的实例,不难发现编码灵活性不足,当接口“DollMachine”增加一个方法,对应我们的代理类“ProxyDollMachine”也要做相应的改变,代码维护和拓展性比较差。为了解决这个缺陷,下面了解下动态代理!
动态代理
// 方法接口
public interface DollMachine {
int sum() throws InterruptedException;
String dollMachineState() throws InterruptedException;
}
//接口实现类
public class DollMachineImpl implements DollMachine {
@Override
public int sum() throws InterruptedException {
System.out.println("返回机器中娃娃的数量");
Thread.sleep(5000);
return 100;
}
@Override
public String dollMachineState() throws InterruptedException {
System.out.println("返回机器的使用状态");
Thread.sleep(100);
return "0";
}
}
// 代理接口的实现类对象
// JDK内置的动态代理Proxy只能代理接口
public class ProxyDollMachine implements DollMachine {
private DollMachineImpl dollMachine;
public ProxyDollMachine(){
this.dollMachine=new DollMachineImpl();
}
@Override
public int sum() throws InterruptedException {
long start = System.currentTimeMillis();
int sum = dollMachine.sum();
long end = System.currentTimeMillis();
System.out.println("调用耗时:"+(end-start));
return sum;
}
@Override
public String dollMachineState() throws InterruptedException {
long start = System.currentTimeMillis();
String machineState = dollMachine.dollMachineState();
long end = System.currentTimeMillis();
System.out.println("调用耗时:"+(end-start));
return machineState;
}
}
// 调用处理器,当代理对象调用代理方法的时候,注册在调用处理器中的invoke方法会自动调用。
// 该接口作为代理实例的调用处理者的公共父类,
public class DollMachineInvocationHandler implements InvocationHandler {
//注册目标对象,该目标对象的方法执行invoke()方法
private Object target;
//构造方法
public DollMachineInvocationHandler(Object target) {
this.target = target;
}
// 方法说明
// Object proxy:代理对象的引用,proxy变量中保存代理对象的内存地址(这个参数很少用)
// Method method:目标对象的目标方法。
// Object[] args:目标对象的目标方法执行的时候所需要实参。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
//执行目标对象中的方法
Object retValue = null;
retValue = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("代理调用耗时:" + (end - start));
return retValue;
}
}
测试类:
public class MainTest {
// JDK内置的动态代理Proxy只能代理接口
//(如果既想代理接口又想代理抽象类需要使用第三方组件:例如 cglib)
public static void main(String[] arg) throws InterruptedException {
DollMachine target = new ProxyDollMachine();
// 创建动态代理类和实例对象的方法
/**
* 第一个参数loader表示代理类的类加载器,
* 第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),
* 第三个参数h表示所指派的调用处理程序类。
*/
DollMachine proxy = (DollMachine) Proxy.newProxyInstance(
target.getClass().getClassLoader()
,new Class[]{DollMachine.class}
,new DollMachineInvocationHandler(target));
// 调用方法
proxy.dollMachineState();
proxy.sum();
}
}
动态代理灵活的实现对目标接口的代理处理,那么如果不同的权限可不可以有不同的处理方式呢?
继续了解保护代理 !
保护代理
控制对一个对象的访问权限,为不同用户不同方法提供不同级别的使用权限。
下面是在 DollMachineInvocationHandler.java 中做的修改。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
//执行目标对象中的方法
Object retValue = null;
// 保护代理思想,针对不同的方法,不同的角色执行权限不同。
// 当然也可以重新写一个 InvocationHandler 处理器,来区分不同的执行权限
if(method.getName().equals("dollMachineState")){
return null;
}else{
retValue = method.invoke(target, args);
}
long end = System.currentTimeMillis();
System.out.println("代理调用耗时:" + (end - start));
return retValue;
}
远程代理
接口:
//服务端接口:想要实现服务端的方法接口
public interface MyRemote extends Remote {
// 客户端和服务端首次通信
String sayHello() throws RemoteException;
// 查看机器状态
String dollMachineState() throws RemoteException;
}
实现类:
//服务端方法
public class MyRemoteSvcImpl extends UnicastRemoteObject implements MyRemote {
//构造方法
protected MyRemoteSvcImpl() throws RemoteException {
super();
}
//客户端和服务端首次通信
@Override
public String sayHello() throws RemoteException{
return "Hello World!";
}
//返回抓娃娃机器的状态
@Override
public String dollMachineState() throws RemoteException {
return "1";
}
}
代理类:
public class ProxyRemote {
// 代理服务端接口
MyRemote service;
public ProxyRemote() throws RemoteException, MalformedURLException, NotBoundException {
//通过Naming.lookup方法解析ip 查找注册地址,返回
service = (MyRemote) Naming.lookup("rmi://127.0.0.1:6600/RemoteHello");
}
}
服务注册:
//RMI服务注册
public class Route {
public static void main(String[] args) {
try {
MyRemote service=new MyRemoteSvcImpl();
LocateRegistry.createRegistry(6600);
//IP地址要写本机ip
Naming.rebind("rmi://10.0.5.27:6600/RemoteHello", service);//绑定开启服务
} catch (Exception e) {
e.printStackTrace();
System.out.println( e.toString());
}
}
}
测试类:
public class MainTest {
public static void main(String[] args) {
try {
ProxyRemote proxyRemote = new ProxyRemote();
// 通过代理类获取代理接口
String s=proxyRemote.service.sayHello();
String status=proxyRemote.service.dollMachineState();
System.out.println("首次通信:" + s);
System.out.println("返回远程机器状态:" + status);
} catch (Exception e) {
e.printStackTrace();
}
}
}
上述代码 GitHub 地址:https://github.com/baicun/designPatterns