精华内容
下载资源
问答
  • 实现java动态代理的两个实例,jdk动态代理和cglib
  • JDK动态代理实例

    2018-12-09 19:06:07
    在使用SpingMVC做WEB应用层架构时,经常使用...但JDK代理只能为接口创建代理实例。今天主要以简单代码说明JDK代理机制。  JDK代理主要涉及Java.lang.reflect包中两个类:Proxy和InvocationHandler,其中Invoca...

        在使用SpingMVC做WEB应用层架构时,经常使用SpringAOP实现数据库事务控制,SpringAOP使用动态代理,有两种,JDK代理和cglib代理,默认使用JDK代理。但JDK代理只能为接口创建代理实例。今天主要以简单代码说明JDK代理机制。

        JDK代理主要涉及Java.lang.reflect包中两个类:Proxy和InvocationHandler,其中InvocationHandler是接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码。动态的将横切逻辑和业务代码编织在一起。Proxy生成目标类的代理对象。

         代码类名说明

         JcyaoServiceImpl :业务接口实现类

         JcyaoService: 业务接口

         JcyaoInvovaHandler:横切逻辑实现类

         JcyaoProxy:代理对象生成测试实例

    public interface JcyaoService {
        /**业务逻辑*/
        public String showMsg();
    }

     

    public class JcyaoServiceImpl implements JcyaoService{
        private String name ;
        private int age;
    
        public JcyaoServiceImpl() {
            super();
        }
    
        public JcyaoServiceImpl(String name, int age){
            this.name = name;
            this.age = age;
        }
    
        public String showMsg(){
            System.out.println(name + age);
            return "it is ok";
        }
    }
    public class JcyaoInvovaHandler implements InvocationHandler {
    
        /**
         * 业务实体类
         */
        private Object target;
    
        public JcyaoInvovaHandler(Object target){
            this.target = target;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(System.currentTimeMillis() + target.getClass().getName() + "." +method.getName() + "开始");
            Object obj = method.invoke(target, args);//通过反射调取业务的目标方法
            System.out.println(System.currentTimeMillis() + target.getClass().getName() + "." +method.getName() + "结束");
            return obj;
        }
    }
    public class JcyaoProxy {
    
        @Test
        public void proxy(){
            //java动态代理,必须使用接口代理
            JcyaoService js = new JcyaoServiceImpl("jcyao", 25);
            JcyaoInvovaHandler ji = new JcyaoInvovaHandler(js);
    
            JcyaoService proxy = (JcyaoService) Proxy.newProxyInstance(js
                            .getClass().getClassLoader(),
                    js.getClass().getInterfaces(), ji);
    
            proxy.showMsg();
        }
    }

     

     

     

     

     

        

    展开全文
  • java动态代理实例aop

    2017-04-20 21:22:29
    aop java
  • java动态代理实例

    2009-01-21 17:37:29
    java动态代理实例 要想理解拦截器的知识 最好要先理解java动态代理这块
  • Java动态代理实例

    千次阅读 2017-06-03 23:43:22
    动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。 代理设计模式的原理: 使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原

    静态代理:特征是代理类和目标对象的类都是在编译期间确定下来的,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。

    最好可以通过一个代理类完成全部的代理功能。

    动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

    代理设计模式的原理:
    使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。


    /**
     * 静态代理模式
     */
    //接口
    interface ClothFactory {
        void productCloth();
    }
    
    //被代理类
    class NikeClothFactory implements ClothFactory{
        public void productCloth() {
            System.out.println("Nike Factory!");
        }
    }
    
    //代理类
    class ProxyFactory implements ClothFactory{
        ClothFactory cf;
        //创建代理类的对象时,实际传入一个被代理类的对象
        public ProxyFactory(ClothFactory cf) {
            this.cf = cf;
        }
        public void productCloth() {
            System.out.println("代理类开始执行!");
            cf.productCloth();
        }
    }
    
    public class TestClothProduct {
        public static void main(String[] args) {
            NikeClothFactory nike = new NikeClothFactory();  //创建被代理类的对象
            ProxyFactory proxy = new ProxyFactory(nike);       //创建代理类的对象
            proxy.productCloth();
        }
    }
    

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 动态代理的使用
     */
    interface Subject {
        void action();
    }
    
    //被代理类
    class RealSubject implements Subject {
        public void action() {
            System.out.println("我是被代理类!");
        }
    }
    
    //
    class MyInvocationHander implements InvocationHandler{
        Object obj;  // 实现了接口的被代理类的对象的声明
        //1.给被代理的对象实例化;2.返回一个代理类的对象
        public Object blind(Object obj) {
            this.obj = obj;
            return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
        }
    
        //当通过代理类的对象发起对被重写的方法调用时,都会转换为对如下的invoke方法的调用
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //method方法的返回值是returnVal
            Object returnVal = method.invoke(obj, args);
            return returnVal;
        }
    }
    
    public class TestProxy {
        public static void main(String[] args) {
            //1.被代理类的对象
            RealSubject real = new RealSubject();
            //2.创建一个实现了InvocationHandler接口类的对象
            MyInvocationHander hander = new MyInvocationHander();
            //3.调用blind()方法,动态的返回一个同样实现了real所在类的实现的接口Subject的代理类的对象
            Object obj = hander.blind(real);
            Subject sub = (Subject)obj; //此时的sub就是代理类的对象
    
            sub.action();  //转到对InvocationHandler接口的实现类的invoke()的调用
            
            
            //再举一个例子
            NikeClothFactory nike = new NikeClothFactory();
            ClothFactory proxyCloth = (ClothFactory) hander.blind(nike); // proxyCloth即为代理类的对象
            proxyCloth.productCloth();
        }
    }
    

    展开全文
  • 实例理解JDK动态代理和Cglib动态代理及其区别 深入理解设计模式之代理模式 代理代理化妆品生产理解JDK动态代理 代理代理汽车制造理解Cglib动态代理

    动态代理

    代理模式参考博客:

    https://blog.csdn.net/qq_42937522/article/details/105067563

    代理模式原理

    使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

    作用

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

    应用场景

    1)远程代理(Remote Proxy)

    为一个位于不同的地址空间的对象提供一个本地的代理对象。这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中。也即为不同地址空间提供局部的代表。

    2)虚拟代理(Virtual Proxy)

    根据需要创建开销很大的对象。如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。

    3)保护代理(Protection Proxy)

    控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。

    4)智能指引(Smart Reference)

    取代了简单的指针,它在访问对象时执行一些附加操作。

    5)Copy-on-Write代理

    它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。

    动态代理

    动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

    动态代理相比于静态代理的优点:

    抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

    动态代理的一般用法

    在这里插入图片描述

    JDK动态代理

    使用Proxy和InvocationHandler创建动态代理

    Proxy提供用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果我们在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类:如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。
    Proxy提供了如下两个方法来创建动态代理类和动态代理实例:

    • static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):创建一- 个动态
      代理类所对应的Class对象,该代理类将实现interfaces 所指定的多个接口。第一个ClassLoader指定生成动态代理类的类加载器。
    • static Object newProxyInstance(ClassLoader loader,Class<?>I] interfaces, InvocationHandler h):直接创建一个动态代理对象, 该代理对象的实现类实现了interfaces 指定的一系列接口,执行代理对象的每个方法时都会被替换执行InvocationHandler对象invoke方法。

    实际上,即使采用第一-种方式获取了 一个动态代理类之后,当程序需要通过该代理类来创建对象时一样需要传入一个InvocationHandler对象。也就是说,系统生成的每个代理对象都有一个与之关联的InvocationHandler对象。

    示例

    以代理商代理生产商的产品为例(JDK代理实现)

    代码实现

    需要实现的接口

    /**
     * 抽象接口——生产者
     */
    public interface Producer {
        String produce(String product);
    }
    
    /**
     * 功能描述:化妆品生产者(被代理对象)
     **/
    public class CosmeticProducer implements Producer {
    
        /**
         * 生产化妆品
         */
        @Override
        public String produce ( String product ) {
            System.out.println("正在生产化妆品" + product);
            return product;
        }
    
    }
    
    /**
     * 功能描述:提供产品的工具类
     **/
    public class ProductUtil {
    
        public static void prepareMaterial(){
            System.out.println("代理商为生产商准备材料");
        }
    
        public static void sellProduct(){
            System.out.println("代理商为生产商卖产品");
        }
        
    }
    
    /**
     * 功能描述:代理对象调用被代理对象方法,所必须实现的接口
     **/
    public class MyInvocationHandler implements InvocationHandler {
    
        /** 需要被代理的对象 */
        private Object target;
    
        public void setTarget ( Object target ) {
            this.target = target;
        }
    
        @Override
        public Object invoke ( Object proxy, Method method, Object[] args ) throws Throwable {
            //调用被代理对象方法前,执行的方法
            ProductUtil.prepareMaterial();
    
            //调用被代理对象的方法
            Object obj = method.invoke(target, args);
    
            //调用被代理对象方法后,执行的方法
            ProductUtil.sellProduct();
    
            //返回被代理对象方法的返回值
            return obj;
        }
    }
    
    /**
     * 功能描述:产品代理商(代理对象)
     *
     * 采用自定义的外部类MyInvocationHandler来处理
     *
     * @author RenShiWei
     * Date: 2020/6/19 16:46
     **/
    public class ProductProxy {
    
        //***方式一:使用自定义的外部类MyInvocationHandler实现InvocationHandler接口处理***
    
        public ProductProxy(){}
    
        //通过代理对象,调用被代理对象的方法
        public static Object getProductProxy ( Object target ) {
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.setTarget(target);
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
        }
    
        //***方式二:使用匿名实现类来处理(可以减少类的数量)
    
        private Object target;
    
        public ProductProxy ( Object target ) {
            this.target = target;
        }
    
        //通过代理对象,调用被代理对象的方法
        public Object getProductProxy2 () {
            MyInvocationHandler handler = new MyInvocationHandler();
            handler.setTarget(target);
            //这里的InvocationHandler的实现类,也可以采用接口的匿名实现类来处理,减少类的数量
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke ( Object proxy, Method method, Object[] args ) throws Throwable {
                    //调用被代理对象方法前,执行的方法
                    ProductUtil.prepareMaterial();
    
                    //调用被代理对象的方法
                    Object obj = method.invoke(target, args);
    
                    //调用被代理对象方法后,执行的方法
                    ProductUtil.sellProduct();
    
                    //返回被代理对象方法的返回值
                    return obj;
                }
            });
        }
    }
    
    

    客户端调用

    /**
     * 功能描述:客户端调用
     **/
    public class client {
    
        public static void main ( String[] args ) {
            System.out.println("**方式一:使用MyInvocationHandler**");
            Producer cosmeticProducer = new CosmeticProducer();
            Producer productProxy = (Producer) ProductProxy.getProductProxy(cosmeticProducer);
            String s = productProxy.produce("欧莱雅男士洗面奶");
            System.out.println("被代理的产品:" + s);
    
            System.out.println("**方式二:使用匿名InvocationHandler接口实现类的方式**");
            ProductProxy proxy = new ProductProxy(new CosmeticProducer());
            Producer productProxy2 = (Producer) proxy.getProductProxy2();
            String s1 = productProxy2.produce("兰蔻洁面乳");
            System.out.println("被代理的产品:" + s1);
        }
    
    }
    

    结果:

    **方式一:使用MyInvocationHandler**
    代理商为生产商准备材料
    正在生产化妆品欧莱雅男士洗面奶
    代理商为生产商卖产品
    被代理的产品:欧莱雅男士洗面奶
    **方式二:使用匿名InvocationHandler接口实现类的方式**
    代理商为生产商准备材料
    正在生产化妆品兰蔻洁面乳
    代理商为生产商卖产品
    被代理的产品:兰蔻洁面乳
    

    JDK动态代理原理

    为什么要叫JDK动态代理?

    是因为代理对象是由JDK动态生成的,而不像静态代理方式写死代理对象和被代理类,不灵活。

    JDK动态代理基于拦截器和反射来实现

    使用条件

    1)必须实现InvocationHandler接口;

    2)使用Proxy.newProxyInstance产生代理对象;

    3)被代理的对象必须要实现接口;

    源码分析

    参考博客:https://blog.csdn.net/yhl_jxy/article/details/80586785

    Cglib动态代理

    1. 引入相关依赖
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    
    1. 代理类实现MethodInterceptor接口,实现intercept方法

    2. 创建代理对象

    //返回一个代理对象:  是 target 对象的代理对象
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }
    

    4.客户端调用

    注意事项

    • 在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
    • 目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

    示例

    汽车制造厂制造汽车,代理商代理准备材料和售卖汽车

    /**
     * 功能描述:被代理者 汽车制造工厂
     **/
    public class CarFactory {
    
        public void productCar(){
            System.out.println("制造汽车");
        }
    
    }
    
    /**
     * 功能描述:代理类
     **/
    public class ProxyFactory implements MethodInterceptor {
    
        //维护一个目标对象
        private Object target;
    
        //构造器,传入一个被代理的对象
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        //返回一个代理对象:  是 target 对象的代理对象
        public Object getProxyInstance() {
            //1. 创建一个工具类
            Enhancer enhancer = new Enhancer();
            //2. 设置父类
            enhancer.setSuperclass(target.getClass());
            //3. 设置回调函数
            enhancer.setCallback(this);
            //4. 创建子类对象,即代理对象
            return enhancer.create();
        }
    
        //重写  intercept 方法,会调用目标对象的方法
        @Override
        public Object intercept ( Object o, Method method, Object[] objects, MethodProxy methodProxy ) throws Throwable {
            System.out.println("---代理商准备材料");
            Object returnVal = method.invoke(target,objects);
            System.out.println("---代理商售卖汽车");
            return returnVal;
        }
    
    }
    
    /**
     * 功能描述:客户端调用
     *
     **/
    public class Client {
    
        public static void main ( String[] args ) {
            //获取代理对象,并强转成被代理对象的数据类型
            CarFactory proxyInstance = (CarFactory) new ProxyFactory(new CarFactory()).getProxyInstance();
            //执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用
            proxyInstance.productCar();
        }
    
    }
    

    结果

    ---代理商准备材料
    制造汽车
    ---代理商售卖汽车
    

    Cglib动态代理实现原理

    可以在运行期扩展Java类与实现Java接口。

    现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口

    参考博客:Cglib动态代理实现原理

    MethodInterceptor接口源码

    package net.sf.cglib.proxy;
    
    import java.lang.reflect.Method;
    
    public interface MethodInterceptor extends Callback {
        Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
    }
    

    MethodInterceptor接口只有一个intercept()方法,这个方法有4个参数:

    1)Object表示增强的对象,即实现这个接口类的一个对象;

    2)Method表示要被拦截的方法;

    3)Object[]表示要被拦截方法的参数;

    4)MethodProxy表示要触发父类的方法对象;

    JDK动态代理VSCglib动态代理

    1.从实现方式上说

    JDK动态代理利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    CGLIB动态代理利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    2.何时使用JDK或者CGLIB?

    1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。

    2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。

    3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

    JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理CGLib必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法,是一种继承。

    但是针对接口编程的环境下推荐使用JDK的代理;

    3. JDK动态代理和CGLIB字节码生成的区别?

    1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。

    2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。

    4.CGlib比JDK快?

    1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

    2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,

    总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。

    5.Spring如何选择用JDK还是CGLIB?

    1)当Bean实现接口时,Spring就会用JDK的动态代理。

    2)当Bean没有实现接口时,Spring使用CGlib是实现。

    3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)。

    动态代理与AOP

    图解

    在这里插入图片描述

    展开全文
  • 几个Java反射和动态代理的小例子。可以学习如何通过Java的反射机制实例化对象、调用对象的方法、操作对象的私有成员变量、改变数组中的某项的值、改变数组大小等;可以学习Java的动态代理模式、学习Java工厂模式以及...
  • 附件为java 动态代理实例,有全码,包括测试代码。 代码少,注释全。 对理解代理非常不错。
  • 3个动态代理实例

    2012-08-02 11:45:11
    3个动态代理实例,不错咯,希望能给大家带来帮助!
  • 动态代理和cglib例子

    2017-12-25 10:27:46
    动态代理和cglib例子,动态代理和cglib例子动态代理和cglib例子动态代理和cglib例子
  • 动态代理例子

    2015-05-17 16:59:27
    是一个描述动态代理实例,用的是反射的方法,感觉还是比较容易理解得
  • Java两种动态代理JDK动态代理和CGLIB动态代理

    万次阅读 多人点赞 2018-08-07 15:33:35
    JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是...

    目录

    代理模式

    JDK动态代理

    cglib动态代理

    测试


    代理模式

    代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。

    使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。

    上述例子中,类A写死持有B,就是B的静态代理。如果A代理的对象是不确定的,就是动态代理。动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。

    JDK动态代理

    jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。

    首先有个“明星”接口类,有唱、跳两个功能:

    package proxy;
    
    public interface Star
    {
        String sing(String name);
        
        String dance(String name);
    }
    

    再有个明星实现类“刘德华”:

    package proxy;
    
    public class LiuDeHua implements Star
    {   
        @Override
        public String sing(String name)
        {
             System.out.println("给我一杯忘情水");
    
            return "唱完" ;
        }
        
        @Override
        public String dance(String name)
        {
            System.out.println("开心的马骝");
    
            return "跳完" ;
        }
    }
    

    明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务,它仅仅是一个wrapper。

    package proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class StarProxy implements InvocationHandler
    {
        // 目标类,也就是被代理对象
        private Object target;
        
        public void setTarget(Object target)
        {
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里可以做增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }
        
        // 生成代理类
        public Object CreatProxyedObj()
        {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }  
       
    }
    

    上述例子中,方法CreatProxyedObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个CreatProxyedObj方法不一定非得在我们的StarProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:

    1、new一个目标对象

    2、new一个InvocationHandler,将目标对象set进去

    3、通过CreatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。

            Star ldh = new LiuDeHua();
    
            StarProxy proxy = new StarProxy();
    
            proxy.setTarget(ldh); 
      
            Object obj = proxy.CreatProxyedObj();
            
            Star star = (Star)obj;

    Proxy(jdk类库提供)根据B的接口生成一个实现类,我们成为C,它就是动态代理类(该类型是 $Proxy+数字 的“新的类型”)。生成过程是:由于拿到了接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它调用另一个对象的方法。当然这个被调用的对象不能是对象B,如果是对象B,我们就没法增强了,等于饶了一圈又回来了。

    所以它调用的是B的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,上述例子中就是StarProxy, 这个接口里面有个方法,它是所有Target的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。

    看下我们的实现,我们在InvocationHandler里调用了对象B(target)的方法,调用之前增强了B的方法。

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }

    所以可以这么认为C代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。

    整个JDK动态代理的秘密也就这些,简单一句话,动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。

    下面看下动态代理类到底如何调用的InvocationHandler的,为什么InvocationHandler的一个invoke方法能为分发target的所有方法。C中的部分代码示例如下,通过反编译生成后的代码查看,摘自链接地址。Proxy创造的C是自己(Proxy)的子类,且实现了B的接口,一般都是这么修饰的:

    public final class XXX extends Proxy implements XXX
    

    一个方法代码如下:

    
      public final String SayHello(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);
        }
    

    可以看到,C中的方法全部通过调用h实现,其中h就是InvocationHandler,是我们在生成C时传递的第三个参数。这里还有个关键就是SayHello方法(业务方法)跟调用invoke方法时传递的参数m4一定要是一一对应的,但是这些对我们来说都是透明的,由Proxy在newProxyInstance时保证的。留心看到C在invoke时把自己this传递了过去,InvocationHandler的invoke的第一个方法也就是我们的动态代理实例类,业务上有需要就可以使用它。(所以千万不要在invoke方法里把请求分发给第一个参数,否则很明显就死循环了)

    C类中有B中所有方法的成员变量

      private static Method m1;
      private static Method m3;
      private static Method m4;
      private static Method m2;
      private static Method m0;
    

    这些变量在static静态代码块初始化,这些变量是在调用invocationhander时必要的入参,也让我们依稀看到Proxy在生成C时留下的痕迹。

    static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
          m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          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动态代理类图如下

    cglib动态代理

    我们了解到,“代理”的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有、包装对象这一种方式。

    通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。

    先看下代码

    package proxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxy implements MethodInterceptor
    {
        // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
        public Object CreatProxyedObj(Class<?> clazz)
        {
            Enhancer enhancer = new Enhancer();
            
            enhancer.setSuperclass(clazz);
            
            enhancer.setCallback(this);
            
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        } 
    }
    

    从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。

    cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

    这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

    C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。

    这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?

        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        }

     因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。

    所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。

    fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。

    对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。

    参考

    https://blog.csdn.net/jiankunking/article/details/52143504

    http://www.php.cn/java-article-407212.html

    https://www.cnblogs.com/chinajava/p/5880887.html

    https://rejoy.iteye.com/blog/1627405

    测试

       public static void main(String[] args)
        {
            int times = 1000000;
            
            Star ldh = new LiuDeHua();
            StarProxy proxy = new StarProxy();
            proxy.setTarget(ldh);
            
            long time1 = System.currentTimeMillis();
            Star star = (Star)proxy.CreatProxyedObj();
            long time2 = System.currentTimeMillis();
            System.out.println("jdk创建时间:" + (time2 - time1));
            
            CglibProxy proxy2 = new CglibProxy();
            long time5 = System.currentTimeMillis();
            Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class);
            long time6 = System.currentTimeMillis();
            System.out.println("cglib创建时间:" + (time6 - time5));
            
            long time3 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star.sing("ss");
                
                star.dance("ss");
            }
            long time4 = System.currentTimeMillis();
            System.out.println("jdk执行时间" + (time4 - time3));
            
            long time7 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star2.sing("ss");
                
                star2.dance("ss");
            }
            
            long time8 = System.currentTimeMillis();
            
            System.out.println("cglib执行时间" + (time8 - time7));   
        }

    经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。

    展开全文
  • 1 什么是代理 代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。举个例子,如A对象有若干个方法,这时A对象对B对象进行委托授权,B对象便成了A对象的代理方,因此B对象便可对A对象进行访问并调用A...
  • 一个简单的java动态代理实例
  • 主要介绍了Java JDK动态代理实现原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要介绍了Java动态代理静态代理实例分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • Spring AOP中的JDK和CGLib动态代理哪个效率更高?

    万次阅读 多人点赞 2018-09-07 16:52:54
    一、背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 ...自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方。 JDK动态代理主要涉及...
  • 上篇介绍了一下静态代理:Java中的代理模式——静态代理以及分析静态代理的缺点 也分析了一下静态代理的缺点: 1、由于静态代理中的代理类是针对某一个类去做代理的,那么假设一个系统中有100个Service,则需要...
  • java 动态代理 简单实例 新手看看
  • 什么是Java动态代理,如何实现一个动态代理例子

    千次阅读 多人点赞 2019-09-04 11:06:15
    Java动态代理 一、概述 1. 什么是代理 我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说...
  • JDK动态代理技术动态代理最常见应用是AOP(面向切面编程)。通过AOP,我们能够地拿到我们的程序运行到某个节点时的方法、对象、入参、返回参数,并动态地在方法调用前后新添一些新的方法逻辑,来满足我们的新需求,...
  • Java实现动态代理示例

    千次阅读 2018-08-15 10:38:16
    首先定义一个接口 ...本文中的动态代理是通过JDK动态代理机制实现的,必须要先实现业务接口,即IStar接口;然后根据反射机制找到对应的被代理的类,就可以在动态生成的代理类中调用业务实现类的同名方法。
  • JAVA动态代理实例

    2007-11-25 15:25:22
    JAVA的动态代理的一个例子
  • jdk动态代理和CGLIB动态代理的区别

    千次阅读 2019-01-29 15:11:40
    java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。 1...
  • 1.默认 EntityFramework 使用延迟加载,使用动态代理实例 2.无论是否启用延迟加载,对于IQueryable 都是在拼接查询sql,并没有直接去数据库执行sql 3.跟踪实践证明,对于关联表数据获取没有没有跟踪到执行Sql语句(不...
  • Spring AOP使用的核心技术是动态代理,说到动态代理就不得不和设计模式中的代理模式联系起来,通过代理模式我们可以对目标类进行功能增强,在某个方法的执行前后增加一些操作,例如计算方法执行效率、打印日志等。...
  • CGLib动态代理基本原理CGLib——Code Generation Library,它是一个动态字节代码生成库,基于asm。使用CGLib时需要导入asm相关的jar包。而asm又是何方神圣? asm是一个java字节码操纵框架,它能被用来动态生成类...
  • 本文详细介绍了Java编程中使用动态代理实现AOP功能,AOP是OOP的延续,意思是面向切面编程。 从这篇文章可以大体理解spring AOP的原理。
  • jdk动态代理与CGLib的区别

    万次阅读 2017-09-20 22:31:10
    动态代理proxy与CGLib的区别 标签: 代理模式 2013-09-03 08:50 ...昨天被人问及动态代理与CGlib的区别,赶紧回顾一下: ...静态代理与动态代理静态代理实例JDK动态代理实例CGLib 简介CGLib 与JD
  • java jdk动态代理和cglib动态代理对比,实现,区别 jdk动态代理和cglib动态代理对比 jdk动态代理 特点 Interface:对于JDK Proxy,业务类是需要一个Interface的,这是一个缺陷; Proxy:Proxy类是动态产生的,这个类...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 390,557
精华内容 156,222
关键字:

动态代理实例