zoukankan      html  css  js  c++  java
  • 代理模式

    代理介绍:

    代理(Proxy)是一种设计模式, 提供了对目标对象另外的访问方式;即通过代理访问目标对象。 这样好处: 可以在目标对象实现的基础上,增强额外的功能操作。(扩展目标对象的功能)。

    可以做到在不修改目标对象的功能前提下,对目标对象功能拓展。

    简单例子:

    • 现在我是一个明星,拥有很多粉丝。粉丝希望我唱歌给他们听,但是如果都是我来接应他们,我岂不是很忙….于是乎,我就去找了个经纪人。这个经纪人就代表了我。当粉丝想要我唱歌的时候,应该是找经纪人,告诉经纪人想让我唱歌。

    • 现在我越来越红了,不是粉丝想要我唱歌,我就唱了。我要收费了。但是呢,作为一个公众人物,不可能是我自己说:我要收10000万,我才会去唱歌。于是这就让经纪人对粉丝说:只有10000万,我才会唱歌。

    • 无论外界是想要我干什么,都要经过我的经纪人。我的经纪人也会在其中考虑收费、推脱它们的请求。

    经纪人就是代理,实际上台唱歌、表演的还是我

    静态代理

    直接使用例子来说明吧…现在我有一个IUserDao的接口,拥有save方法()

    1 // 接口
    2 public interface IUserDao {
    3 
    4     void save();
    5 }
    • UserDao实现该接口,重写save()方法
    public class UserDao implements IUserDao{
    
        @Override
        public void save() {
            System.out.println("-----已经保存数据!!!------");
        }
    
    }

    现在,我想要在save()方法保存数据前开启事务、保存数据之后关闭事务…(当然啦,直接再上面写不就行了吗…业务方法少的时候,确实没毛病…)

    public void save() {
    
            System.out.println("开启事务");
            System.out.println("-----已经保存数据!!!------");
    
            System.out.println("关闭事务");
        }

    但是呢,现在如果我有好多好多个业务方法都需要开启事务、关闭事务呢?

    public void save() {
    
            System.out.println("开启事务");
            System.out.println("-----已经保存数据!!!------");
    
            System.out.println("关闭事务");
        }
        public void delete() {
    
            System.out.println("开启事务");
            System.out.println("-----已经保存数据!!!------");
    
            System.out.println("关闭事务");
        }
        public void update() {
    
            System.out.println("开启事务");
            System.out.println("-----已经保存数据!!!------");
    
            System.out.println("关闭事务");
        }
        public void login() {
    
            System.out.println("开启事务");
            System.out.println("-----已经保存数据!!!------");
    
            System.out.println("关闭事务");
        }

    我们发现就有了很多很多的重复代码了…我们要做的就是:当用户调用UserDao方法的时候,找的是代理对象、而代理帮我在解决这么繁琐的代码

    于是呢,我们就请了一个代理了

    • 这个代理要和userDao有相同的方法…没有相同的方法的话,用户怎么调用啊??
    • 代理只是对userDao进行增强,真正做事的还是userDao..

    因此,我们的代理就要实现IUserDao接口,这样的话,代理就跟userDao有相同的方法了。

    public class UserDaoProxy implements IUserDao{
    
        // 接收保存目标对象【真正做事的还是UserDao】,因此需要维护userDao的引用
        private IUserDao target;
        public UserDaoProxy(IUserDao target) {
            this.target = target;
        }
    
        @Override
        public void save() {
            System.out.println("开始事务...");
    
            target.save();          // 执行目标对象的方法
    
            System.out.println("提交事务...");
        }
    }

    外界并不是直接去找UserDao,而是要通过代理才能找到userDao

     public static void main(String[] args) {
            // 目标对象
            IUserDao target = new UserDao();
    
            // 代理
            IUserDao proxy = new UserDaoProxy(target);
            proxy.save();  // 执行的是,代理的方法
        }

    这样一来,我们在UserDao中就不用写那么傻逼的代码了…傻逼的事情都交给代理去干了…

    为什么要用动态代理?

    我们首先来看一下静态代理的不足:

    • 如果接口改了,代理的也要跟着改,很烦!
    • 因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。

    动态代理比静态代理好的地方:

    • 代理对象,不需要实现接口【就不会有太多的代理类了】
    • 代理对象的生成,是利用JDKAPI, 动态地在内存中构建代理对象(需要我们指定创建 代理对象/目标对象 实现的接口的类型;)

    java中提供了一个代理对象。

    • 参数一:生成代理对象使用哪个类装载器【一般我们使用的是代理类的装载器】
    • 参数二:生成哪个对象的代理对象,通过接口指定【指定要代理类的接口】
    • 参数三:生成的代理对象的方法里干什么事【实现handler接口,我们想怎么实现就怎么实现】

    在编写动态代理之前,要明确两个概念:

    • 代理对象拥有目标对象相同的方法【因为参数二指定了对象的接口】
    • 用户调用代理对象的什么方法,都是在调用处理器的invoke方法。
    • 使用JDK动态代理必须要有接口【参数二需要接口】

    对象

    小明是一个明星,拥有唱歌和跳舞的方法。实现了人的接口

     1 public class XiaoMing implements Person {
     2 
     3     @Override
     4     public void sing(String name) {
     5 
     6         System.out.println("小明唱" + name);
     7     }
     8 
     9     @Override
    10     public void dance(String name) {
    11 
    12         System.out.println("小明跳" + name);
    13 
    14     }
    15 }

    接口

    public interface Person {
        void sing(String name);
    
        void dance(String name);
    }

    代理类

    public class XiaoMingProxy {
    
        //代理只是一个中介,实际干活的还是小明,于是需要在代理类上维护小明这个变量
        XiaoMing xiaoMing = new XiaoMing();
    
    
        //返回代理对象
        public Person getProxy() {
    
            /**
             * 参数一:代理类的类加载器
             * 参数二:被代理对象的接口
             * 参数三:InvocationHandler实现类
             */
            return (Person)Proxy.newProxyInstance(XiaoMingProxy.class.getClassLoader(), xiaoMing.getClass().getInterfaces(), new InvocationHandler() {
                            用哪个类装载器,                                  利用反射得到接口的名称             实现代理对象。能做什么事。
                /**
                 * proxy : 把代理对象自己传递进来
                 * method:把代理对象当前调用的方法传递进来
                 * args:把方法参数传递进来
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                    //如果别人想要让小明唱歌
                    if (method.getName().equals("sing")) {
    
                        System.out.println("给1000万来再唱");
    
                        //实际上唱歌的还是小明
                        method.invoke(xiaoMing, args);
                    }
                    return null;
                }
            });
    
        }
    }

    测试类

      public static void main(String[] args) {
    
            //外界通过代理才能让小明唱歌
            XiaoMingProxy xiaoMingProxy = new XiaoMingProxy();
            Person proxy = xiaoMingProxy.getProxy();
    
    
            proxy.sing("我爱你");
    
        }
  • 相关阅读:
    手把手实战:eclipse 搭建 SpringMvc 框架环境
    解决eclipse中Tomcat服务器的server location选项不能修改的问题
    如何解决JSP页面顶端报错 The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
    第二课 --- git的(管理修改和撤销修改、删除文件)
    第二课 ---git时光穿梭(版本回退)
    第一课——git的简介和基本使用
    001——使用composer安装ThinkPHP5
    微信小程序中对于变量的定义
    微信小程序onLaunch修改globalData的值
    7——ThinkPhp中的响应和重定向:
  • 原文地址:https://www.cnblogs.com/bulrush/p/8681291.html
Copyright © 2011-2022 走看看