先来看看几个场景,比如刷网课,我们都要自己刷对吧,一节节看,还有单元测试啥的,浪费时间又学不到自己感兴趣的东西,或者玩游戏,需要升级,但是又常常充满罪恶感,可惜自己没有好好学习。那我们用代码表示一下就是这样子。
一个抽象主题接口,用来定义我们要做的事情的一个抽象接口:
package com.ongbo.ProxyPattern;
/**
* 抽象主题接口角色
* */
public interface SubObject {
/**
* 正常代理的请求方法
*/
public void request();
/**
* 本篇博客用例使用的代理方法
* 平常开发不需要
* */
public void login(String user,String password);
public void kill();
public void upgrade();
}
真实主题类,用来实现抽象主题的具体操作类
package com.ongbo.ProxyPattern;
/**
* 真实主题
* 需要实现抽象主题接口
* */
public class RealSubObject implements SubObject{
//保存用户名
private String name = "";
public RealSubObject(String name){
this.name = name;
}
public void request() {
}
public void login(String user, String password) {
System.out.println("用户"+user+"的用户"+this.name+"登陆了");
}
public void kill() {
System.out.println(this.name+"杀了一个怪物");
}
public void upgrade() {
System.out.println(this.name+"升级了");
}
}
Client类,代表我自己去操作:
package com.ongbo.ProxyPattern;
public class Client_demo1 {
public static void main(String[] args) {
SubObject gameuser = new RealSubObject("ongbo");
gameuser.login("xxxx","xxxxx");
gameuser.kill();
gameuser.upgrade();
}
}
这上面就是一个游戏,自己作死的打,但是就是菜,打不上去,没有成就感,还耽误了自己的学习。这时候可能就会像干脆让别人代替我玩算啦。这时候就有了代理模式
代理模式即委托,代理,就是相让别人代替我做一件事情,我似乎只要发一种指令,来减少我和那件事情的耦合,比如,我们在学校有网课,可以花钱给别人代刷网课,或者你玩游戏很菜,于是让别人玩自己的号码来提神等级这些都是代理模式的一种体现。
代理模式中的角色
SubObjec抽象主题角色
声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,而Client常常需要根据抽象主题进行编程
Proxy代理主题角色
它包含了对真实主题的引用,从而可以在任何时候操作真实主题 对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代 真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实 主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所 引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中 的操作。
RealObject真实主题
它定义了代理角色所代表的真实对象,在真实主题角色中 实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操 作。
代理模式简单实现
抽象主题
package com.ongbo.ProxyPattern;
/**
* 抽象主题接口角色
* */
public interface SubObject {
/**
* 正常代理的请求方法
*/
public void request();
/**
* 本篇博客用例使用的代理方法
* 平常开发不需要
* */
public void login(String user,String password);
public void kill();
public void upgrade();
}
代理主题角色
package com.ongbo.ProxyPattern;
/**
* 代理主题角色
* */
public class ObjectProxy implements SubObject {
/** 保存一个真实主题角色,在构造方法传入关联真实主题角色*/
private SubObject subObject = null;
public ObjectProxy(SubObject subObject){
this.subObject = subObject;
}
/** 下面就用上面关联的真实主题角色对象来操控访问 */
public void request() {}
public void login(String user, String password) {
this.subObject.login(user,password);
}
public void kill() {
this.subObject.kill();
}
public void upgrade() {
this.subObject.upgrade();
}
}
真实主题角色
package com.ongbo.ProxyPattern;
/**
* 真实主题
* 需要实现抽象主题接口
* */
public class RealSubObject implements SubObject{
//保存用户名
private String name = "";
public RealSubObject(String name){
this.name = name;
}
public void request() {
}
public void login(String user, String password) {
System.out.println("用户"+user+"的用户"+this.name+"登陆了");
}
public void kill() {
System.out.println(this.name+"杀了一个怪物");
}
public void upgrade() {
System.out.println(this.name+"升级了");
}
}
Client实现
package com.ongbo.ProxyPattern;
public class Client_demo2 {
public static void main(String[] args) {
//定义一个游戏上瘾的玩家,或者一个爱学习的但是要刷客的学生,即真实主题对象
SubObject realObject = new RealSubObject("ongbo");
//然后创建一个代理主题对象,并且将其与真实主题对象相关联
SubObject ProxyObject = new ObjectProxy(realObject);
/**接下来全部使用代理主题对象去操作*/
ProxyObject.login("xxxx","xxx");
ProxyObject.kill();
ProxyObject.upgrade();
}
}
好了,到现在为止,至少已经有人替你干活了,你就不用自己去沉迷游戏,或者还要一边学习,一边刷网课,这样就可以减轻自己的负担,让自己去做其他事情,而且不用那么多复杂的逻辑了。
上面代码看到了,在看到Client的时候,你会看见,不还是要在本地弄一个真实主题对象吗,我Client不还是要管理这个对象吗,这时候就可以修改一下,在真实主题内关联抽象主题,来看看自己创建没有,而我们把真是主题角色的创建推移到代理主题的构造方法,在Client上就只要传入一个name即可,而不用先创建一个真实主题对象再传入
Client改动
//原版
SubObject ProxyObject = new ObjectProxy(realObject);
/** 现版*/
SubObject ProxyObject = new ObjectProxy("ongbo");
//原版
public ObjectProxy(SubObject subObject){
this.subObject = subObject;
}
//现版
public ObjectProxy(String name){
SubObject realObject = new RealSubObject(name);
this.subObject = realObject;
}
而在真实主题角色中,也可以自己关联代理主题角色
这样子,我们就可以将真实主题和Client屏蔽了,Client不用管真是主体的实际是谁,只知道名字,也不需要进行更复杂的操作
动态代理
有动态代理就有静态代理,上面的都是静态代理,即都要使用具体的Proxy类去实现抽象主题接口,从而才能够使Client间接使用真实主题。但是静态代理有缺点的,就是对于每个逻辑,可能都要去实现一个代理类,从而导致类爆炸,而动态代理就回去解决这个问题
package com.ongbo.ProxyPattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client_demo3 {
public static void main(String[] args) {
/**创建一个真是主题对象*/
SubObject realObject = new RealSubObject("ongbo");
/**使用JDK中的InvocationHandler,去实现这个接口,实现类NormalHandler*/
InvocationHandler invocationHandler = new NormalHandler(realObject);
/**获得真实主题类的ClassLoader*/
ClassLoader cl = realObject.getClass().getClassLoader();
/**使用Proxy.newProxyInstance去构建一个抽象主题的代理*/
/**参数说明:
* 第一个参数:ClassLoader,即我们真实主题对象的ClassLoader就行
* 第二个参数:我们真实主题类实现的接口
* 第三个参数:我们上面实现的InvocationHandler
* */
SubObject newProxyInstance = (SubObject)Proxy.newProxyInstance(cl, realObject.getClass().getInterfaces(), invocationHandler);
/**调用相应的方法即可*/
newProxyInstance.login("xx","xxx");
newProxyInstance.kill();
newProxyInstance.upgrade();
}
}
那来看看那个InvocationHandler的实现类NormalHandler
package com.ongbo.ProxyPattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class NormalHandler implements InvocationHandler {
/**这里是自己写的逻辑而已,引入的是真实主题的关联*/
private Object obj = null;
Class cls = null;
public NormalHandler(Object obj){
this.obj = obj;
}
/**这是实现Invocationhandler必须实现的方法
* 可以看到里面使用了反射,来调用方法了。
* */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj,args);
return result;
}
}
Proxy类源码解析
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{
//检查这个Handler是不是null,会抛出NullPointExeception
Objects.requireNonNull(h);
//克隆我们要生成代理类的所有接口,即这个接口数组
final Class<?>[] intfs = interfaces.clone();
//检查权限
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 查找或者生成一个特定的代理类对象
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* 检查权限,并且调用这个代理类的参数为Handler的构造器,将其实例化,然后强制修改权限
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//利用构造器,返回代理类对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
/**
* 生成一个代理类,生成之前必须检查权限
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//如果这个类加载已经实现了这个接口的代理类,那么就会返回这个缓存,否则就直接通过ProxyClassFactory工厂类创建这个对象
return proxyClassCache.get(loader, interfaces);
}