精华内容
下载资源
问答
  • 关系模式静态还是动态
    千次阅读
    2021-08-23 01:06:43

    什么是代理

    代理是设计模式的一种,代理类为委托类提供消息预处理,消息转发,事后消息处理等功能。Java中的代理分为三种角色:

    1. 代理类(ProxySubject)
    2. 委托类(RealSubject)
    3. 接口(Subject)

    三者关系可以表示如下图:

     

    Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。

    静态代理

    Java中的静态代理要求代理类(ProxySubject)和委托类(RealSubject)都实现同一个接口(Subject)。静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一单需要修改接口,代理类和委托类都需要修改。
    举个例子:

    接口(Subject)

    interface HelloService {
        void sayHello();
    }

    委托类

    class HelloServiceImpl implements HelloService {
        @Override
        public void sayHello() {
            System.out.println("Hello World!");
        }
    }

    代理类

    class HelloServiceProxy implements HelloService {
        private HelloService helloService;
        
        public HelloServiceProxy(HelloService helloService) {
            this.helloService = helloService;
        }
        
        @Override
        public void sayHello() {
            System.out.println("Before say hello...");
            helloService.sayHello();
            System.out.println("After say hello...");
        }
    }

    测试类

    public class HelloServiceProxyTest {
        
        public static void main(String[] args) {
            HelloService helloService = new HelloServiceImpl();
            HelloServiceProxy proxy = new HelloServiceProxy(helloService);
            proxy.sayHello();
        }
    }

    输出结果

    Before say hello...
    Hello World!
    After say hello...

    动态代理

    Java中的动态代理依靠反射来实现,代理类和委托类不需要实现同一个接口。委托类需要实现接口,否则无法创建动态代理。代理类在JVM运行时动态生成,而不是编译期就能确定。
    Java动态代理主要涉及到两个类:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler。代理类需要实现InvocationHandler接口或者创建匿名内部类,而Proxy用于创建动态动态。
    我们用动态代理来实现HelloService:

    接口(Subject)

    interface HelloService {
        void sayHello();
    }

    委托类

    class HelloServiceImpl implements HelloService {
        @Override
        public void sayHello() {
            System.out.println("Hello World!");
        }
    }

    动态代理类

    class HelloServiceDynamicProxy {
    
        private HelloService helloService;
        public HelloServiceDynamicProxy(HelloService helloService) {
            this.helloService = helloService;
        }
    
        public Object getProxyInstance() {
            return Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("Before say hello...");
                    Object ret = method.invoke(helloService, args);
                    System.out.println("After say hello...");
                    return ret;
                }
            });
        }
    }

    测试类

    public class HelloServieDynamicProxyTest {
        public static void main(String[] args){
            HelloService helloService = new HelloServiceImpl();
            HelloService dynamicProxy = (HelloService) new HelloServiceDynamicProxy(helloService).getProxyInstance();
            dynamicProxy.sayHello();
        }
    }

    输出结果

    Before say hello...
    Hello World!
    After say hello...

    总结

    1. 静态代理实现较简单,代理类在编译期生成,效率高。缺点是会生成大量的代理类。
    2. JDK动态代理不要求代理类和委托类实现同一个接口,但是委托类需要实现接口,代理类需要实现InvocationHandler接口。
    3. 动态代理要求代理类InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。



    作者:zhong0316
    链接:https://www.jianshu.com/p/f56e123817b5
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    更多相关内容
  • 静态代理和动态代理详解

    千次阅读 2022-02-25 20:25:46
    2 什么是代理模式 代理模式是指:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。 换句话说,...

    1 代理

    •  生活中的代理:比如我们生活中比较熟悉的代购、中介、商家等都是属于代理

    2 什么是代理模式

    代理模式是指:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用。

    换句话说,使用代理对象,是为了在不修改目标对象的基础上,增强主业务逻辑。

    客户类真正想要访问的对象是目标对象,但客户类真正可以访问的对象是代理对象。客户类对目标对象的访问是通过访问代理对象来实现的。当然,代理类与目标类要实现同一个接口。

    比如:

    有A、B、C三个类,A原来可以调用C类的方法,现在因为某种原因C类不允许A类调用其方法,但B类可以调用C类的方法。A类通过B类调用C类的方法。这里B是C的代理。

    A通过代理B访问C ,这种模式就是代理模式。

    • 原来的访问关系:

    • 通过代理的访问关系:

    3 代理模式的作用

    1. 功能增强: 在原有的功能上,增加了额外的功能,新增加的功能,叫做功能增强,
    2. 控制访问:代理类不让你访问目标,例如商家不让用户访问厂家

    4 实现代理的方式

    4.1 静态代理

    4.1.1 什么是静态代理?

    (1)代理类是自己手工实现的,自己创建一个Java类,表示代理类。

    (2)同时你所要代理的目标是确定的。

    4.1.2 静态代理的特点:

    (1)实现简单

    (2)容易理解

    4.1.3 静态代理的缺点:

    当项目中目标类和代理类很多的时候,会有以下的特点:

    (1)当目标类增加了,代理类可能也需要成倍的增加,代理类数量过多。

    (2)当你的接口中功能增加了,或者修改了,会影响众多的实现类,厂家类,代理都需要修改,影响比较多。

    4.1.4 静态代理举例:

    • 模拟一个用户购买U盘的行为

            用户:客户端类

            商家:代理,代理某个品牌的U盘

            厂家:目标类

            三者的关系:用户(客户端)  ----  商家(代理)----厂家(目标)

            商家和厂家都是卖U盘的,他们完成的功能是一致的,都是卖U盘。

    • 实现步骤:

    (1)创建一个接口,定义卖U盘的方法,表示你的厂家和商家做的事情。

    (2)创建厂家类,实现(1)步骤的接口

    (3)创建商家,就是代理,也需要实现(1)步骤中的接口

    (4)创建客户端类,调用商家的方法买一个U盘

    • 代理类完成的功能:

    (1)实现目标类中方法的调用

    (2)功能增强

    • Java代码实现如下:

    (1)创建一个接口,定义卖U盘的方法,表示你的厂家和商家做的事情。

    package com.powernode.service;
    
    /**
     * 表示功能的,厂家,商家都要完成的功能
     * 备注:下面只是定义了一个卖U盘的功能,也可以写其它的功能
     */
    public interface UsbSell {
    
        //定义方法 参数 amount:表示一次购买的数量,暂时不用
        //返回一个U盘的价格
         float sell(int amout);
         
    }
    

    (2)创建厂家类,实现(1)步骤的接口

    package com.powernode.factory;
    
    import com.powernode.service.UsbSell;
    
    //目标类:金士顿厂家.不接受用户的单独购买
    public class UsbKingFactory implements UsbSell {
        @Override
        public float sell(int amout) {
            //一个128G的U盘是85元
            return 85.0f;
        }
    }
    

    (3)创建商家,就是代理,也需要实现(1)步骤中的接口

    package com.powernode.shangjia;
    
    import com.powernode.factory.UsbKingFactory;
    import com.powernode.service.UsbSell;
    
    /**
     * 淘宝是一个商家,代理金士顿厂家U盘的销售
     */
    public class TaoBao implements UsbSell {
        private UsbKingFactory factory = new UsbKingFactory();
    
        @Override
        //实现销售U盘功能
        public float sell(int amout) {
    
            //向厂家发送订单,告诉厂家,我买了U盘,厂家发货
            float price = factory.sell(amout);//厂家的价格
    
            //商家需要加价,也就是代理需要增加价格
            //增强功能,代理类在完成目标类方法调用后,增强的功能。在目标类的方法调用后,你做的其它功能,都是增强的意思。
            price = price + 25;
            //返回增加的价格
            return price;
        }
    }
    

    (4)创建客户端类,调用商家的方法买一个U盘

    package com.powernode;
    
    import com.powernode.shangjia.TaoBao;
    
    public class ShopMain {
    
        public static void main(String[] args) {
    
            TaoBao taoBao = new TaoBao();
            float price = taoBao.sell(1);
            System.out.println("通过淘宝的商家,购买得u盘单价" + price);
        }
    }
    

    4.2 动态代理

    在静态代理中目标类很多时候,可以使用动态代理。避免静态代理的缺点。

    动态代理中目标类即使很多,

    1)代理类数量可以很少

    2)当你修改了接口中的方法时,不会影响代理类

    4.2.1 什么是动态代理

    说起动态代理,就先说下什么是动态:

    动态指的是在程序执行时,调用jdk提供的方法才能创建代理类的对象。

    动态代理是:

    在程序执行过程中,使用jdk的反射机制,创建代理类对象,并动态的指定要代理目标类(静态代理中,代理目标是固定,写死的)。而不用你创建类文件,不用写Java文件。

    动态代理其实就是jdk运行期间,动态创建class字节码并加载到JVM

    换句话说,动态代理是一种创建Java对象的能力,让你不用创建TaoBao类,就能创建代理类对象。一般创建对象是 new Xx(),那现在动态代理也可以做这个事情。

    动态代理的实现方式常用的有两种:

    1、JDK动态代理

    使用Java反射包中的类和接口实现动态代理的功能。

    1)反射,Method类,表示方法。类中的方法,通过Method可以执行某个方法。

    2)jdk动态代理的实现

    反射包 java.lang.reflect, 里面有三个类:InvocationHandler, Method, Proxy。

    (1)InvocationHandler 接口(中文意思:调用处理器):就是一个方法 invoke()

    invoke():表示代理对象要执行的功能代码,你的代理类要完成的功能就卸载invoke()方法中。

            代理类完成的功能:

                    1、调用目标方法,执行目标方法的功能

                    2、功能增强,在目标方法调用时,增加功能。

    invoke()方法原型:

    public Object invoke(Object proxy, Method method, Object[] args)

    入参介绍:

    • Object proxy:jdk创建的代理对象,无需赋值
    • Method method: 目标类中的方法,jdk提供method对象的
    • object[] args: 目标类中方法的参数

    InvocationHandler 接口 : 表示你的代理要干什么。

    怎么使用:

            1)创建类实现接口InvocationHandler

            2)重写invoke()方法,把原来静态代理中代理类要完成的功能,写到这个地方

    (2)Method类:目标类中的方法。

    作用:通过Method可以执行某个目标类的方法,Method.invoke();

            Method.invoke(目标对象, 方法的参数)

    说明:Method.invoke()就是用来执行目标方法的,等同于上面在静态代理中的

         float price = factory.sell(amout);//厂家的价格

    (3)Proxy类:核心的对象,创建代理对象,之前创建对象都是new 类的构造方法()现在我们使用Proxy类的方法,代理new的使用。

    方法: 静态方法:newProxyInstance()

    作用:创建代理对象,等同于我上述静态代理举例中的new TaoBao()= new TaoBao()

    方法原型:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

    入参介绍:

    • ClassLoader loader :类加载器,负责向内存中加载对象的,使用反射获取对象的classLoader。 例如: 类A: A.getClass.getClassLoader(), 主要是为了获取目标对象的类加载器。
    • Class<?>[] interfaces:接口,目标对象实现的接口,也是反射获取的。
    • InvocationHandler h:是我们自己写的,代理类要完成的功能。

    出参介绍:

    Object:就是目标对象的代理对象

    4.2.2 动态代理举例

    • 实现动态代理的步骤:

    (1)创建接口,定义目标类要完成的功能

    (2)创建目标类实现接口

    (3)创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能

            1)调用目标方法

            2)增强功能

    (4)使用Proxy类的静态方法,创建代理对象。并把返回值转为接口类型

    • Java代码实现如下

    (1)创建接口,定义目标类要完成的功能

    package com.powernode.service;
    
    /**
     * 表示功能的,厂家,商家都要完成的功能
     * 备注:下面只是定义了一个卖U盘的功能,也可以写其它的功能
     */
    public interface UsbSell {
    
        //定义方法 参数 amount:表示一次购买的数量,暂时不用
        //返回一个U盘的价格
         float sell(int amout);
    
    }
    

    (2)创建目标类实现接口

    package com.powernode.factory;
    
    import com.powernode.service.UsbSell;
    
    //目标类:金士顿厂家.不接受用户的单独购买
    public class UsbKingFactory implements UsbSell {
        @Override
        public float sell(int amout) {
            //一个128G的U盘是85元
            return 85.0f;
        }
    }
    

    (3)创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能

    package com.powernode.handler;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * 必须实现InvocationHandler接口,完成代理类要做的功能(1.调用目标方法 2.功能增强)
     */
    public class MysellHandler implements InvocationHandler {
        
        private Object  target = null;
        //动态代理:目标对象是活动的,不是固定的,需要传入进来。
        //传入是谁,就给谁创建代理
        public MysellHandler(Object target){
            this.target = target;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           // float price = factory.sell(amout);//厂家的价格
    
            //商家需要加价,也就是代理需要增加价格
            //增强功能,代理类在完成目标类方法调用后,增强的功能。在目标类的方法调用后,你做的其它功能,都是增强的意思。
            //price = price + 25;
            //返回增加的价格
    
            //(1)执行目标方法
            Object res = null;
            method.invoke(target, args);//执行目标方法,target你传入的是A,那你的目标方法就是A,你传入的是B,那目标方法就是B
            //(2)进行功能增强
            if (res != null) {
                Float price = (Float) res;
                price = price+25;
                res = price;
            }
            return res;
        }
    }
    

    (4)使用Proxy类的静态方法,创建代理对象。并把返回值转为接口类型

    package com.powernode;
    
    import com.powernode.factory.UsbKingFactory;
    import com.powernode.handler.MysellHandler;
    import com.powernode.service.UsbSell;
    import com.powernode.shangjia.TaoBao;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class ShopMain {
    
        public static void main(String[] args) {
            //创建代理对象,使用Proxy
            // 1、创建目标对象
           // UsbKingFactory factory = new UsbKingFactory();
            UsbSell factory = new UsbKingFactory();
    
            //2、创建InvocationHandler对象
            InvocationHandler handler = new MysellHandler(factory);
    
            //3、创建代理对象
            UsbSell proxy = (UsbSell) Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                    factory.getClass().getInterfaces(),
                    handler);
    
            //4、通过代理执行方法
            float price = proxy.sell(1);
            System.out.println(price);
    
        }
    }
    

    2、通过CGLIB动态代理

    cglib是第三方的工具库,创建代理对象。它可以在运行期间扩展Java类与实现Java接口,它广泛的被许多AOP的框架使用,例如Spring AOP。

    使用JDK的Proxy实现代理,要求目标类和代理类实现相同的接口。若目标类不存在接口,则无法使用该方式实现。

    但对于无接口的类,要为其创建动态代理,就要通过cglib来实现。cglib代理的生成原理是生成目标类的子类,而子类是通过增强过的,这个子类对象就是代理对象,所以,使用cglib生成动态代理,要求目标类必须能够被继承,即不能是final的类。

    cglib在很多的框架中使用。比如mybatis,spring框架中都有使用。

    在Java中,要想创建对象:

    (1)创建类文件,Java文件编译为class

    (2)使用构造方法,创建类的对象

    然而,动态代理是不用做上面的步骤(1),只需要经过步骤(2)就可以把对象创建出来

    展开全文
  • 深入理解代理模式静态代理与JDK动态代理

    万次阅读 多人点赞 2018-03-01 00:22:11
     代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其...

    摘要:
      
      代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其所生成的代理类在将所有的方法调用分派到委托对象上反射执行的同时,还可以对方法进行增强,这也正是Spring AOP的实现基础。通过阅读本文,读者将会对代理模式和Java动态代理机制有更加深入的理解。


    版权声明:

    本文原创作者:书呆子Rico
    作者博客地址:http://blog.csdn.net/justloveyou_/


    一. 代理模式

    1、简介

      代理模式是一种常用的设计模式,在AOP、RPC等诸多框架中均有它的身影。根据代理类的创建时机和创建方式的不同,可以将其分为静态代理和动态代理两种形式:在程序运行前就已经存在的编译好的代理类是为静态代理,在程序运行期间根据需要动态创建代理类及其实例来完成具体的功能是为动态代理。代理模式的目的就是为真实业务对象提供一个代理对象以控制对真实业务对象的访问,代理对象的作用有:

    • 代理对象存在的价值主要用于拦截对真实业务对象的访问;

    • 代理对象具有和目标对象(真实业务对象)实现共同的接口或继承于同一个类;

    • 代理对象是对目标对象的增强,以便对消息进行预处理和后处理。


    2、定义与结构

      定义:为其他对象提供一种代理以控制对这个对象的访问。

                    这里写图片描述

      代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如上图所示:

    • 抽象主题角色:可以是接口,也可以是抽象类;
    • 委托类角色:真实主题角色,业务逻辑的具体执行者;
    • 代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。

    3、静态代理实现

      静态代理是代理模式的实现方式之一,是相对于动态代理而言的。所谓静态代理是指,在程序运行前,由程序员创建或特定工具自动生成源代码并对其编译生成.class文件。静态代理的实现只需要三步:首先,定义业务接口;其次,实现业务接口;然后,定义代理类并实现业务接口;最后便可通过客户端进行调用。例如,

    • 抽象主题角色:HelloService 接口
    public interface HelloService {
    
        String hello(String name);
    
        String hi(String msg);
    }
    • 委托类角色: HelloServiceImpl类
    public class HelloServiceImpl implements HelloService{
        @Override
        public String hello(String name) {
            return "Hello " + name;
        }
    
        @Override
        public String hi(String msg) {
            return "Hi, " + msg;
        }
    }
    • 代理类角色: HelloServiceProxy类
    public class HelloServiceProxy implements HelloService {
    
        private HelloService helloService;
    
        public HelloServiceProxy(HelloService helloService) {
            this.helloService = helloService;
        }
    
        @Override
        public String hello(String name) {
            System.out.println("预处理...");
            String result = helloService.hello(name);
            System.out.println(result);
            System.out.println("后处理...");
            return result;
        }
    
        @Override
        public String hi(String msg) {
            System.out.println("预处理...");
            String result = helloService.hi(msg);
            System.out.println(result);
            System.out.println("后处理...");
            return result;
        }
    }
    • 客户端:
    public class Main {
        public static void main(String[] args){
            HelloService helloService = new HelloServiceImpl();
            HelloServiceProxy helloServiceProxy = new HelloServiceProxy(helloService);
            helloServiceProxy.hello("Panda");
            helloServiceProxy.hi("Panda");
        }
    }/** Output
     预处理...
    Hello Panda
    后处理...
    预处理...
    Hi, Panda
    后处理...
    **/

    4、代理模式与软件设计原则

      代理类不仅是一个隔离客户端和委托类的中介,还可以通过代理类在不修改原有代码的前提下增加一些新功能,是开闭原则(Open for Extension, Closed for Modification)最典型的实践。

      代理类可以为委托类预处理消息、过滤消息、把消息转发给委托类以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法来提供特定的服务。

      也就是说,真正的业务功能还是由委托类来实现,但是在实现业务功能前后可以增加一些公共逻辑,用于增强业务功能。例如,在项目前期开发中我们没有加入缓存、日志等这些功能,后期若想加入,我们就可以使用代理来实现,而且不必对原有代码进行改动。因此,代理模式是对开闭原则的典型实践,也是AOP理念的实现基础。


    二.JDK 动态代理

    1、动态代理引入

      对代理模式而言,一般来说,具体主题类与其代理类是一一对应的,这也是静态代理的特点。但是,也存在这样的情况:有N个主题类,但是代理类中的“预处理、后处理”都是相同的,仅仅是调用主题不同。那么,若采用静态代理,那么必然需要手动创建N个代理类,这显然让人相当不爽。动态代理则可以简单地为各个主题类分别生成代理类,共享“预处理,后处理”功能,这样可以大大减小程序规模,这也是动态代理的一大亮点。

      在动态代理中,代理类是在运行时期生成的。因此,相比静态代理,动态代理可以很方便地对委托类的相关方法进行统一增强处理,如添加方法调用次数、添加日志功能等等。动态代理主要分为JDK动态代理和cglib动态代理两大类,本文主要对JDK动态代理进行探讨。


    2、JDK动态代理机制的相关类/接口

      要想使用JDK动态代理,首先需要了解其相关的类或接口:

    • java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口、目标接口的类加载器以及InvocationHandler便可为目标接口生成代理类及代理对象。
    // 方法 1: 该方法用于获取指定代理对象所关联的InvocationHandler
    static InvocationHandler getInvocationHandler(Object proxy) 
    
    // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
    static Class getProxyClass(ClassLoader loader, Class[] interfaces) 
    
    // 方法 3:该方法用于判断指定类是否是一个动态代理类
    static boolean isProxyClass(Class cl) 
    
    // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, 
        InvocationHandler h)

    • java.lang.reflect.InvocationHandler:该接口包含一个invoke方法,通过该方法实现对委托类的代理的访问,是代理类完整逻辑的集中体现,包括要切入的增强逻辑和进行反射执行的真实业务逻辑。
    // 该方法代理类完整逻辑的集中体现。第一个参数既是代理类实例,第二个参数是被调用的方法对象,
    // 第三个方法是调用参数。通常通过反射完成对具体角色业务逻辑的调用,并对其进行增强。
    Object invoke(Object proxy, Method method, Object[] args)
    • java.lang.ClassLoader:类加载器类,负责将类的字节码装载到Java虚拟机中并为其定义类对象,然后该类才能被使用。Proxy静态方法生成动态代理类同样需要通过类加载器来进行加载才能使用,它与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的而非预存在于任何一个.class 文件中。

    3、JDK动态代理使用步骤

      JDK动态代理的一般步骤如下:

    • 创建被代理的接口和类;

    • 实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;

    • 调用Proxy的静态方法,创建代理类并生成相应的代理对象;

    • 使用代理。


    (1).创建被代理的接口和类

    // 抽象主题角色
    public interface HelloService {
    
        String hello(String name);
    
        String hi(String msg);
    }
    
    // 具体(真实)主题角色
    public class HelloServiceImpl implements HelloService{
        @Override
        public String hello(String name) {
            return "Hello " + name;
        }
    
        @Override
        public String hi(String msg) {
            return "Hi, " + msg;
        }
    }

    (2).实现InvocationHandler接口

    public class MyInvocationHandler implements InvocationHandler{
    
        // 真实业务对象
        private Object target;
    
        public MyInvocationHandler(Object target){
            this.target = target;
        }
    
        /**
         * Processes a method invocation on a proxy instance and returns
         * the result.  This method will be invoked on an invocation handler
         * when a method is invoked on a proxy instance that it is
         * associated with.
         *
         * @param proxy  the proxy instance that the method was invoked on
         * @param method the {@code Method} instance corresponding to
         *               the interface method invoked on the proxy instance.  The declaring
         *               class of the {@code Method} object will be the interface that
         *               the method was declared in, which may be a superinterface of the
         *               proxy interface that the proxy class inherits the method through.
         * @param args   an array of objects containing the values of the
         *               arguments passed in the method invocation on the proxy instance,
         *               or {@code null} if interface method takes no arguments.
         *               Arguments of primitive types are wrapped in instances of the
         *               appropriate primitive wrapper class, such as
         *               {@code java.lang.Integer} or {@code java.lang.Boolean}.
         * @return the value to return from the method invocation on the
         * proxy instance.  If the declared return type of the interface
         * method is a primitive type, then the value returned by
         * this method must be an instance of the corresponding primitive
         * wrapper class; otherwise, it must be a type assignable to the
         * declared return type.  If the value returned by this method is
         * {@code null} and the interface method's return type is
         * primitive, then a {@code NullPointerException} will be
         * thrown by the method invocation on the proxy instance.  If the
         * value returned by this method is otherwise not compatible with
         * the interface method's declared return type as described above,
         * a {@code ClassCastException} will be thrown by the method
         * invocation on the proxy instance.
         * @throws Throwable the exception to throw from the method
         * invocation on the proxy instance.  The exception's type must be
         * assignable either to any of the exception types declared in the
         * {@code throws} clause of the interface method or to the
         * unchecked exception types {@code java.lang.RuntimeException}
         * or {@code java.lang.Error}.  If a checked exception is
         * thrown by this method that is not assignable to any of the
         * exception types declared in the {@code throws} clause of
         * the interface method, then an
         * {@link UndeclaredThrowableException} containing the
         * exception that was thrown by this method will be thrown by the
         * method invocation on the proxy instance.
         * @see UndeclaredThrowableException
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 增强逻辑
            System.out.println("PROXY : " + proxy.getClass().getName());
    
            // 反射调用,目标方法
            Object result = method.invoke(target, args);
    
            // 增强逻辑
            System.out.println(method.getName() + " : " + result);
    
            return result;
        }
    }

    (3). 创建代理类并生成相应的代理对象

    // 生成代理类的class对象
    Class<?> clazz = Proxy.getProxyClass(helloService.getClass().getClassLoader(), helloService
                .getClass().getInterfaces());
    // 创建InvocationHandler
    InvocationHandler myInvocationHandler = new MyInvocationHandler(helloService);
    // 获取代理类的构造器对象
    Constructor constructor = clazz.getConstructor(new Class[] {InvocationHandler.class});
    // 反射创建代理对象
    HelloService proxy = (HelloService)constructor.newInstance(myInvocationHandler);

    也可以一步到位,

    HelloService proxy = (HelloService)Proxy.newProxyInstance(HelloService.class.getClassLoader(),
    helloService.getClass().getInterfaces(), new MyInvocationHandler(helloService));
    

    (4).使用代理

    proxy.hello("rico");
    proxy.hi("panda");
    
    /** Output
    PROXY : com.sun.proxy.$Proxy0
    hello : Hello rico
    PROXY : com.sun.proxy.$Proxy0
    hi : Hi, panda
    **/

    三.JDK动态代理原理与源码

      代理类与代理对象的生成是由Proxy的newProxyInstance()方法来完成的。因此,我们先来看一下newProxyInstance()的实现:

    /**
         * Returns an instance of a proxy class for the specified interfaces
         * that dispatches method invocations to the specified invocation
         * handler.
         *
         * <p>{@code Proxy.newProxyInstance} throws
         * {@code IllegalArgumentException} for the same reasons that
         * {@code Proxy.getProxyClass} does.
         *
         * @param   loader the class loader to define the proxy class
         * @param   interfaces the list of interfaces for the proxy class
         *          to implement
         * @param   h the invocation handler to dispatch method invocations to
         * @return  a proxy instance with the specified invocation handler of a
         *          proxy class that is defined by the specified class loader
         *          and that implements the specified interfaces
         * @throws  IllegalArgumentException if any of the restrictions on the
         *          parameters that may be passed to {@code getProxyClass}
         *          are violated
         * @throws  SecurityException if a security manager, <em>s</em>, is present
         *          and any of the following conditions is met:
         *          <ul>
         *          <li> the given {@code loader} is {@code null} and
         *               the caller's class loader is not {@code null} and the
         *               invocation of {@link SecurityManager#checkPermission
         *               s.checkPermission} with
         *               {@code RuntimePermission("getClassLoader")} permission
         *               denies access;</li>
         *          <li> for each proxy interface, {@code intf},
         *               the caller's class loader is not the same as or an
         *               ancestor of the class loader for {@code intf} and
         *               invocation of {@link SecurityManager#checkPackageAccess
         *               s.checkPackageAccess()} denies access to {@code intf};</li>
         *          <li> any of the given proxy interfaces is non-public and the
         *               caller class is not in the same {@linkplain Package runtime package}
         *               as the non-public interface and the invocation of
         *               {@link SecurityManager#checkPermission s.checkPermission} with
         *               {@code ReflectPermission("newProxyInPackage.{package name}")}
         *               permission denies access.</li>
         *          </ul>
         * @throws  NullPointerException if the {@code interfaces} array
         *          argument or any of its elements are {@code null}, or
         *          if the invocation handler, {@code h}, is
         *          {@code null}
         */
        @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException {
    
            // (Objects工具类)检查指定类型的对象引用不为空null。当参数为null时,抛出空指针异常。设计这个方法主要是为了在方法、构造函数中做参数校验。
            Objects.requireNonNull(h);
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
                // Class<?>[] constructorParams ={ InvocationHandler.class };
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                // 利用构造器对象反射生成代理对象
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }

      继续跟进getProxyClass0()方法,

    /**
         * Generate a proxy class.  Must call the checkProxyAccess method
         * to perform permission checks before calling this.
         */
        private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
    
            // 实现接口数应小于65535                                
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
    
            // If the proxy class defined by the given loader implementing
            // the given interfaces exists, this will simply return the cached copy;
            // otherwise, it will create the proxy class via the ProxyClassFactory
             如果代理类创建,则返回缓存中的该类的副本;否则通过ProxyClassFactory创建代理类 
            return proxyClassCache.get(loader, interfaces);
        }

      到此为止还是没有看到如何生成代理类的,只知道代理类是从proxyClassCache中取得的,这个变量是与缓存相关的一个对象,查看该变量的声明与初始化:

        /**
         * a cache of proxy classes
         */
        private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

      继续跟进ProxyClassFactory类,

        /**
         * A factory function that generates, defines and returns the proxy class given
         * the ClassLoader and array of interfaces.
         * 根据给定的类加载器和接口数组生成代理类的工厂类 
         */
        private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>
        {
            // prefix for all proxy class names
            private static final String proxyClassNamePrefix = "$Proxy";
    
            // next number to use for generation of unique proxy class names
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
                Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
                for (Class<?> intf : interfaces) {
                    /*
                     * Verify that the class loader resolves the name of this
                     * interface to the same Class object.
                     */
                    Class<?> interfaceClass = null;
                    try {
                        interfaceClass = Class.forName(intf.getName(), false, loader);
                    } catch (ClassNotFoundException e) {
                    }
                    if (interfaceClass != intf) {
                        throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                    }
                    /*
                     * Verify that the Class object actually represents an
                     * interface.
                     */
                    if (!interfaceClass.isInterface()) {
                        throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                    }
                    /*
                     * Verify that this interface is not a duplicate.
                     */
                    if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                        throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                    }
                }
    
                String proxyPkg = null;     // package to define proxy class in
                int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
                /*
                 * Record the package of a non-public proxy interface so that the
                 * proxy class will be defined in the same package.  Verify that
                 * all non-public proxy interfaces are in the same package.
                 */
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        accessFlags = Modifier.FINAL;
                        String name = intf.getName();
                        int n = name.lastIndexOf('.');
                        String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                        }
                    }
                }
    
                if (proxyPkg == null) {
                    // if no non-public proxy interfaces, use com.sun.proxy package
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                }
    
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                /*
                 * Generate the specified proxy class.
                 * 生成代理类字节码
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
                try {
    
                    // 加载生成class对象
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    /*
                     * A ClassFormatError here means that (barring bugs in the
                     * proxy class generation code) there was some other
                     * invalid aspect of the arguments supplied to the proxy
                     * class creation (such as virtual machine limitations
                     * exceeded).
                     */
                    throw new IllegalArgumentException(e.toString());
                }
            }
        }

      由ProxyClassFactory类可以知道产生代理类的具体逻辑大致上是,根据传递的被代理类及其实现的接口生成代理类的字节码,然后进行加载并生成对应的Class对象。


      对于JDK动态代理,在运行时我们可以设置JVM参数(-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true)来得到动态生成 的class文件,然后通过 Java Decompiler 反编译上述得到的class文件,有:

    package com.sun.proxy;
    
    import com.youku.zixu.api.HelloService;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    // JDK动态代理生成的代理类继承于父类Proxy
    public final class $Proxy0 extends Proxy
      implements HelloService {
    
      private static Method m1;
      private static Method m3;
      private static Method m2;
      private static Method m4;
      private static Method m0;
    
      // 构造函数
      public $Proxy0(InvocationHandler paramInvocationHandler){
        super(paramInvocationHandler);
      }
    
      public final boolean equals(Object paramObject){
        try{
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final String hi(String paramString){
        try{
          return (String)this.h.invoke(this, m3, new Object[] { paramString });
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final String toString(){
        try{
          return (String)this.h.invoke(this, m2, null);
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final String hello(String paramString){
        try{
          return (String)this.h.invoke(this, m4, new Object[] { paramString });
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final int hashCode(){
        try{
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      static{
        try{
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("com.youku.zixu.api.HelloService").getMethod("hi", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m4 = Class.forName("com.youku.zixu.api.HelloService").getMethod("hello", new Class[] { Class.forName("java.lang.String") });
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }catch (NoSuchMethodException localNoSuchMethodException){
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }catch (ClassNotFoundException localClassNotFoundException){
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    }

      由上述反编译结果我们可以知道:

    • JDK动态代理生成的代理类继承了Proxy类,这正是JDK动态代理只能实现接口代理而不能实现类代理的原因,即Java不允许多继承,而动态代理生成的代理类本身就已经继承了Proxy类;

    • JDK动态代理生成的代理类也代理了三个Object类的方法:equals()方法、hashCode()方法和toString()方法;


    四、小结

      1、实现动态代理的关键技术是反射;

      2、代理对象是对目标对象的增强,以便对消息进行预处理和后处理;

      3、InvocationHandler中的invoke()方法是代理类完整逻辑的集中体现,包括要切入的增强逻辑和进行反射执行的真实业务逻辑;

      4、使用JDK动态代理机制为某一真实业务对象生成代理,只需要指定目标接口、目标接口的类加载器以及具体的InvocationHandler即可。

      5、JDK动态代理的典型应用包括但不仅限于AOP、RPC、Struts2、Spring等重要经典框架。


    引用:

    Java设计模式——代理模式实现及原理
    Java Decompiler
    Java 动态代理机制分析及扩展,第 1 部分

    展开全文
  • java中的静态动态代理模式以及Spring中的CgLib动态代里解读(面试必问)     基础知: 反射知识 什么是反射 反射是框架的灵魂 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法...

    java中的静态、动态代理模式以及Spring中的CgLib动态代理解读(面试必问)




    基础知: 反射知识

    代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
    这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法

    代理模式是java23种设计模式之一, 那什么是代理呢,大白话解释一下就是:李四现在要买车,张三跑过来说我可以帮你买(代理对象张三),张三把李四想买的车的信息都掌握了,然后去帮李四买车去了,买车的过程李四啥都不知道,张三就可以做一些不可描述的事情,比如加价、吃回扣等等。



    大体了解了什么是代理,我们看看java中的代理模式是什么样的,怎么用的,用在哪儿的。

    在java中,代理模式分3种

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

    静态代理

    静态代理是指代理类在程序运行前就已经存在,这种情况下的代理类通常都是我们在Java代码中定义的。

    模拟一个接口

    public interface ITest {
    
        public void addNewData();
    }
    

    编写一个实现类实现此接口

    public class TestImpl implements ITest {
        @Override
        public void addNewData() {
            System.out.println("开始插入新数据");
        }
    }
    

    测试:

        public static void main(String[] args) {
            ITest iTest = new TestImpl();
            iTest.addNewData();;
        }
    

    结果:
    在这里插入图片描述
    好!上面的代码是很正常的,大家应该都很熟悉对吧。
    好!现在我加一个需求,在不改变上面任何代码的情况下,给我输出任务的执行时间起和执行时间止。例如:
    在这里插入图片描述
    怎么办?不能改原始代码诶。假如在工作中发现这种类似的需求,但是不能改原来的祖传代码,怕改了会出错,怎么办呢?

    好,问题抛出来了,我们开始静态代理。
    新建一个代理类实现ITest接口,里面的代码我一行一行的解释

    public class staticProxyImpl implements ITest {
        
    1.成员变量,类型是ITest类型,通过构造函数赋值。
        private ITest iTest;
    
    2.构造函数,调用处把ITest 的实现类对象创造出来后通过构造方法传进来
        public staticProxyImpl(ITest iTest){
            this.iTest = iTest;
        }
    
    3.重写目标方法,开始编写自己的代理业务核心
        @Override
        public void addNewData() {
    4.输出开始时间
            System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
    5.调用目标方法!!!!!!!!!!!!!!!!!!!!!!!!!!!
            this.iTest.addNewData();
    6.输出结束时间
            System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
        }
    }
    

    上面的代码可能有些同鞋还没看懂,那么我们现在看看怎么调用的:

        public static void main(String[] args) {
        1.实例化TestImpl对象
            ITest iTest = new TestImpl();
        2.调用有参构造传入TestImpl对象创建代理对象,并调用所重写的方法
            new staticProxyImpl(iTest).addNewData();
        }
    

    结果:
    在这里插入图片描述
    在上述代码中,针对客户看到的是ITest 接口提供了功能,而功能又是由staticProxyImpl提供的。我们可以在staticProxyImpl中修改或新增一些内容,而不影响被代理类TestImpl。

    静态代理的缺点:

    1. 当需要代理多个类时,代理对象要实现与目标对象一致的接口。要么,只维护一个代理类来实现多个接口,但这样会导致代理类过于庞大。要么,新建多个代理类,但这样会产生过多的代理类。
    2. 当接口需要增加、删除、修改方法时,目标对象与代理类都要同时修改,不易维护。

    动态代理

    与静态代理不同,之前我们讲的静态代理是什么?是不是在编码阶段完成的? 那么局限性就出来了,一切在编码阶段定死的东西维护性都怎么样?是不是都很差啊? 好!动态代理请求出战!
    动态代理是指代理类在程序运行时进行创建的代理方式。这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据Java代码中的“指示”动态生成的。
    相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。

    所以,动态代理就是想办法,根据接口或目标对象,计算出代理类的字节码,然后再加载到JVM中使用。

    JDK动态代理主要涉及两个类:java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler,我们仍然通过案例来学习

    继续我们上面的例子:

    模拟一个接口

    public interface ITest {
    
        public void addNewData();
    }
    

    编写一个实现类实现此接口

    public class TestImpl implements ITest {
        @Override
        public void addNewData() {
            System.out.println("开始插入新数据");
        }
    }
    

    我们现在要通过动态代理实现之前的需求:在不改变上面任何代码的情况下,给我输出任务的执行时间起和执行时间止。例如:
    在这里插入图片描述
    好!到这一步我们要开始想办法实现动态的代理。也就是在程序运行期间时候,我们要处理两个问题!

    1. 我们要怎么样拿到我们的被代理类对象TestImpl 的实例,并创建代理类????
    2. 我们怎样通过代理类,去动态地调用被代理类中的同名方法????

    编写一个类,实现java.lang.reflect.InvocationHandler接口

    public class jdkProxyImpl implements InvocationHandler {
    
    1.包含一个被代理对象的实例,这里用顶级父类Object 
        private Object object;
        
    2.构造方法,通过此构造方法初始化被代理对象实例。
        public jdkProxyImpl(Object object){
            this.object = object;
        }
    3.重写的InvocationHandler 中的invoke方法,这个方法是什么? 
      先一句话概括一下:在程序运行期间,
      我们拿到代理类的对象的时候(后面会说怎么拿),
      调用xxx方法例如上面例子中的addNewData()方法的时候,
      程序会自动地调用到当前的invoke()方法,而真实的addNewData()方法的调用,
      将会在我们当前的invoke()中手动的写出来!!!
      也就是说,我们可以在调用真实的目标方法的前、后,加上我们新增的逻辑,起到了代理的效果!
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    5.打印起始时间
            System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
    6.调用目标方法
            method.invoke(object,args);
    7.打印结束时间
            System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
            return null;
        }
    }
    

    invoke方法有三个参数

    1. proxy:代理类的对象

    2. method:代理类所调用的具体哪个方法,这里是通过反射,通过方法名拿到具体的方法,我们就可以通过invoke方法调用了。但我们知道,反射中的invoke方法需要两个参数,这里解释一下:
      参数一:当前方法所属的类的对象,这里就是被代理类的对象,也就是构造方法初始化的object对 象,也就是我们的被代理类TestImpl
      参数二:方法的参数

    3. args:所包含的参数

    调用:

        public static void main(String[] args) {
    1.实例化被代理类
            ITest iTest = new TestImpl();
            
    2.实例化jdkProxyImpl类,并将代理类对象初始化给它
            InvocationHandler invocationHandler = new jdkProxyImpl(iTest);
            
    3.重点来了!!!Proxy是jdk提供的一个类,Proxy.newProxyInstance方法会返回一个代理类!那么怎么
      创造我们想要的代理类呢?
    
            ITest proxy = (ITest) Proxy.newProxyInstance(iTest.getClass().getClassLoader(),iTest.getClass().getInterfaces(),invocationHandler);
    
    4.代理类调用目标方法(最终会走到jdkProxyImpl中的invoke方法)
            proxy.addNewData();
    
        }
    

    Proxy.newProxyInstance(iTest.getClass().getClassLoader(),iTest.getClass().getInterfaces(),invocationHandler),三个参数,一一解释!

    1. 被代理类的类加载器
    2. 被代理类所实现的接口
    3. 实现了invocationHandler接口的类的对象

    看不懂对吧?我大白话解释一下。
    newProxyInstance方法就是创造一个类,这个类就是代理类,那是谁的代理类呢?第一个参数就跳出来了:是我TestImpl的代理类,我把我的类加载器给你,你可以反编译。好!第二个参数,获取到被代理类所继承的接口,然后代理类继承所有获取到的接口,重写所有的方法,所有的方法体全部调用第三个参数中的invoke方法!

    最后在上面代码中的第四部,通过代理类调用addNewData方法,这个addNewData方法我们给它取个名字,叫假小子,因为它的方法体和真正的addNewData没有一点关系,方法体直接调用到了invocationHandler中的invoke方法。然后在invoke方法中,我们通过反射拿到了真正的addNewData方法
    调用!

    这里有一个面试题:为什么JDK原生的动态代理,要求被代理类必须实现接口?
    答案:因为在生成代理对象的底层源码中,我们发现代理对象是继承自Proxy父类的,而java中,类之能单继承,所以要实现被代理类的所有方法,只能继承接口。

    其实这里我有一个疑问,既然我们已经拿到了被代理类的Class对象,按理来说就可以通过反射获取到他所有的方法,没有必要去继承接口了啊。有知道的同鞋希望评论区留言 讨论。。。。。

    CgLib动态代理

    一道面试题:cglib和jdk动态代理区别?
    答案:
    jdk动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
    而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    cglib代理不需要被代理类继承接口,而jdk动态代理必须要求被代理类实现接口。

    在sping的AOP中,就是使用的Cglib动态代理。在jdk1.8以后,cglib的动态代理效率要低于jdk的动态代理

    好了 基本已经讲完,欢迎大家评论区指出不足,一起学习进步!
    大家看完了点个赞,码字不容易啊。。。

    展开全文
  • 静态功耗与动态功耗

    万次阅读 多人点赞 2020-04-05 15:01:57
    功耗的本质是能量耗散。由能量守恒定律可知,能量只能从一种形式转成另一种形式,能量的总量不变。芯片耗散的电能主要转化成热能。如果一颗芯片的功耗过大,容易导致工作时...静态功耗以及动态功耗是两个主要的功耗源。
  • VMware虚拟机有三种网络模式,分别是Bridged(桥接模式)、NAT(网络地址转换模式)、Host-only(主机模式)。  宿主机在VMware workstation安装好之后会多出两个网络连接,分别是VMware Network Adapter VMnet1和VMware...
  • 关系,关系模式,关系模型区别和联系

    千次阅读 2021-06-24 09:18:20
    关系是关系模式在某一个时刻的状态或者内容,关系模式静态的,稳定的,而关系是动态的,随时间不断变化的,因为关系操作在不断地更新着数据库中的数据 类似于面向对象程序设计中”类“与”对象“的区别。”关系“是”...
  • 动态代理与静态代理区别

    万次阅读 多人点赞 2018-09-18 09:26:55
    一、代理概念  为某个对象提供一个代理,以控制对这个对象的访问。...图1:代理模式  从图中可以看出,代理接口(Subject)、代理类(ProxySubject)、委托类(RealSubject)形成一个“品”...
  • 静态关联和动态关联

    千次阅读 2018-05-30 15:29:48
    3.2静态关联与动态关联关联:确定调用的具体对象的过程...动态关联:编译系统把他放在运行阶段进行处理,在运行阶段确定关联关系。也叫滞后关联。3.3 在什么情况下应当声明虚函数使用虚函数的注意:1)只能用virtual...
  • gcc 静态编译与动态编译

    千次阅读 2018-04-14 15:50:02
    gcc中的连接选项(Linker Options):-LDirectory-L 用于指定连接库所需要的搜索路径,后面的参数“Directory”为相对路径或绝对路径-llibrary-l 用于指定连接库文件选项例如:-lmylib 如果存在静态库,则意味着需要...
  • 代理模式———动态代理

    千次阅读 2022-01-23 00:03:30
    在百度百科中没有动态代理的概念,但是动态代理其实就是代理模式中的一种,所以在了解动态代理前最好先了解一下代理模式; 代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式; 代理模式就是为其他对象...
  • 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。 这种类型的设计...
  • 记得有一次和一朋友散步,他问到什么是动态网页、什么是静态网页、动态网页与静态网页有什么区别。当时我的回答似乎并没有让他真的明白到底什么是动态、什么是静态,所以今天想在这篇文章中详细的举例说明什么是动态...
  • 设计模式之——静态代理模式

    万次阅读 2021-02-11 16:54:45
    设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。 正确使用设计模式具有以下优点: 可以提高程序员的思维能力、编程能力和设计能力。 使程序...
  • Java设计模式学习06——静态代理与动态代理

    万次阅读 多人点赞 2016-10-30 09:57:18
    一、代理模式为某个对象提供一个代理,从而控制这个代理的访问。代理类和委托类具有共同的父类或父接口,这样在任何使用委托类对象的地方都可以使用代理类对象替代。代理类负责请求的预处理、过滤、将请求分配给委托...
  • 代理模式 什么是代理模式 通过代理为原始类增加额外功能 好处:有利于目标类的维护,增强的更能是可插拔的。 名词解释 目标类:如UserServiceImpl 目标方法: login(String username,String password) 额外功能:...
  • 本文主要从三个方面介绍代理模式,什么是代理模式,提供了什么好处;代理模式的三种实现方式;三种代理的区别 首先简单说明下为什么需要代理模式:为其他对象提供一种代理以控制对这个对象的访问,可以隔离客户端和...
  • 其跟国外狭义上的ABS产品还是有一定的差异的。国外狭义的ABS一般是指车贷、卡贷、个人消费贷等等,以这些资产为基础支持发行的一些证券。 ABS其实是一个舶来品,起源于美国上世纪60年代,当时二战结束以后,美国人口...
  • 以下内容是本人利用网络资料以及课堂学习总结的学习笔记,如有错误欢迎评论区更正。 前言 这是一组关系实例 域(Domain):一组具有相同数据类型的值的集合(如:整数、...二、关系模式 三、关系数据库 ...
  • 光照也是实时渲染的最重要的部分之一,和反射一样,光照和阴影在实时渲染中的开销也很大,因此一部分计算被分流到预先计算/预算渲染中,而静态光照是指所有预先计算而非实时渲染的光照。 静态光照和静态阴影 从流程...
  • 配置静态路由,动态路由,默认路由

    万次阅读 多人点赞 2019-07-30 16:06:09
    通过发报文的形式成为邻居关系,邻居再相互发送链路状态信息形成邻接关系,之后各自根据最短路径算法算出路由,放在OSPF路由表,OSPF路由与其他路由比较后优的加入全局路由表。整个过程使用了五种报文、三个阶段、四...
  • 这三种方式分别为:使用静态工厂...静态工厂和实例工厂区别:静态工厂指的是工厂的服务是静态的,也就是工厂提供的方法是static的,那么这些方法仅有工厂提供以及管理更新等,跟客户端或者说调用端是没有关系的;...
  • 静态代理&动态代理

    万次阅读 2021-12-17 10:09:06
    讲解静态代理与动态代理的实现示例,静态代理与动态代理的区别
  • 浅析静态规划和动态规划

    千次阅读 2017-03-19 18:26:32
    动态规划真正的威力在于它的高效率。状态转移模型中,状态的数量远远少于状态转移路径的数量。采用传统的搜索算法暴力穷举每条路径,算法复杂度远远高于遍历状态节点。
  • 静态端口和动态端口

    千次阅读 2018-09-11 18:59:32
    端口映射分为动态静态。  动态端口映射  内网中的一台电脑要访问一个网站,会向NAT网关发送数据包,包头中包括对方(就是目标官网)IP、端口和本机IP、端口,NAT网关会把本机IP、端口替换成自己的公网IP、一个未...
  • 单例模式就是保证系统中这个对象只有一个实例 什么是单例模式?  单例模式确保某个类只有一个实例,而且自行...1)饿汉模式:饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,除非系统重启,这...
  • 静态代理与动态代理(JDK、CGLIB) 问题引入 什么是静态代理? 什么是动态代理? 静态代理和动态代理的区别是什么? JDK动态代理和CGLIB动态代理的区别? 静态代理 主要是通过代码的业务来处理,在编译...
  • 程序设计之单例模式 VS 静态方法

    万次阅读 多人点赞 2013-09-24 11:57:48
    单例模式(Singleton); 2>静态方法. 但是, 对于这两种实现方式 , 那种更好呢? 在国内论坛上看了一下其他的一些看法 : http://hi.baidu.com/jiangzhong8715/item/c8b66e3d6afd2f677c034b07: ...
  • (3)动态电压缩放DVS技术(处理器在不同工作模式下使用不同电压); (4)电源关断技术,power-gating; 电流角度 I_leak(漏电流): (1)使用HVT高阈值晶体管,漏电流小; (2)多阈值; 3.动态功耗 P...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 353,546
精华内容 141,418
热门标签
关键字:

关系模式静态还是动态