本文共 3034 字,大约阅读时间需要 10 分钟。
上篇讲述了普通代理模式,今天来讲讲动态代理.说起动态代理,大家可能首先想到的就是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");}
执行结果如下:
执行前置方法用户的图形验证码是:2222Exception in thread "main" java.lang.IllegalArgumentException: 图形验证码不正确
可以看到由于验证码并不匹配,所以在before方法中抛出异常,并未走到用户校验登录密码等逻辑,实现了提前业务处理.
接下来看验证码校验通过的情况,将login入参改为”1111”,执行结果如下:
执行前置方法用户的图形验证码是:1111动态代理执行器开始执行login方法!玩家white登录成功!!!!!执行后续方法
可以看到校验通过,用户登录成功.
看到这里不知道大家有没有想起平时所用的Spring AOP?是否觉得我们平时用的AOP就是这样的(当然此处没有引入切面的概念).当然有朋友说了,我们的AOP可以在多个方法上使用,而且不用修改AOP的代码,那这里比如此处如果我们要扩展上面的程序,给玩家添加注册功能,也要校验验证码,我们的代理类也是不需要做任何修改的,只需要在IPlayer和Player中增加regist方法即可实现.
看到这里大家是否明白了我们AOP中到底是怎么运用动态代理模式的了,就是我们写的AOP作为一个动态代理类,在执行完我们的预处理方法befor后,动态的获取到我们添加注解的方法所在的对象,之后调用我们添加注解的方法,之后再执行我们的后续处理after方法.
总结
欢迎关注个人博客:blog.scarlettbai.com