• 代理模式--学习记录


    定义

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

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

      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;
        }
    }
    
  • 相关阅读:
    需要我们了解的SQL Server阻塞原因与解决方法
    SQL Server应用模式之OLTP系统性能分析
    第一章 scala环境搭建
    IO
    装饰器模式
    java 泛型方法
    文件格式转换
    spring ioc
    深入浅出Java模式设计之模板方法模式
    struts2
  • 原文地址:https://www.cnblogs.com/cmi-sh-love/p/dai-li-mo-shixue-xi-ji-lu.html
Copyright © 2020-2023  润新知