• (一)设计模式之代理模式


     

    一、前言

       AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

    二、案例

    以用户去飞猪上买票为例,很明显飞猪平台便是一个代理,飞猪平台在替用户购票的基础上,还增加了额外的功能,比如购票前要校验身份证,购票后需要提供凭证给用户。

    1、定义购票行为接口,包括获取身份证号、姓名、余额

    public interface TicketBuy {
    
        /**
         * 买票
         */
        void buy();
    
        /**
         * 获取身份证号
         * @return
         */
        String getId();
    
        /**
         * 获取姓名
         * @return
         */
        String getName();
    
        /**
         * 获取余额
         * @return
         */
        Double getMoney();
    }
    

      

    2、定义用户类,即被代理类

    public class Person implements TicketBuy {
    
        private String id;
    
        private String name;
    
        private Double money;
    
        @Override
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public Double getMoney() {
            return money;
        }
    
        public void setMoney(Double money) {
            this.money = money;
        }
    
        @Override
        public void buy() {
            this.money --;
            System.out.println("用户:"+this.getName()+"买一张票,余额还剩:"+money);
        }
    }

    3、定义飞猪平台类,即代理类

    public class FlyPig implements InvocationHandler {
    
        private Person target;
    
        /**
         * 获取被代理人资料
         * @param target
         * @return
         */
        public Object getInstance(Person target){
            this.target = target;
            Class clazz = target.getClass();
            return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
        }
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            System.out.println("飞猪平台校验身份证+姓名:"+this.target.getId()+"+"+this.target.getName());
    
            this.target.buy();
    
            System.out.println("打印凭证!");
    
            return null;
        }
    }

    4、测试类:

    public class Test {
    
        public static void main(String[] args) {
            Person person1 = new Person();
            person1.setName("张三");
            person1.setId("35052511111111111");
            person1.setMoney(100.00);
            TicketBuy proxy = (TicketBuy)(new FlyPig().getInstance(person1));
            proxy.buy();
        }
    
    }

    结果

    5、解析

  • 相关阅读:
    深度解密Go语言之sync.map
    Golang常用框架
    fclist :lang=zh
    linux常用硬件故障排查工具之dmesg
    Go进阶53:从零Go实现WebsocketH5RDP/VNC远程桌面客户端
    wsl 2 设置静态 DNS 服务地址及 Linux 和 Windows 主机网络互相访问设置
    go.定时邮件发送
    使用arthas定位问题
    在kubernetes中定义覆盖docker中的命令和参数
    NGINX转发端口后却跳转到80端口的解决方法
  • 原文地址:https://www.cnblogs.com/shyroke/p/11994852.html
Copyright © 2020-2023  润新知