【设计模式】动态代理模式

上篇讲述了普通代理模式,今天来讲讲动态代理.说起动态代理,大家可能首先想到的就是Spring的AOP.我们天天在说AOP是通过动态代理实现的,那么动态代理到底是个什么呢?看完这篇文章你就会明白,同时也明白AOP到底是哪里用到了动态代理.

    首先,我们来看动态代理的定义:动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象的一种特殊代理模式。

    接下来还是通过实例来学习吧,假定现有用户登录需求,在登录时需要校验验证码,校验验证码这个逻辑严格来讲其实是不属于登录逻辑的,只是一种额外的防刷校验,防止用户通过重复密码碰撞来实现密码破解,所以此时我们是不应该把他放进登录逻辑里,so,此处引入动态代理模式,校验逻辑交由代理类执行,接下来我们来看动态代理模式类图:

动态代理模式

    其中InvocationHandler是javareflect包中用于实现动态代理模式的一个核心接口,通过实现它并实现invoke接口来实现我们自己的逻辑.接下来我们来看具体代码实现:

    首先基,于抽象化,先定义接口IPlayer:

public interface IPlayer {
    /**
     * 登录
     */
    void login(String captcha);
}

    接下来我们看具体的玩家实现类Player:

public class Player implements IPlayer {
    /** 玩家姓名 */
    private String        name;

    public Player(String name) {
        this.name = name;
    }

    @Override
    public void login(String captcha) {
        //此处校验登录密码等
        System.out.println(MessageFormat.format("玩家{0}登录成功!!!!!", name));
    }
}   

    可以看到我们玩家的实现中并不会对图形验证码做处理,只会执行自有登录逻辑.接下来我们来看核心类PlayerHandler:

public class PlayerHandler implements InvocationHandler {

    private Object targetObj;

    /**
     * 代理方法,通过该方法返回具体的代理类
     */
    public Object proxy(Object sourceObj) {
        this.targetObj = sourceObj;
        return Proxy.newProxyInstance(sourceObj.getClass().getClassLoader(), sourceObj.getClass().getInterfaces(),
            this);
    }

    /**
     * 前置包装方法  此处业务为校验验证码
     */
    private void before(Method method, Object[] args) {
        System.out.println("执行前置方法");
        String captcha = args[0].toString();
        System.out.println(MessageFormat.format("用户的图形验证码是:{0}", captcha));
        if (!"1111".equals(captcha)) {
            throw new IllegalArgumentException("图形验证码不正确");
        }
    }

    /**
     * 后续包装方法
     */
    private void after(Method method, Object[] args) {
        System.out.println("执行后续方法");
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行前置方法
        before(method, args);
        System.out.println(MessageFormat.format("动态代理执行器开始执行{0}方法!", method.getName()));
        //这一步会调用代理的具体对象执行具体方法
        Object result = method.invoke(targetObj, args);
        //执行后续方法
        after(method, args);
        return result;
    }
}

    上述代码中注释已经很详细了,如有不明白的可以留言.此处我们直接来看client调用:

public static void main(String[] args) {
    IPlayer player = (IPlayer) new PlayerHandler().proxy(new Player("white"));
    player.login("2222");
}

    执行结果如下:

执行前置方法
用户的图形验证码是:2222
Exception in thread "main" java.lang.IllegalArgumentException: 图形验证码不正确

    可以看到由于验证码并不匹配,所以在before方法中抛出异常,并未走到用户校验登录密码等逻辑,实现了提前业务处理.

    接下来看验证码校验通过的情况,将login入参改为”1111”,执行结果如下:

执行前置方法
用户的图形验证码是:1111
动态代理执行器开始执行login方法!
玩家white登录成功!!!!!
执行后续方法

    可以看到校验通过,用户登录成功.

    看到这里不知道大家有没有想起平时所用的Spring AOP?是否觉得我们平时用的AOP就是这样的(当然此处没有引入切面的概念).当然有朋友说了,我们的AOP可以在多个方法上使用,而且不用修改AOP的代码,那这里比如此处如果我们要扩展上面的程序,给玩家添加注册功能,也要校验验证码,我们的代理类也是不需要做任何修改的,只需要在IPlayer和Player中增加regist方法即可实现.

    看到这里大家是否明白了我们AOP中到底是怎么运用动态代理模式的了,就是我们写的AOP作为一个动态代理类,在执行完我们的预处理方法befor后,动态的获取到我们添加注解的方法所在的对象,之后调用我们添加注解的方法,之后再执行我们的后续处理after方法.

总结

  1. 隔离性:动态代理类不关心原有业务逻辑,只关心自己的前后处理逻辑
  2. 扩展性:增加其他业务时只要前后处理逻辑相同,不用修改代理类代码,只需新增业务接口即可

欢迎关注个人博客:blog.scarlettbai.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值