美文网首页
java反射的运用之一动态代理

java反射的运用之一动态代理

作者: 程er狗 | 来源:发表于2018-10-19 23:23 被阅读190次

代理分类:

1.静态代理
2.动态代理

业务场景:
假设程二狗想买一台小米手机,陈雪峰正好开了一家手机专卖店买,程二狗通过陈雪峰的手机专卖店买到小米公司生产的手机,而不是去小米的总部去购买。(小米总部的责职只是负责生产小米手机)
在这个过程中,程二狗想买小米手机,是发出行为的人,是被代理的人,但是他并没有实际去做这件事情,而是把这件事交给陈雪峰来帮他完成;陈雪峰替程二狗买到了手机,是中间人,是代理人,他替程二狗完成了这件事,是代理人。
-------这就是静态代理:代理类(陈雪峰)和目标对象(被代理类)(程二狗)的类都是程序在编译期间就确定下来的,不利于程序的扩张,同时,每一个代理类只能为一个接口服务(陈雪峰就只能替别人买手机,其它的事情都做不了),这样一来程序在开发中必然产生过多的代理类

突然有一天,陈雪峰NB了,他什么事情都能代替陈二狗做,不管陈二狗要买手机、还是要买房、还是办证啥的,只要陈二狗提出相应的需求,他都能帮陈二狗完成。
--------这就是动态代理:就好比陈雪峰开了一家店,能满足不同客户的不同需求,他的店有一点像‘万事屋’的性质。如此一来,只要一个动态代理类,就能完成所有的事

一.先看静态代理:一个代理类只能帮一个目标对象(被代理类)完成一件事
package com.ergou.spirngboot;
import org.junit.Test;
public class ProxyTest {
    //1.声明接口(房屋出售)
    interface HouseSale{
       void SaleHouse();
    }
    //2.代理类和被代理类同时实现接口
    //中介人 代理类(拿到房源)
    class Agencyer implements HouseSale{
        private Customer customer;

        public Agencyer(Customer customer) {
            this.customer = customer;
        }

        @Override
        public void SaleHouse() {
            customer.SaleHouse();
            System.out.println("我是房产中介人,我能替你买房子!但要收费");
            System.out.println("我替你买到房子了");
        }
    }
    //消费者 被代理类 (想要买房子)
    class Customer implements HouseSale{
        @Override
        public void SaleHouse() {
            System.out.println("我是实际买房者,我想买房!");
        }
    }
    @Test
    public void test(){
        //顾客发起需求--》中间人找到房源--》中间人替你完成买房
         Customer customer=new Customer();
         Agencyer a = new Agencyer(customer);
         a.SaleHouse();
         //我是实际买房者,我想买房!
         //我是房产中介人,我能替你买房子!但要收费
         //我替你买到房子了
    }
}

核心代码

代理类



执行:给被代理类赋值


静态代理实现总结:
1.创建一个接口
2.创建代理类(注:一定要拥有被代理类这个属性)和被代理类两个类实现1创建的接口
3.执行:3.1创建被代理类的对象(目标对象)3.2通过有参构造器创建代理类的对象,参数为被代理类的3.3执行代理类的方法。

二.动态代理:一个代理类能帮不同的目标对象(被代理类)完成不同的事
/**
 * @create by 程二狗 on 2018/10/16 0016
 **/
public class ProxyTest1 {
    //商店卖东西
    interface Shop{
        void sale();
    }
    //顾客买东西
    class Customer implements Shop{
        @Override
        public void sale() {
            System.out.println("我是陈二狗,我想在商店里买一台笔记本!");
        }
    }
     //通用代理类:自定义一个类实现InvocationHandler接口,重写invoke()方法
    class  MyInvocationHandler implements InvocationHandler{
       //理解:我是通用代理类,既然你想让我代替你完成事情,你就必须把你要做的事情告诉我,所以我必须
      //要拥有被代理类(目标对象),即目标对象是我的一个属性。
       
       //既然我想得到一个万能代理类,那么我的对象类型就必须是Object类型,如果还是Customer,那么就
      //只能代理 Customer 类了。
        private Object object;//被代理对象(目标对象)
       // private Customer customer;//被代理对象(目标对象)
      
     //下面代码的目的:1.通过构造方法给被代理对象赋值 2.返回一个万能代理对象
    //理解:参1:加载器(加载代理对象类的加载器实际上就是加载被代理对象的加载器,二者类字节码的加 
    //载都是同一个加载器,参数2:实现的借口(代理类和被代理类都需要实现相同的接口)
   //参3: InvocationHandler 就是MyInvocationHandler ,即本类this
        public Object getProxyObject(Object object){
            this.object=object;
            return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
        }
        //当通过代理类的对象对被重写方法的调用时,都会转化为对如下invoke方法的调用。
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return  method.invoke(object,args);
        }
    }
    @Test
    public void test(){
        //1.被代理的对象(现在是顾客)
        Customer customer = new Customer();
       //1.1 被代理的对象,假设是学生
      // Student  s = new Student();
        //2.创建一个实现InvocationHandler接口的类对象
        MyInvocationHandler handler = new MyInvocationHandler();
        //3.调用blind方法,动态的返回一个实现Shop接口的代理对象
        Shop shopProxy = (Shop)handler.blind(customer);//传入学生对象即可
        shopProxy.sale();//转到对invoke方法的调用
    }
}

小tips:动态代理核心代理理解

1.我是通用代理类,既然你想让我代替你完成事情,你就必须把你要做的事情告诉我,所以我必须
要拥有被代理类(目标对象),即目标对象是我的一个属性。
既然我想得到一个万能代理类,那么我的属性的对象类型就必须是Object类型,如果还是Customer,那么就
只能代理 Customer 了。
2.下面代码的目的:1.通过构造方法给被代理对象赋值 2.调用Proxy类的静态方法newProxyInstance()创建一个万能代理对象。
参1:加载器(加载代理对象类的加载器实际上就是加载被代理对象的加载器,二者类字节码的加载都是同一个加载器
参2:实现的借口(代理类和被代理类都需要实现相同的接口)
参3:InvocationHandler 就是MyInvocationHandler ,即本类this


当通过代理类的对象调用被重写的方法时,都会转化为对如下invoke方法的调用。
个人理解:静态代理时,我们执行代理类的方法就是执行代理类重写了接口的方法;动态代理类也是要执行重写接口的方法,即invoke()方法;我们在静态代理中执行代理类重写接口方法的同时,在这个方法中还调用了被代理类的方法,所以动态代理也同理:执行了代理类的invoke()方法,在这个方法中还要去执行被代理类的方法,被代理类的方法即method.invoke(object,args),因为代理类和被代理类都是重写同一个方法,所以Method对象是同一个,参数也相同。利用反射去调一个方法,是类的运行时去进行的,这就是动态。。。

对比反射,我们用反射创建了对象,那么是如何调用反射的呢?(其中stu为对象)

如果你对java的反射机制还不大熟悉,请参考我的另一篇文章java的反射机制

动态代理总结:
1.创建一个接口
2.创建被代理类(目标)对象实现接口
3.创建一个万能代理类

class  MyInvocationHandler implements InvocationHandler{
     private Object object;
   public Object blind(Object object){
     this.object=object;
   return >Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
     }
   @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   return  method.invoke(object,args);
}
}

4.传入不同被代理类,动态生成对应的代理类,执行代理类的方法

public void test(){
     Customer customer = new Customer();
   MyInvocationHandler handler = new MyInvocationHandler();
 Shop shopProxy = (Shop)handler.blind(customer);
shopProxy.sale();
}

三.动态代理的运用:spring框架日志记录==

业务场景:现在有一个user类,有2个方法,我需要在这2个方法执行前和执行后添加日志记录,如何实现?

3.1 创建接口(业务)

/**
 * @create by 程二狗 on 2018/10/19 0019
 **/
public interface User {
    void  add();
    void  update();
}

3.2创建被代理类实现接口(业务实现)

/**
 * @create by 程二狗 on 2018/10/19 0019
 **/
public class UserService implements User {
    @Override
    public void add() {
        System.out.println("user添加");
    }

    @Override
    public void update() {
        System.out.println("user更新");
    }
}

3.3动态创建代理类(代理目标对象去实现业务,并添加日志)

/**
 * @create by 程二狗 on 2018/10/19 0019
 **/
public class Myhandler implements InvocationHandler {
    private Object object;

    public  Object getProxy(Object object){
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是user方法执行前的日志记录<----");//额外增加的方法,和业务无关
        Object obj = method.invoke(object,args);
        System.out.println("我是user方法执行后的日志记录---->");//额外增加的方法,和业务无关
        return obj;
    }
}

3.4 测试

  @Test
    public void logTest(){
        Myhandler handler = new Myhandler();
        UserService  target = new UserService();
        User proxyUser = (User)handler.getProxy(target);
        proxyUser.add();
        System.out.println("=======================");
        proxyUser.update();
//        我是user方法执行前的日志记录<----
//                user添加
//        我是user方法执行后的日志记录---->
//                =======================
//        我是user方法执行前的日志记录<----
//                user更新
//        我是user方法执行后的日志记录---->
    }

小结动态代理优势:日志记录和正常的业务(添加、更新)没有关系,实现了代码的解耦,而且让业务更灵活。比如商品举行打折活动之类的都可以通过动态代理去实现

相关文章

网友评论

      本文标题:java反射的运用之一动态代理

      本文链接:https://www.haomeiwen.com/subject/wdlkzftx.html