设计模式——代理模式2

2018-08-07  本文已影响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

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


上一篇下一篇

猜你喜欢

热点阅读