定义
代理模式(委托模式)
定义:对其他对象
提供一种代理
从而控制对这个对象
的访问。
就是,代理类 代理 被代理类,来执行被代理类里的方法。
一般情况下,代理模式化有三个角色。
1. 抽象的主题类(或者接口) IGamePlayer
2. 代理类。
3. 被代理类。
一、最基本的代理模式
三个角色:主题接口,被代理类,代理类

主题接口:
//游戏玩家主题接口
public interface IGamePlayer {
public void login(String username, String password); //登录游戏
public void killBoss(); //击杀Boss
public void upGrade(); //升级
}
被代理类
//需要代理的主题类。
public class GamePlayer implements IGamePlayer{
private String name = "";
public GamePlayer(String name){
this.name = name;
}
public void login(String username, String password) {
System.out.println("登录名为 "+username+" 进入游戏," + name + " 登录成功!");
}
public void killBoss() {
System.out.println(this.name + " 击杀了Boss!");
}
public void upGrade() {
System.out.println(this.name + "升级了!");
}
}
代理类
//代理类
//代理也是个游戏玩家,所以也要实现IGamePlayer
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer proxyGp = null;
//代理需要获取被代理的信息,就是执行被代理所要执行的方法,
//所以要获取被代理的对象。这里通过构造方法获取。
public GamePlayerProxy(GamePlayer gp){
this.proxyGp = gp;
}
public void login(String username, String password) {
System.out.println("代理登录的游戏!");
proxyGp.login(username, password);
}
public void killBoss() {
proxyGp.killBoss();
}
public void upGrade() {
proxyGp.upGrade();
}
}
不使用代理的场景
//这是正常的,用非代理的情况下。
public class Client {
public static void main(String [] args){
IGamePlayer gp = new GamePlayer("张三");
//开始执行主题接口中的方法。
gp.login("zhangsan", "123456");//登录游戏
gp.killBoss();//杀怪
gp.upGrade();//升级
}
}
执行结果:
登录名为 zhangsan 进入游戏,张三 登录成功!
张三 击杀了Boss!
张三升级了!
使用代理的场景
游戏也有玩累,玩乏的时候,所以找个代练来升级。下面就是代理类的场景类。
//代理客户端
public class BasePoxyClient {
public static void main(String[] args){
IGamePlayer proxyGp = new GamePlayerProxy(new GamePlayer("张三"));
proxyGp.login("zhangsan", "123456");
proxyGp.killBoss();
proxyGp.upGrade();
}
}
执行结果为:
代理登录的游戏!
登录名为 zhangsan 进入游戏,张三 登录成功!
张三 击杀了Boss!
张三升级了!
执行还是这样,但是可以看出,确实是代理类来执行的。这就是最简单的代理模式了。
代理模式还是有很多种的,比如,普通代理模式,强制代理模式,虚拟代理模式,动态代理模式…..
接下来我们一一来看。
二、普通代理模式:
其实普通代理模式和上面的差不多。
普通代理模式,它不用知道代理的真实角色是谁,屏蔽了真实角色的更变对高层模块的影响。
(本例中,就是,不用知道为谁代练游戏,只需要知道代练游戏的用户名,密码即可。)
三个角色:主题接口,被代理类,代理类

主题接口
//游戏玩家主题接口
public interface IGamePlayer {
public void login(String username, String password); //登录游戏
public void killBoss(); //击杀Boss
public void upGrade(); //升级
}
被代理类
public class GamePlayer implements IGamePlayer{
private String name = "";
//通过构造方法,将代理传递进来。
public GamePlayer(IGamePlayer proxyGp, String name){
if(proxyGp == null){
//处理非正常情况。
}else{
this.name = name;
}
}
//登录游戏
public void login(String username, String password) {
System.out.println("登录名为 "+username+" 进入游戏," + name + " 登录成功!");
}
//杀Boss
public void killBoss() {
System.out.println(this.name + " 击杀了Boss!");
}
//升级
public void upGrade() {
System.out.println(this.name + "升级了!");
}
}
代理类
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer proxyGp = null;
public GamePlayerProxy(String name){
try {
//通过构造方法创建GamePlayer,同时将自己传递进去。用于在GamePlayer判断业务逻辑
proxyGp = new GamePlayer(this, name);
} catch (Exception e) {
e.printStackTrace();
}
}
public void login(String username, String password) {
System.out.println("代理登录的游戏!");
proxyGp.login(username, password);
}
public void killBoss() {
proxyGp.killBoss();
}
public void upGrade() {
proxyGp.upGrade();
}
}
使用场景类
/**
* 普通代理模式,它不用知道代理的真实角色是谁,屏蔽了真实角色的更变对高层模块的影响。
* (本例中,就是,不用知道为谁代练游戏,只需要知道代练游戏的用户名,密码即可。)
*/
public class Client {
public static void main(String[] args){
//并没有将张三的游戏者传给代理,而只需要知道为张三在代练
IGamePlayer proxyGp = new ProxyGamePlayer("张三");
proxyGp.login("zhangsan", "123456");
proxyGp.killBoss();
proxyGp.upGrade();
}
}
执行结果:
代理登录的游戏!
登录名为 zhangsan 进入游戏,张三 登录成功!
张三 击杀了Boss!
张三升级了!
三、强制代理模式
一般的代理模式都是通过代理类找到被代理的对象,从而调用被代理类中的方法(即完成被代理类中的任务)。
而,强制代理模式则是先找到被代理类自己去完成事情,然后被代理类又将该做的事情转交到代理类中,让代理类来完成。
假如:你有事求助于某位名人。
你告诉名人说有事想请他帮忙,然后他说最近一段时间比较忙,要不你找我的经纪人来办吧。
(本来找名人办事,事情由他来完成的,即 表示 真实主题类。然而,他要将事情转交给自己的经纪人(即代理类),这时该名人就是被代理者)。
三个角色:主题接口,被代理类,代理类

主题接口
public interface IGamePlayer {
public void login(String username, String password);//登录游戏
public void killBoss(); //击杀Boss
public void upGrade();//升级
public IGamePlayer getProxy();//❗️[增加]获取代理
}
//由于要通过主题类找代理类,所以在此添加getProxy()方法,获取代理。
被代理类
public class GamePlayer implements IGamePlayer{
private String name = "";
private IGamePlayer proxyGp = null;
public GamePlayer(String name){
this.name = name;
}
//获取代理。
public IGamePlayer getProxy() {
this.proxyGp = new ProxyGamePlayer(this);
return this.proxyGp;
}
public void login(String username, String password) {
//只有是自己指定的代理人才给办事,别人的代理也不搭理咱啊,所以判断是否为自己的代理,若是,则正常执行。
if(this.isProxy()){
System.out.println("登录名为 "+username+" 进入游戏," + name + " 登录成功!");
}else{
System.out.println("请使用指定代理访问!");
}
}
public void killBoss() {
if(this.isProxy()){
System.out.println(this.name + " 击杀了Boss!");
}else{
System.out.println("请使用制定代理访问!");
}
}
public void upGrade() {
if(this.isProxy()){
System.out.println(this.name + "升级了!");
}else{
System.out.println("请使用制定代理访问!");
}
}
//判断是否是代理
public boolean isProxy(){
if(this.proxyGp!=null){
return true;
}else {
return false;
}
}
}
代理类
//强制代理的代理类。
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer proxyGp = null;
public GamePlayerProxy(IGamePlayer gp){
this.proxyGp = gp;
}
public void login(String username, String password) {
System.out.println("代理登录游戏!");
this.proxyGp.login(username, password);
}
public void killBoss() {
this.proxyGp.killBoss();
}
public void upGrade() {
this.proxyGp.upGrade();
}
public IGamePlayer getProxy() {
return this;
}
}
场景类一:不通过代理来执行。
package com.yemaozi.proxy.force;
public class Client {
public static void main(String[] args) {
IGamePlayer gp = new GamePlayer("张三");
gp.login("zhangsan", "123456");
gp.killBoss();
gp.upGrade();
}
}
执行结果:
请使用制定代理访问!
请使用制定代理访问!
请使用制定代理访问!
//很显然,不用代理,是不能正常执行。(这也就是为什么叫强制代理)
场景类二:用不是指定的代理类来执行。
//直接访问代理类(非真实用户制定的代理)
public class ProxyClient {
public static void main(String[] args) {
IGamePlayer proxy = new ProxyGamePlayer(new GamePlayer("张三"));
proxy.login("zhangsan", "123456");
proxy.killBoss();
proxy.upGrade();
}
}
执行结果:
代理登录游戏!
请使用制定代理访问!
请使用制定代理访问!
请使用制定代理访问!
//显然虽然代理登录了,但是由于是非法的(有可能属于盗号行为),所以下面还是执行不了。
场景类三:使用真实主题类指定的代理。
//通过真是类去访问代理类。
public class SpecificProxyClient {
public static void main(String[] args) {
IGamePlayer gp = new GamePlayer("张三");
IGamePlayer proxyGp = gp.getProxy();
proxyGp.login("zhangsan", "123456");
proxyGp.killBoss();
proxyGp.upGrade();
}
}
执行结果:
代理登录游戏!
登录名为 zhangsan 进入游戏,张三 登录成功!
张三 击杀了Boss!
张三升级了!
//这次终于可以顺利的执行了。
强制代理模式的概念就是要从真是角色那里查找到代理角色,不允许直接访问真实角色。上层模块只需要调用getProxy()获取代理来访问真实角色的所有方法,它根本就不需要产生一个代理角色,代理的管理已经由真实角色自己来完成。
四、动态代理模式
当然代理模式中,用的最广泛的,用的最多的是 动态代理模式。
动态代理:就是实现阶段不用关系代理是哪个,而在运行阶段指定具体哪个代理。
抽象接口的类图如下:

所有动态代理模式要有一个InvocationHandler接口 和 GamePlayerIH实现类。其中 InvocationHandler是JDK提供的动态代理接口,对被代理类的方法进行代理。
三个角色:主题接口,被代理类,代理类
主题接口
/*
* 动态代理:就是实现阶段不用关系代理是哪个,而在运行阶段指定具体哪个代理。
*/
public interface IGamePlayer {
public void login(String username, String password);
public void killBoss();
public void upGrade();
}
被代理类
public class GamePlayer implements IGamePlayer {
private String name = "";
public GamePlayer(String name){
this.name = name;
}
public void login(String username, String password) {
System.out.println("登录名为 "+username+" 进入游戏," + name + " 登录成功!");
}
public void killBoss() {
System.out.println(this.name + " 击杀了Boss!");
}
public void upGrade() {
System.out.println(this.name + "升级了!");
}
}
代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class GamePlayerInvocationHandler implements InvocationHandler{
//被代理的对象
private Object obj;
//将需要代理的实例通过处理器类的构造方法传递给代理。
public GamePlayerInvocationHandler(Object obj){
this.obj = obj;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
if("login".equalsIgnoreCase(method.getName())){
/*这个在主题方法不受任何影响的情况下,在主题方法前后添加新的功能,
或者增强主题方法,从侧面切入从而达到扩展的效果的编程,就是面向
切面编程(AOP Aspect Oriented Programming)。
AOP并不是新技术,而是相对于面向对象编程的一种新的编程思想。
在日志,事务,权限等方面使用较多。*/
System.out.println("代理登录游戏!");
result = method.invoke(this.obj, args);
return result;
}
result = method.invoke(this.obj, args);
return result;
}
}
由于代理是动态产生的,所以不需要再声明代理类。
动态代理场景类:
package com.yemaozi.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
IGamePlayer gp = new GamePlayer("张三");
InvocationHandler gpHandler = new GamePlayerInvocationHandler(gp);
//获取真实主题类的ClassLoader
ClassLoader classLoader = gp.getClass().getClassLoader();
//动态产生一个代理者。
Class<?>[] cls = new Class[]{IGamePlayer.class};
IGamePlayer proxyGp = (IGamePlayer) Proxy.newProxyInstance(classLoader, cls, gpHandler);
proxyGp.login("zhangsan", "123456");
proxyGp.killBoss();
proxyGp.upGrade();
}
}
执行结果:
代理登录游戏!
登录名为 zhangsan 进入游戏,张三 登录成功!
张三 击杀了Boss!
张三升级了!
//在此,我们没有创建代理类,但是确实有代理类帮我们完成事情。
其中,在此代理模式中,不仅代理是动态产生的(即在运行的时候生成),而且还在代理的时候,也增加了一些处理。在此处增加的处理,其实就是另一种编程思想—–面向切面编程思想(AOP Aspect Oriented Programming)。
带有AOP的动态代理模式类图:

从上图中,可以看出有两个相对独立的模块(Subject和InvocationHandler)。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。然而,通知Advice从另一个切面切入,最终在上层模块就是Client耦合,完成逻辑的封装。
三个角色:主题接口,被代理类,代理类
抽象主题或者接口:
public interface Subject {
public void doSomething(String str);
//...可以多个逻辑处理方法。。。
}
真实主题:
public class RealSubject implements Subject{
public void doSomething(String str) {
System.out.println("do something..." + str);
}
}
通知接口:
//通知接口及定义、
public interface IAdvice {
public void exec();
}
前置通知:
public class BeforeAdvice implements IAdvice {
//在被代理的方法前来执行,从而达到扩展功能。
public void exec() {
System.out.println("前置通知被执行!");
}
}
后置通知:
public class AfterAdvice implements IAdvice {
//在被代理的方法后来执行,从而达到扩展功能。
public void exec() {
System.out.println("后置通知被执行!");
}
}
动态代理的处理器类:
所有的方法通过invoke方法类实现。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
//被代理的对象
private Subject realSubject;
//通过MyInvocationHandler的构造方法将被代理对象传递过来。
public MyInvocationHandler(Subject realSubject){
this.realSubject = realSubject;
}
//执行被代理类的方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在执行方法前,执行前置通知。
IAdvice beforeAdvice = new BeforeAdvice();
beforeAdvice.exec();
Object result = method.invoke(this.realSubject, args);
//在执行方法后,执行后置通知。
IAdvice afterAdvice = new AfterAdvice();
afterAdvice.exec();
//前置通知,和后置通知,都是要看具体实际的业务需求来进行添加。
return result;
}
}
动态代理类:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler handler)
loader:
一个ClassLoader对象,定义了由哪个ClassLoader对象,来对生产的代理进行加载。interfaces:
一个Interfaces数组,表示我将要给我所代理的对象提供一组什么样的接口, 如果提供一组接口给它,那么该代理对象就宣称实现了该接口,从而可以调用接口中的方法。 即,查找出真是主题类的所实现的所有的接口。handler:
一个InvocationHandler对象,表示当我这个动态代理对象在调用方法时,会关联到该InvocationHandler对象。 该InvocationHandler与主题类有着关联。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class DynamicProxy {
public static <T> T newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler handler){
@SuppressWarnings("unchecked")
T t = (T) Proxy.newProxyInstance(classLoader, interfaces, handler);
return t;
}
}
动态代理场景类:
import java.lang.reflect.InvocationHandler;
public class AOPClient {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
InvocationHandler handler = new MyInvocationHandler(realSubject);
ClassLoader classLoader = realSubject.getClass().getClassLoader();
Class<?>[] interfaces = realSubject.getClass().getInterfaces();
Subject proxySubect = DynamicProxy.newProxyInstance(classLoader, interfaces, handler);
proxySubect.doSomething("这是一个Dynamic AOP示例!!!");
}
}
执行结果:
前置通知被执行!
do something...这是一个Dynamic AOP示例!!!
后置通知被执行!
动态代理中invoke的动态调用:

动态代理类DynamicProxy是个纯粹的动态创建代理类通用类。
所以在具体业务中,可以在进一步封装具体的具有业务逻辑意义的DynamicProxy类。
代码如下
具体业务的动态代理:
import java.lang.reflect.InvocationHandler;
//具体业务的动态代理。
public class SubjectDynamicProxy extends DynamicProxy {
public static <T> T newProxyInstance(Subject subject){
ClassLoader classLoader = subject.getClass().getClassLoader();
Class<?>[] interfaces = subject.getClass().getInterfaces();
InvocationHandler handler = new MyInvocationHandler(subject);
T t = newProxyInstance(classLoader, interfaces, handler);
return t;
}
}