代理模式--学习记录

定义

代理模式(委托模式)
定义:对其他对象提供一种代理从而控制对这个对象的访问。
就是,代理类 代理 被代理类,来执行被代理类里的方法。

一般情况下,代理模式化有三个角色。

  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;
    }
}
原文地址:https://www.cnblogs.com/cmi-sh-love/p/dai-li-mo-shixue-xi-ji-lu.html