zoukankan      html  css  js  c++  java
  • Spring之静态/动态代理模式

    代理模式

    为什么要学习代理模式,因为AOP的底层机制就是动态代理!

    代理模式:

    • 静态代理
    • 动态代理

    学习aop之前 , 我们要先了解一下代理模式!

    img

    静态代理

    静态代理角色分析

    • 抽象角色 : 一般使用接口或者抽象类来实现
    • 真实角色 : 被代理的角色
    • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
    • 客户 : 使用代理角色来进行一些操作 .

    代码实现

    Rent . java 即抽象角色

    //抽象角色:租房
    
    public interface Rent {
    
        public void rent();
    
    }
    

    Host . java 即真实角色

    //真实角色: 房东,房东要出租房子
    
    public class Host implements Rent{
    
        public void rent() {
    
            System.out.println("房屋出租");
    
        }
    
    }
    

    Proxy . java 即代理角色

    //代理角色:中介
    
    public class Proxy implements Rent {
    
        private Host host;
    
        public Proxy() { }
    
        public Proxy(Host host) {
    
            this.host = host;
    
        }
    
        //租房
    
        public void rent(){
    
            seeHouse();
    
            host.rent();
    
            fare();
    
        }
    
        //看房
    
        public void seeHouse(){
    
            System.out.println("带房客看房");
    
        }
    
        //收中介费
    
        public void fare(){
    
            System.out.println("收中介费");
    
        }
    
    }
    

    Client . java 即客户

    //客户类,一般客户都会去找代理!
    
    public class Client {
    
        public static void main(String[] args) {
    
            //房东要租房
    
            Host host = new Host();
    
            //中介帮助房东
    
            Proxy proxy = new Proxy(host);
    
            //你去找中介!
    
            proxy.rent();
    
        }
    
    }
    

    分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。

    静态代理的好处:

    • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
    • 公共的业务由代理来完成 . 实现了业务的分工 ,
    • 公共业务发生扩展时变得更加集中和方便 .

    缺点 :

    • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

    我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

    静态代理再理解

    练习步骤:

    1、创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!

    //抽象角色:增删改查业务
    
    public interface UserService {
    
        void add();
    
        void delete();
    
        void update();
    
        void query();
    
    }
    

    2、我们需要一个真实对象来完成这些增删改查操作

    //真实对象,完成增删改查操作的人
    
    public class UserServiceImpl implements UserService {
    
        public void add() {
    
            System.out.println("增加了一个用户");
    
        }
    
        public void delete() {
    
            System.out.println("删除了一个用户");
    
        }
    
        public void update() {
    
            System.out.println("更新了一个用户");
    
        }
    
        public void query() {
    
            System.out.println("查询了一个用户");
    
        }
    
    }
    

    3、需求来了,现在我们需要增加一个日志功能,怎么实现!

    • 思路1 :在实现类上增加代码 【麻烦!】
    • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!

    4、设置一个代理类来处理日志!代理角色

    //代理角色,在这里面增加日志的实现
    
    public class UserServiceProxy implements UserService {
    
        private UserServiceImpl userService;
    
        public void setUserService(UserServiceImpl userService) {
    
            this.userService = userService;
    
        }
    
        public void add() {
    
            log("add");
    
            userService.add();
    
        }
    
        public void delete() {
    
            log("delete");
    
            userService.delete();
    
        }
    
        public void update() {
    
            log("update");
    
            userService.update();
    
        }
    
        public void query() {
    
            log("query");
    
            userService.query();
    
        }
    
        public void log(String msg){
    
            System.out.println("执行了"+msg+"方法");
    
        }
    
    }
    

    5、测试访问类:

    public class Client {
    
        public static void main(String[] args) {
    
            //真实业务
    
            UserServiceImpl userService = new UserServiceImpl();
    
            //代理类
    
            UserServiceProxy proxy = new UserServiceProxy();
    
            //使用代理类实现日志功能!
    
            proxy.setUserService(userService);
    
            proxy.add();
    
        }
    
    }
    

    我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想

    聊聊AOP:纵向开发,横向开发

    img

    动态代理

    • 动态代理的角色和静态代理的一样 .

    • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

    • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

      • 基于接口的动态代理----JDK动态代理 目标对象必须有接口没有接口不能实现jdk版动态代理
      • 基于类的动态代理--cglib
      • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
      • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、

    动态代理原理

    方式一:基于接口的动态代理

    核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看

    动态代理∶不用手动编写一个代理对象,不需要一编写与目标对象相同的方法,这个过程,在运行时的内存中动态生成代理对象。------字节码对象级别的代理对象

    - static Object newProxyInstance​(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回指定接口的代理实例,
    该代理实例将方法调用分派给指定的调用处理程序。 
    
    - 参数解释:
    
             *      ClassLoader:类加载器
             *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
             *      Class[]:字节码数组
             *          它是用于让代理对象和被代理对象有相同方法。固定写法。
             *      InvocationHandler:用于提供增强的代码
             *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
             *          此接口的实现类都是谁用谁写。
    
    
    **代码举例**
    
    ```php
    /**
     * 对生产厂家要求的接口
     */
    public interface IProducer {
    
        /**
         * 销售
         * @param money
         */
        public void saleProduct(float money);
    
        /**
         * 售后
         * @param money
         */
        public void afterService(float money);
    }
    
    /**
     * 一个生产者
     */
    public class Producer implements IProducer{
    
        /**
         * 销售
         * @param money
         */
        public void saleProduct(float money){
            System.out.println("销售产品,并拿到钱:"+money);
        }
    
        /**
         * 售后
         * @param money
         */
        public void afterService(float money){
            System.out.println("提供售后服务,并拿到钱:"+money);
        }
    }
    
    
    /**
     * 模拟一个消费者
     */
    public class Client {
    
        public static void main(String[] args) {
            final Producer producer = new Producer();
    
            /**
             * 动态代理:
             *  特点:字节码随用随创建,随用随加载
             *  作用:不修改源码的基础上对方法增强
             *  分类:
             *      基于接口的动态代理
             *      基于子类的动态代理
             *  基于接口的动态代理:
             *      涉及的类:Proxy
             *      提供者:JDK官方
             *  如何创建代理对象:
             *      使用Proxy类中的newProxyInstance方法
             *  创建代理对象的要求:
             *      被代理类最少实现一个接口,如果没有则不能使用
             *  newProxyInstance方法的参数:
             *      ClassLoader:类加载器
             *          它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
             *      Class[]:字节码数组
             *          它是用于让代理对象和被代理对象有相同方法。固定写法。
             *      InvocationHandler:用于提供增强的代码
             *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
             *          此接口的实现类都是谁用谁写。
             */
           IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                    producer.getClass().getInterfaces(),
                    new InvocationHandler() {
                        /**
                         * 作用:执行被代理对象的任何接口方法都会经过该方法
                         * 方法参数的含义
                         * @param proxy   代理对象的引用
                         * @param method  当前执行的方法
                         * @param args    当前执行方法所需的参数
                         * @return        和被代理对象方法有相同的返回值
                         * @throws Throwable
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //提供增强的代码
                            Object returnValue = null;
    
                            //1.获取方法执行的参数
                            Float money = (Float)args[0];
                            //2.判断当前方法是不是销售
                            if("saleProduct".equals(method.getName())) {
                                returnValue = method.invoke(producer, money*0.8f);
                            }
                            return returnValue;
                        }
                    });
            proxyProducer.saleProduct(10000f);
        }
    }
    

    方式二:基于子类的动态代理

    要求:被代理类不能是最终类

    第一步:导入jar包

    <dependencies>
    
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>2.1_3</version>
            </dependency>
    
        </dependencies>
    

    代码举例

    Producer.java

    /**
     * 一个生产者
     */
    public class Producer {
    
        /**
         * 销售
         * @param money
         */
        public void saleProduct(float money){
            System.out.println("销售产品,并拿到钱:"+money);
        }
    
        /**
         * 售后
         * @param money
         */
        public void afterService(float money){
            System.out.println("提供售后服务,并拿到钱:"+money);
        }
    }
    
    

    Client.java

    /**
     * 模拟一个消费者
     */
    public class Client {
    
        public static void main(String[] args) {
            final Producer producer = new Producer();
    
            /**
             * 动态代理:
             *  特点:字节码随用随创建,随用随加载
             *  作用:不修改源码的基础上对方法增强
             *  分类:
             *      基于接口的动态代理
             *      基于子类的动态代理
             *  基于子类的动态代理:
             *      涉及的类:Enhancer
             *      提供者:第三方cglib库
             *  如何创建代理对象:
             *      使用Enhancer类中的create方法
             *  创建代理对象的要求:
             *      被代理类不能是最终类
             *  create方法的参数:
             *      Class:字节码
             *          它是用于指定被代理对象的字节码。
             *
             *      Callback:用于提供增强的代码
             *          它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
             *          此接口的实现类都是谁用谁写。
             *          我们一般写的都是该接口的子接口实现类:MethodInterceptor
             */
            Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
                /**
                 * 执行北地阿里对象的任何方法都会经过该方法
                 * @param proxy
                 * @param method
                 * @param args
                 *    以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
                 * @param methodProxy :当前执行方法的代理对象
                 * @return
                 * @throws Throwable
                 */
                @Override
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    //提供增强的代码
                    Object returnValue = null;
    
                    //1.获取方法执行的参数
                    Float money = (Float)args[0];
                    //2.判断当前方法是不是销售
                    if("saleProduct".equals(method.getName())) {
                        returnValue = method.invoke(producer, money*0.8f);
                    }
                    return returnValue;
                }
            });
            cglibProducer.saleProduct(12000f);
        }
    }
    
    

    动态代理的好处

    静态代理有的它都有,静态代理没有的,它也有!

    • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
    • 公共的业务由代理来完成 . 实现了业务的分工 ,
    • 公共业务发生扩展时变得更加集中和方便 .
    • 一个动态代理 , 一般代理某一类业务
    • 一个动态代理可以代理多个类,代理的是接口!

    B站地址: https://www.bilibili.com/video/BV1WE411d7Dv?p=19

  • 相关阅读:
    物理课件开发记录之一2013年10月25日
    以as中的泛型数组举一例看帮助手册优化的好处
    flash的显示列表的机制探索一
    组合模式
    actionscript中重写注意事项
    用adobe air写一个金鱼监控程序
    adobe air桌面应用程序在前端显示,类似于暴风的总是在桌面的最上方
    windows7下的cmd命令之powercfg命令,不常用的
    设置默认访问项目的客户端的浏览器版本(IE版本)
    防火墙设置对外开放端口
  • 原文地址:https://www.cnblogs.com/ITHSZ/p/13848251.html
Copyright © 2011-2022 走看看