美文网首页
设计模式——代理模式2

设计模式——代理模式2

作者: 追云的帆 | 来源:发表于2018-08-07 01:03 被阅读4次

    动态代理

    什么是动态代理?动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。
    常见的切面编程AOP(Abspect Oriented Programming),其核心就是采用了动态代理机制。

    动态代理.jpg
    从类图中看到增加了一个InvocationHandler接口和GamePlayIH类,作用就是产生一个对象的代理对象,其中InvocationHandle是JDK提供的动态代理接口,对被代理类的方法进行代理。

    动态代理类

    public class GamePlayH implements InvocationHandler {
        //被代理者
        Class cls = null;
        //被代理的实例
        Object obj = null;
        //代理的目标 
        public GamePlayH(Object _obj) {
            this.obj = _obj;
        }
        //调用被代理的方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object res = method.invoke(this.obj, args);
            return res;
        }
    }
    

    其中invoke方法是接口InvocationHandle定义必须实现的,它完成对真实方法的调用。动态代理是根据被代理的接口生成所有的方法,也就是给定一个接口,动态代理会宣称,我们已经实现该接口下的所有方法了。默认情况下所有方法的返回值都是空的,代理已经实现了它了,通过InvocationHandle接口,所有方法都由该Handle来进行处理,即所有被代理的方法都由InvocationHandle接管实际的处理任务。

    动态代理的场景

    public class Client {
        public static void main(String[] args) {
            //定义一个玩家
            IGamePlayer player = new GamePlayer("张三");
            //定义一个Handle
            InvocationHandler handle = new GamePlayH(player);
            //开始游戏 ,记下时间
            System.out.println("开始时间:2018-8-2 13:10");
            //获得类 的 class loader
            ClassLoader cl = player.getClass().getClassLoader();
            //动态产生一个代理类
            IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl, new Class[]{IGamePlayer.class}, handle);
            //登录
            proxy.login("yangfan", "123123");
            //操作
            proxy.killBoss();
            proxy.upgrade();
            //记录结束游戏时间
            System.out.println("结束时间:2018-8-2 15:10");
        }
    }
    

    结果:

    开始时间:2018-8-2 13:10
    登录名为yangfan的用户张三 登录成功
    张三在击杀小怪
    张三又升一级
    结束时间:2018-8-2 15:10

    既没有创建代理类,也没有实现IGamePlayer接口,这就是动态代理。进阶,在游戏登录后发送一个信息告知登录,防止盗号异地登录。

    修改后的动态代理

    public class GamePlayH2 implements InvocationHandler {
        //被代理者
        Class cls = null;
        //被代理的实例
        Object obj = null;
        //代理的目标 
        public GamePlayH2(Object _obj) {
            this.obj = _obj;
        }
        //调用被代理的方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object res = method.invoke(this.obj, args);
            //如果是登录方法那么进行信息反馈
            if(method.getName().equalsIgnoreCase("login")){
                System.out.println("有人在用我的账号登录");
            }
            return res;
        }
    }
    

    结果:

    开始时间:2018-8-2 13:10
    登录名为yangfan的用户张三 登录成功
    有人在用我的账号登录
    张三在击杀小怪
    张三又升一级
    结束时间:2018-8-2 15:10

    这就可以看作AOP编程,AOP编程没有使用新的技术,但是它对我们的设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不用考虑,在后期通过AOP的方式切过去。

    通用动态代理的模型:

    动态代理通用类图.jpg

    两条发展的路线。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的互相耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。

    抽象主题

    public interface Subject{
         //业务操作
         public void doSomething(String str);
    }
    

    真实主题

    public class RealSubject implements Subject {
        //业务操作
        @Override
        public void doSomething(String str) {
            System.out.println("do something"+str);
        }
    }
    

    动态代理的Handle类

    public class MyInvocationHandler implements InvocationHandler {
        //被代理的对象
        private Object target = null;
        //通过构造函数传递一个对象
        public MyInvocationHandler(Object _obj) {
            this.target=_obj;
        }
        //代理方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //执行被代理的方法
            return method.invoke(this.target, args);
        }
    }
    

    动态代理类

    public class DynamicProxy<T> {
        public static <T> T newProxyInstance(ClassLoader loader,Class<?>[] interfaces ,InvocationHandler h){
            //寻找JoinPoint连接点,AOP框架使用元数据定义
            if(true){
                //执行一个前置通知
                (new BeforeAdvice()).exec();
            }
            //执行目标返回结果:
            return (T) Proxy.newProxyInstance(loader, interfaces, h);
        }
    }
    

    通知接口及其实现

    public interface IAdvice {
        //通知的一个方法
        public void exec();
    }
    public class BeforeAdvice implements IAdvice{
        @Override
        public void exec() {
            System.out.println("我是前置通知,我被执行了!");
        }
    }
    

    场景

    public class Client {
        public static void main(String[] args) {
            //定义一个主题
            Subject subject = new RealSubject();
            //定义一个Handle
            InvocationHandler handler = new MyInvocationHandler(subject);
            //定义主题的代理
            Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
            //代理的行为
            proxy.doSomething("完成");
        }
    }
    

    结果:

    我是前置通知,我被执行了!
    do something完成

    DynamicProxy中我们有这样的方法:
    this.obj = Proxy.newProxyInstance( c.getClassLoader() , c.getInterfaces() , new MyInvocationHandler(_obj) )
    该方法是重新生成了一个对象,为什么要重新生成,因为需要使用代理。c.getInterfaces()是说查找到该类的所有接口,然后实现接口的所有方法。方法是空的,是由new MyInvocationHandler(_obj)这个对象负责接管。于是我们知道一个类的动态代理类是这样一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有的方法的实现,其动态调用过程如下图:

    动态代理调用过程示意图.jpg

    DynamicProxy它是一个通用类,不具有业务意义,可以产生一个它的实现类

    DynamicProxy的具体业务实现类

    import java.lang.reflect.InvocationHandler;
    
    public class SubjectDynamicProxy extends DynamicProxy {
        public static <T> T newProxyInstance(Subject subject){
            //获得ClassLoader
            ClassLoader loader = subject.getClass().getClassLoader();
            //获得接口数组
            Class<?>[] classes = subject.getClass().getInterfaces();
            //获得handler
            InvocationHandler handler = new MyInvocationHandler(subject);
            return newProxyInstance(loader, classes, handler);
        }
    }
    

    扩展以后,高层模块对代理的访问会更简单

    具体业务的场景

    public class Client_具体业务的场景 {
        public static void main(String[] args) {
            //定义一个主题
            Subject subject = new RealSubject();
            //定义主题的代理
            Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
            //代理的行为
            proxy.doSomething("Finish");
        }
    }
    

    结果:

    我是前置通知,我被执行了!
    do somethingFinish

    动态代理的主要意图就是解决我们常说的横切面编程,在不改变我们已有代码的结构的情况下增强或控制对象的行为。


    相关文章

      网友评论

          本文标题:设计模式——代理模式2

          本文链接:https://www.haomeiwen.com/subject/ecmnvftx.html