2、4 代理模式
定义
为其他对象提供一种代理以控制对这个对象的访问。
案例
案例情景 1-1
图形结构
代码示例
public class Client1 {
public static void main(String[] args) {
IGamePlayer gamePlayer = new GamePlayer("测试用户");
gamePlayer.login("testuser","123456");
gamePlayer.killBoss();
gamePlayer.upgrade();
}
}
interface IGamePlayer {
//登录游戏
void login(String user,String password);
//杀怪
void killBoss();
//升级
void upgrade();
}
class GamePlayer implements IGamePlayer{
private String name;
public GamePlayer(String name) {
this.name = name;
}
@Override
public void login(String user, String password) {
System.out.println("登录用户名:" + user + ",密码:" + password + ".");
}
@Override
public void killBoss() {
System.out.println(this.name + "在打怪.");
}
@Override
public void upgrade() {
System.out.println(this.name + "又升级了!");
}
}
案例情景 1-2 优化
图形结构
代码示例
public class Client2 {
public static void main(String[] args) {
IGamePlayer gamePlayer = new GamePlayer("测试用户");
IGamePlayer proxy = new GamePlayerProxy(gamePlayer);
proxy.login("usertest","123456");
proxy.killBoss();
proxy.upgrade();
}
}
interface IGamePlayer {
//登录游戏
void login(String user,String password);
//杀怪
void killBoss();
//升级
void upgrade();
}
class GamePlayer implements IGamePlayer{
private String name;
public GamePlayer(String name) {
this.name = name;
}
@Override
public void login(String user, String password) {
System.out.println("登录用户名:" + user + ",密码:" + password + ".");
}
@Override
public void killBoss() {
System.out.println(this.name + "在打怪.");
}
@Override
public void upgrade() {
System.out.println(this.name + "又升级了!");
}
}
class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer;
public GamePlayerProxy(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user,password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
}
案例 2 代理通用案例
图形结构
代码示例
public class Client3 {
}
/**
* 抽象主题类
*/
interface Subject{
void request();
}
/**
* 真实主题类
*/
class RealSubject implements Subject{
@Override
public void request() {
//业务逻辑处理
}
}
/**
* 代理类
*/
class Proxy implements Subject{
//要代理哪个实现类
private Subject subject = null;
//默认被代理类
public Proxy() {
this.subject = new Proxy();
}
//通过构造函数传递代理者
//一个代理类可以代理多个被委托者或被代理类,因此一个代理类具体代理哪个真实主题角色,是由场景类决定的。
//在通常情况下,一个接口只需要一个代理类就可以了,具体代理哪个实现类由高层模块来决定
//也就是在代理类的构造函数中传递被代理者
public Proxy(Object ...objects) {
}
//实现接口中定义的方法
@Override
public void request() {
this.before();
this.subject.request();
this.after();
}
//预处理
private void before(){
}
//善后处理
private void after(){
}
}
代理模式的应用
优点
1.职责清晰
真实的角色就是实现实际的业务逻辑,不用关系其他非本职责的事物,通过后期的代理完成一件事物,附带的结果就是编程简洁清晰。
2.高扩展性
具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱接口,那么我们的代理类完全就可以在不做任何修改的情况下使用。
3.智能化
比如动态代理、Struts 如何把表单元素映射到对象上的。
使用场景
为什么要使用代理?“其实是自己不想参与中间的是是非非”。
比如 SpringAOP 就是非常典型的动态代理。
1、远程代理,也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
(比如 webservice。)
2、虚拟代理,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
(这样可以达到性能的最优化,比如说打开一个很大的 HTML 网页,里面很多文字和图片,此时看到的是所有的文字,但是图片却是一张一张地下载后才能看到,那些未打开的图片框,就是通过虚拟代理来替代了真实的图片,此时代理存储了真实图片的路径和尺寸。)
3、安全代理,用来控制真实对象访问时的权限。
(一般用于对象应该有不同的访问权限的时候。)
4、智能指引,是指当时调用真实对象时,代理处理另外一些事。
(如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它;或当第一次引用一个持久化对象时,将它装入内存;或在访问一个实际对象前,检查是否已经锁定它,以确保其他对象不能改变它。它们都是通过代理在访问一个对象时附加一些内务处理。)
综上所述,代理模式其实就是在访问对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。代理模式就是真实对象的代表。
代理模式的扩展
普通代理
定义
客户端只能访问代理角色,而不能访问真实角色。
案例
图形结构
代码示例
public class Client4 {
public static void main(String[] args) {
IGamePlayer gamePlayerProxy = new GamePlayerProxy("测试用户");
gamePlayerProxy.login("testuser","testpassword");
gamePlayerProxy.killBoss();
gamePlayerProxy.upgrade();
}
}
interface IGamePlayer {
//登录游戏
void login(String user,String password);
//杀怪
void killBoss();
//升级
void upgrade();
}
class GamePlayer implements IGamePlayer{
private String name = "";
public GamePlayer(IGamePlayer gamePlayer,String name) throws Exception{
if(gamePlayer == null){
throw new Exception("不能创建真实角色!");
}else{
this.name = name;
}
}
@Override
public void login(String user, String password) {
System.out.println("登录名为:" + user + "的用户:" + this.name + "登录成功!");
}
@Override
public void killBoss() {
System.out.println(this.name + "在打怪.");
}
@Override
public void upgrade() {
System.out.println(this.name + "又升了一级!");
}
}
class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(String name) {
try {
//这里要实例化真实的类有点不太理解
this.gamePlayer = new GamePlayer(this,name);
}catch (Exception e){
//TODO
e.printStackTrace();
}
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user,password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
}
描述
在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,该模式非常适合对扩展性要求较高的场合。在实际的项目中,一般都是通过约定来禁止 new 一个真实的角色。
注意:普通代理模式的约束问题尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素。
强制代理
定义
必须通过真实角色查找到代理角色(不允许直接访问真实角色),否则不能访问。甭管是通过代理类还是通过直接 new 一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色,可以不用知道代理存在,但是你的所作所为还是需要代理为你提供。
案例
图形结构
代理示例
public class Client5 {
public static void main(String[] args) {
//直接访问真实角色
//必须通过代理来访问,不能直接访问它
System.out.println("---------直接访问真实角色begin---------");
IGamePlayer gamePlayer1 = new GamePlayer("测试用户");
gamePlayer1.login("testuser","testpassword");
gamePlayer1.killBoss();
gamePlayer1.upgrade();
System.out.println("---------直接访问真实角色end---------");
//直接访问代理类
//不能访问是由于不是真实角色指定的对象,这个代理对象是自己new出来的,当然真实对象不认了
System.out.println("---------直接访问代理类begin---------");
IGamePlayer gamePlayer2 = new GamePlayer("测试用户");
IGamePlayer gamePlayerProxy2 = new GamePlayerProxy(gamePlayer2);
gamePlayerProxy2.login("testuser","testpassword");
gamePlayerProxy2.killBoss();
gamePlayerProxy2.upgrade();
System.out.println("---------直接访问代理类end---------");
//强制代理的场景类
//强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。
//高层模块只要调用getProxy就可以访问真实角色的所有访问,
//它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
System.out.println("---------强制代理的场景类begin---------");
IGamePlayer gamePlayer3 = new GamePlayer("测试用户");
//获得指定的代理
IGamePlayer gamePlayerProxy3 = gamePlayer3.getProxy();
gamePlayerProxy3.login("testuser","testpassword");
gamePlayerProxy3.killBoss();
gamePlayerProxy3.upgrade();
System.out.println("---------强制代理的场景类end---------");
}
}
interface IGamePlayer{
//登录游戏
void login(String user,String password);
//杀怪
void killBoss();
//升级
void upgrade();
//每个人都可以找到自己的代理
IGamePlayer getProxy();
}
class GamePlayer implements IGamePlayer{
private String name;
private IGamePlayer proxy;
public GamePlayer(String name) {
this.name = name;
}
@Override
public IGamePlayer getProxy() {
//找到自己的代理
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
private boolean isProxy(){
if(this.proxy == null){
return Boolean.FALSE;
}else{
return Boolean.TRUE;
}
}
@Override
public void login(String user, String password) {
if(this.isProxy()){
System.out.println("登录名为:" + user + "的用户:" + this.name + "登录成功!");
}else{
System.out.println("login-请使用指定的代理访问!");
}
}
@Override
public void killBoss() {
if(this.isProxy()){
System.out.println(this.name + "在打怪.");
}else{
System.out.println("killBoss-请使用指定的代理访问!");
}
}
@Override
public void upgrade() {
if(this.isProxy()){
System.out.println(this.name + "又升了一级!");
}else{
System.out.println("upgrade-请使用指定的代理访问!");
}
}
}
class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer;
public GamePlayerProxy(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user,password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
@Override
public IGamePlayer getProxy() {
return this;
}
}
代理是有个性的
定义
一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上做增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。
代理类不仅仅是可以有自己的运算方法,通常的情况下,代理的职责并不一定单一,它可以组合其他的真实角色,也可以实现自己的职责,比如下述案例中的计算费用(count()方法)。
代理类可以为真实角色预处理消息、过滤消息、消息转发、事后处理消息等功能。当然一个代理类,可以代理多个真实角色,并且真实角色之间可以有耦合关系,可以自行扩展。
案例
图形结构
代码示例
public class Client6 {
public static void main(String[] args) {
IGamePlayer gamePlayer = new GamePlayer("测试用户");
//获得指定的代理
IGamePlayer gamePlayerProxy = gamePlayer.getProxy();
gamePlayerProxy.login("testuser","testpassword");
gamePlayerProxy.killBoss();
gamePlayerProxy.upgrade();
}
}
interface IGamePlayer{
//登录游戏
void login(String user,String password);
//杀怪
void killBoss();
//升级
void upgrade();
//每个人都可以找到自己的代理
IGamePlayer getProxy();
}
interface IProxy{
void count();
}
class GamePlayer implements IGamePlayer{
private String name;
private IGamePlayer proxy;
public GamePlayer(String name) {
this.name = name;
}
@Override
public IGamePlayer getProxy() {
//找到自己的代理
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
private boolean isProxy(){
if(this.proxy == null){
return Boolean.FALSE;
}else{
return Boolean.TRUE;
}
}
@Override
public void login(String user, String password) {
if(this.isProxy()){
System.out.println("登录名为:" + user + "的用户:" + this.name + "登录成功!");
}else{
System.out.println("login-请使用指定的代理访问!");
}
}
@Override
public void killBoss() {
if(this.isProxy()){
System.out.println(this.name + "在打怪.");
}else{
System.out.println("killBoss-请使用指定的代理访问!");
}
}
@Override
public void upgrade() {
if(this.isProxy()){
System.out.println(this.name + "又升了一级!");
}else{
System.out.println("upgrade-请使用指定的代理访问!");
}
}
}
class GamePlayerProxy implements IGamePlayer,IProxy{
private IGamePlayer gamePlayer;
public GamePlayerProxy(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user,password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
this.count();
}
@Override
public IGamePlayer getProxy() {
return this;
}
@Override
public void count() {
System.out.println("升级总费用是150元.");
}
}
动态代理(重点)
定义
动态代理是在实现阶段不用关心代理谁,而在运行阶段不指定代理哪一个对象。相对来说,自己写代理类的方式是静态代理。
动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”。
案例
案例 1
图形结构
代码示例 1-1
public class Client7 {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("测试用户");
InvocationHandler handler = new GamePlayerIH(player);
ClassLoader cl = player.getClass().getClassLoader();
//动态产生一个代理者
IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl,new Class[]{IGamePlayer.class},handler);
proxy.login("testuser","testpassword");
proxy.killBoss();
proxy.upgrade();
}
}
interface IGamePlayer {
//登录游戏
void login(String user,String password);
//杀怪
void killBoss();
//升级
void upgrade();
}
class GamePlayer implements IGamePlayer{
private String name;
public GamePlayer(String name) {
this.name = name;
}
@Override
public void login(String user, String password) {
System.out.println("登录用户名:" + user + ",密码:" + password + ".");
}
@Override
public void killBoss() {
System.out.println(this.name + "在打怪.");
}
@Override
public void upgrade() {
System.out.println(this.name + "又升级了!");
}
}
class GamePlayerIH implements InvocationHandler{
//被代理者
Class cls = null;
//被代理的实例
Object obj;
//我要代理谁
public GamePlayerIH(Object obj) {
this.obj = obj;
}
//调用被代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj,args);
return result;
}
}
代码示例 1-2(优化)
public class Client8 {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("测试用户");
InvocationHandler handler = new GamePlayerIH(player);
ClassLoader cl = player.getClass().getClassLoader();
//动态产生一个代理者
IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl,new Class[]{IGamePlayer.class},handler);
proxy.login("testuser","testpassword");
proxy.killBoss();
proxy.upgrade();
}
}
interface IGamePlayer {
//登录游戏
void login(String user,String password);
//杀怪
void killBoss();
//升级
void upgrade();
}
class GamePlayer implements IGamePlayer{
private String name;
public GamePlayer(String name) {
this.name = name;
}
@Override
public void login(String user, String password) {
System.out.println("登录用户名:" + user + ",密码:" + password + ".");
}
@Override
public void killBoss() {
System.out.println(this.name + "在打怪.");
}
@Override
public void upgrade() {
System.out.println(this.name + "又升级了!");
}
}
class GamePlayerIH implements InvocationHandler {
//被代理者
Class cls = null;
//被代理的实例
Object obj;
//我要代理谁
public GamePlayerIH(Object obj) {
this.obj = obj;
}
//调用被代理的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj,args);
//如果是登录方法,则发送信息
if(method.getName().equalsIgnoreCase("login")){
System.out.println("有人在用我的账号登录!");
}
return result;
}
}
案例 2(动态代理模板)
描述
动态代理实现代理的职责,业务逻辑 Subject 实现相关的逻辑功能,两者之间没有必然的相互耦合关系。通知 Advice 从另一个切面切入,最终在高层模块也就是 Client 进行耦合,完成逻辑的封装任务。
图形结构(动态代理通用类图)
代码示例 1-1
public class Client9 {
//动态代理场景
public static void main(String[] args) {
//定义一个主题
Subject subject = new RealSubject();
//定义一个Handler
InvocationHandler handler = new MyInvocationHandler(subject);
//定义主题的代理
Subject proxy = DynamicProxy.newProxyInstance(
subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(),
handler);
//代理的行为,当这里和输出"3"的地方打debug时候"3"这个会输出多行
proxy.doSomething("finish");
System.out.println("..............");
}
}
/**
* 抽象主题
*/
interface Subject{
//业务操作
void doSomething(String str);
}
/**
* 真实主题
*/
class RealSubject implements Subject{
//业务操作
@Override
public void doSomething(String str) {
System.out.println("-----------doSomething--------->" + str);
}
}
/**
* 动态代理的Handler类
*/
class MyInvocationHandler implements InvocationHandler{
//被代理的对象
private Object target = null;
//通过构造函数传递一个对象
public MyInvocationHandler(Object target) {
this.target = target;
}
//代理方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行被代理的方法
//所有通过动态代理实现的方法全部通过invoke方法调用
System.out.println("33333333333333333333");
return method.invoke(this.target,args);
}
}
/**
* 动态代理类
* (这里插入了较多的AOP术语,如在什么地方/连接点执行什么行为/通知)
* @param <T>
*/
class DynamicProxy<T>{
public static <T> T newProxyInstance(
ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler){
System.out.println("1111111111111111111111");
//寻找JoinPoint的连接点,AOP框架使用元数据定义
if(true){
//执行一个前置通知
(new BeforeAdvie()).exec();
}
System.out.println("222222222222222222222");
//执行目标并返回结果
return (T) Proxy.newProxyInstance(loader,interfaces,handler);
}
}
/**
* 通知接口(要切入的类)
*/
interface IAdvice{
//通知只有一个方法,执行即可。
void exec();
}
/**
* 通知接口实现
*/
class BeforeAdvie implements IAdvice{
@Override
public void exec() {
System.out.println("我是前置通知,我被执行了!");
}
}
输出结果:
1111111111111111111111
我是前置通知,我被执行了!
222222222222222222222
33333333333333333333
-----------doSomething--------->finish
描述及调用过程图示
有这段代码
“
this.obj = Proxy.newProxyInstance(
subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),
new MyInvocationHandler(subject));
”
该方法是重新生成了一个对象,重新生成对象的原因是要使用代理,注意
“subject.getClass().getInterfaces()”这句话,是说查找到该类的所有接口,然后实现接口的所有方法。当然了,方法都是空的,由谁具体负责接管呢?是“new MyInvocationHandler(subject)”这个对象。于是我们知道一个类的动态代理类是这样的一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现,其动态调用过程如下:
代码示例 1-2(优化)
加入业务相关的动态代理处理方式
public class Client10 {
public static void main(String[] args) {
//定义一个主题
Subject subject = new RealSubject();
//定义主题的代理
Subject proxy = SubjectDynamicProxy.newProxyInstanceSubject(subject);
proxy.doSomething("halou");
}
}
/**
* 抽象主题
*/
interface Subject{
//业务操作
void doSomething(String str);
}
/**
* 真实主题
*/
class RealSubject implements Subject{
//业务操作
@Override
public void doSomething(String str) {
System.out.println("-----------doSomething--------->" + str);
}
}
/**
* 动态代理的Handler类
*/
class MyInvocationHandler implements InvocationHandler{
//被代理的对象
private Object target = null;
//通过构造函数传递一个对象
public MyInvocationHandler(Object target) {
this.target = target;
}
//代理方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行被代理的方法
//所有通过动态代理实现的方法全部通过invoke方法调用
System.out.println("33333333333333333333");
return method.invoke(this.target,args);
}
}
/**
* 动态代理类(通用类而不具有业务意义)
* (这里插入了较多的AOP术语,如在什么地方/连接点执行什么行为/通知)
* @param <T>
*/
class DynamicProxy<T>{
public static <T> T newProxyInstance(
ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler){
System.out.println("1111111111111111111111");
//寻找JoinPoint的连接点,AOP框架使用元数据定义
if(true){
//执行一个前置通知
(new BeforeAdvie()).exec();
}
System.out.println("222222222222222222222");
//执行目标并返回结果
return (T) Proxy.newProxyInstance(loader,interfaces,handler);
}
}
/**
* 动态代理业务实现类
*/
class SubjectDynamicProxy extends DynamicProxy{
public static <T> T newProxyInstanceSubject(Subject subject){
//获得ClassLoader
ClassLoader loader = subject.getClass().getClassLoader();
//获得接口数组
Class<?>[] classes = subject.getClass().getInterfaces();
//获得handler
InvocationHandler handler = new MyInvocationHandler(subject);
return newProxyInstance(loader,classes,handler);
}
}
/**
* 通知接口(要切入的类)
*/
interface IAdvice{
//通知只有一个方法,执行即可。
void exec();
}
/**
* 通知接口实现
*/
class BeforeAdvie implements IAdvice{
@Override
public void exec() {
System.out.println("我是前置通知,我被执行了!");
}
}
输出结果:
1111111111111111111111
我是前置通知,我被执行了!
222222222222222222222
33333333333333333333
-----------doSomething--------->halou
以上的动态代理是一个通用代理框架。如果想设计自己的 AOP 框架,完全可以在此基础上扩展,这里设计的是一个通用代理,只要有一个接口,一个实现类,就可以使用该代理,完成代理的所有功效。
与静态代理区别
看“父类”DynamicProxy,动态代理的主要意图就是解决我们常说的“审计”问题,也就是横切面编程,在不改变我们已有代码结构的情况下增强或控制对象的行为。
实现动态代理的首要条件
1、被代理类必须实现一个接口;2、CGLIB。
最佳实践 AOP
对 SpringAOP 和 AspectJ 这样优秀的工具进行调试时,只要看到 $Proxy()这样的结构,就应该知道这是一个动态代理了。
在学习 SpringAOP 框架时,只要弄清这几个名词就成:切面(Aspect),切入点(JoinPoint),通知(Advice),织入(Weave)就足够了。
标题:设计模式(四)结构型模式(对象结构型模式)代理模式-PROXY
作者:yazong
地址:https://blog.llyweb.com/articles/2020/05/13/1589302717589.html