Java设计模式——04代理模式

目录

一、代理模式简介

1、代理模式的定义

2、组成

3、优缺点

二、静态代理

1、静态代理的实例

 2、静态代理的优缺点

三、动态代理

(一)jdk动态代理模式

>1)模式要点分析

>2)反射实例

>3)动态代理源码分析

>4)动态代理实例

>5)InvocationHandler分析

>6)总结下动态代理的流程:

(二)cglib的动态代理

       实例1:

       实例2:

最后,作为一个搬砖者,再次感谢各位大神的分享,好人一生平安!



一、代理模式简介

1、代理模式的定义

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

2、组成

抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

3、优缺点

优点: 1、职责清晰。 2、高扩展性。 3、智能化。

缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰者模式的区别:装饰者模式为了增强功能,而代理模式是为了加以控制。

二、静态代理

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。

所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

 一句话,自己手写代理类就是静态代理。

--. 静态代理模式类图如下:

被代理对象RealSubject负责执行相关业务逻辑,不同的被代理对象处理不同的业务,而代理对象只需要访问这些业务方法就行,不需要处理具体业务逻辑,但是针对某一类共同问题的的处理,就可以移到代理对象里来实现对方法的增强,由于代理模式功能增强这一块在实际开发中尤为突出,所以后面的例子也会稍微针对一下。

1、静态代理的实例

  • Subject:定义一个普通的接口方法
public interface Moveable {
    void move();
}
  • RealSubject:实现该接口方法
public class Car implements Moveable {

    @Override
    public void move() {
        System.out.println("汽车行驶中....");
    }

}
  • ProxySubject: 调用RealSubject的方法,并在调用前后进行增强处理。
public class CarProxy implements Moveable{
    private Moveable move;
    
    @Override
    public void move() {
        if(move==null){
            move = new Car();
        }
        System.out.println("开始记录日志:");
        move.move();
        System.out.println("记录日志结束!");
         
    }
}
  • 测试方法:实际上是调用ProxySubject的move()方法。
  public static void main(String[] args) throws Exception {
        Moveable m =new CarProxy();
        m.move();
    }

 2、静态代理的优缺点

优点

业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理模式的共有优点。

缺点

1)代理对象的一个接口只服务于一种类型的对象,如果要代理的类型很多,势必要为每一种类型的方法都进行代理,静态代理在程序规模稍大时就无法胜任了。

  • 比如Car类的move()方法需要记录日志,如果还有汽车,火车,自行车类的move()方法也需要记录日志,我们都要一个个的去为它们生成代理类,太麻烦了。

2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。显而易见,增加了代码维护的复杂度。 

三、动态代理

动态代理有jdk动态代理模式和cglib代理模式。JDK的动态代理只能代理实现了接口的类, 没有实现接口的类不能实现动态代理;而cglib的动态代理是通过继承实现的,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

(如果读者只想知道动态代理如何使用,可直接阅读 >4)动态代理实例

(一)jdk动态代理模式

》备注:源码分析版:https://www.jianshu.com/p/5478f170d9ee     

》简单实例版:https://www.cnblogs.com/yueshutong/p/9500632.html

动态代理:在程序运行时,通过反射机制动态地创建一个代理类。

>1)模式要点分析

1,动态的体现:程序开始执行时是没有代理类的,在程序运行时,java利用反射机制动态生成代理类的实例
2,jdk技术支持:java在java.lang.reflect包里提供两个类/接口帮助我们创建和使用代理类实例,一个是Proxy类,一个是InvocationHandler接口。
3,两种代理类的模式区别:静态模式要求为程序中所有需要被访问的目标创建代理类,如果有10种代理目标,我们就得创建10个代理类,让代理类直接访问目标对象,下面是其简易关系图。

动态代理则是在程序调用某个目标类时才通过反射创建代理类的实例,创建过程并不是在开发层面可见的,又加之常常需要在代理类中进行一些额外操作来加强目标类的功能,如果代理类不可见我们就不能达到这个目的,所以这时在代理类和被代理类之间就需要一个第三者,帮助我们完成功能增强,它就是InvocationHandler。下面给出动态代理简易关系图。

对动态代理有初步了解后,我们下面通过源码和实例来分析创建和使用动态代理类的过程。

>2)反射实例

学习动态代理前,我们来先看一个反射的例子,对动态代理的过程理解很有帮助

  • 创建一个被反射的对象,它包含一个String和int类型的构造,和一个getName方法
public class Student {

    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
}

在MainActivity里使用反射获取Student对象的实例,并调用getName方法

  public void reflect() throws Exception {

        //通过反射获取类
        Class<Student> studentClass = Student.class;

        //获取构造时,需要传入的该构造的参数类型
        Class[] classes = new Class[]{String.class, int.class};

        //获取类的参数为String和int类型的public构造函数
        Constructor constructor = studentClass.getConstructor(classes);

        //通过构造参数创建一个Student类的实例
        Student zhangsan = (Student) constructor.newInstance("张三", 18);

        //获取Student对象里名字为“getName”的方法
        Method method = studentClass.getMethod("getName", new Class[]{});

        //执行方法  实例zhangsan的"getName"方法
        String name = (String) method.invoke(zhangsan, new Object[]{});

        //打印我们创建实例对象时传入的name,看是否能通过"getName"方法获取到
        Log.e("miss08", "name = " + name);

    }
  • 看最后的打印
04-27 10:37:15.796 31008-31008/com.example.pver.proxydesign E/miss08: name = 张三
  • 通过反射获取Student类,获取其有参构造,通过构造获取实例,再调用反射类里方法,请大致记住上面反射的流程,接下来我们通过源码分析动态代理的创建过程

>3)动态代理源码分析

  • 先给出代码流程图(流程图后面还会给出,先大致看看)

  • 动态代理需要JVM帮我们创建代理类,就需要jdk提供一些类来支持创建工作,这些类大多存在于java.lang.reflect包里,负责创建工作的类是Proxy,下面我们看看Proxy这个类:
public class Proxy implements Serializable {
    
    //被代理类构造函数的参数类型,即InvocationHandler
    private static final Class<?>[] constructorParams =
            { InvocationHandler.class };

    //代理对象的创建方法,通过Proxy.newProxyInstance(...)生成代理对象
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
        
        ...省去一些方法,只显示核心代码

        final Class<?>[] intfs = interfaces.clone();
        //1,通过类加载器和接口创建代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        //2,通过反射获取代理类中public类型的构造,且参数类型为InvocationHandler
        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;
                }
            });
        }
        //3,通过构造,创建代理类的实例对象,并返回
        return cons.newInstance(new Object[]{h});
     }

    //通过类加载器和接口,动态创建代理类。
    public static Class<?> getProxyClass0(ClassLoader loader, Class... interfaces)
    {...}
 
}

 通过代理类的创建方法newProxyInstance,我们可以看到一个代理对象的创建需要三个步骤三个核心参数,它们分别为:

三个步骤为:
1,通过类加载器和接口创建代理类,方法为getProxyClass0(loader,interfaces)
2,通过反射获取代理类的构造,且构参为InvocationHandler类型。
3,通过构造函数生成代理对象的实例。

三个参数为:
1,类加载器:ClassLoader 将代理类的字节码转换成class类的对象
2,被代理类的接口列表:Class<?>[] interfaces,通过接口动态生成代理类字节码
3,InvocationHandler接口:被代理类的直接调用者

代理类实例创建的步骤最重要的是第一步代理类的创建,后面两步是最基本的反射,我们就结合后面实例来分析,下面我们先来分析第一步代理类的创建,在分析的过程中顺带说说三个参数。

1、代理类Proxy的创建

  • getProxyClass0()方法创建了代理类,我们先来分析它的两个参数,然后再通过源码分析方法。
  • ClassLoader类加载器:将类的二进制数据(Class文件)读到内存,然后在堆区创建这个类的Class对象。看看它常用的方法,截取源码如下:
public abstract class ClassLoader {

    //类加载器作用就是加载字节码,并转换为java.lang.Class类的一个实例,即一个java类
    protected final Class<?> defineClass(byte[] b, int off, int len) ...{
        throw new RuntimeException("Stub!");
    }

    protected final Class<?> defineClass(String name, byte[] b, int off, int len){
        throw new RuntimeException("Stub!");
    }
}
  • 被代理类接口:代理类要实现与被代理对象相同的方法,在面向对象编程里,要么采用继承要么通过实现同一功能接口来完成,在后面的分析我们会知道代理类会继承Proxy,由于对象的单继承性,所以jdk动态代理就必须通过接口的形式来完成。在Proxy类里getProxyClass0方法通过类加载器和接口对象创建出代理类。
  • 接下来看看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) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        //从缓存获取代理类,如果缓存没有,就通过ProxyClassFactory去创建
        return proxyClassCache.get(loader, interfaces);
    }

getProxyClass0方法里首先做接口长度验证,接着会用类加载器和接口从proxyClassCache缓存里去取代理类,如果缓存里有代理类的class对象,就直接返回,不用再做后续操作,提高了创建效率。如果缓存里没有就用ProxyClassFactory去创建代理类。我们先看看proxyClassCache,它是一个WeakCache缓存类。

    /**
     * a cache of proxy classes
     * KeyFactory:key和parameters映射sub-key
     * ProxyClassFactory:key和parameters映射proxy
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
       proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

   WeakCache<K,P,V> ---> 分别代表ClassLoader,interfaces 和 proxy

WeakCache对象在创建时,会传入两个映射接口,一个是key和parameters映射sub-key的KeyFactory,另一个是以key和parameters映射proxy的valueFactory,下面就看看这两个映射在proxyClassCache的get方法里的体现。

 public V get(K key, P parameter) {
        
         //使用WeakReference引用key,当key为null时能立马回收
         Object cacheKey = CacheKey.valueOf(key, refQueue);

        // map是一个ConcurrentMap,用cacheKey为key来缓存代理类,cacheKey支持为null
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);

        //如果在ConcurrentMap里没找到key对应的值,则创建一个添加到集合里
        if (valuesMap == null) {

            //ConcurrentMap并发线程安全,putIfAbsent方法有返回值,需要对返回值进行非空处理
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                    = map.putIfAbsent(cacheKey,
                    valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

       //创建subKey,保证subkey对应的代理类存在于缓存里
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        //如果有值就直接返回supplier.get()
        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

上面分析proxyClassCache.get(...)方法,知道了如何从缓存里取到代理类,但是当缓存里没有值时,会通过ProxyClassFactory创建代理类,我们找到ProxyClassFactory,它是一个静态内部类,分析请看里面的注释:

//只展示核心代码
private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
     
     //所有创建的代理类的名称的前缀必须是"$Proxy"
     private static final String proxyClassNamePrefix = "$Proxy"

     //完成一些验证工作,如类加载器可用于加载接口类型等
     @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());
           }
     }

     //通过代理类名称和接口动态生成生成代理类字节码
     byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
     try {
          //返回代理类,这里没找到本地实现
          return defineClass0(loader, proxyName,
                proxyClassFile, 0, proxyClassFile.length);
       } catch (ClassFormatError e) {
           throw new IllegalArgumentException(e.toString());
     }
}

 从上面方法里我们找到了生成代理类字节码生成代理类的方法:

1,通过代理类名字和接口生成代理类字节码 : ProxyGenerator.generateProxyClass(...)
2,使用类加载器和代理类字节码等,生成代理类:defineClass0(...)

我们先看看生成Prox字节码的方法,这个类ProxyGenerator存在sun包里,这个包不是开源包,在文章结尾我会上传这个反编译的包,下面我们就来看看这个方法:

public static byte[] generateProxyClass(final String name,Class[] interfaces) {
   
    //通过代理类类名和接口生成字节码。
    ProxyGenerator gen = new ProxyGenerator(name, interfaces);
    final byte[] classFile = gen.generateClassFile();

    //saveGeneratedFiles标记位,判断是否把这个字节码文件写入到某个class文件
    if (saveGeneratedFiles) {
        java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction() {
                    public Object run() {
                        try {
                            FileOutputStream file =
                                new FileOutputStream(dotToSlash(name) + ".class");
                            file.write(classFile);
                            file.close();
                            return null;
                        } catch (IOException e) {
                            throw new InternalError(
                                    "I/O exception saving generated file: " + e);
                        }
                    }
           });
    }
}

generateProxyClass方法生成字节码并且会把字节码文件存储,这里我们可以在本地创建一个代理类文件用来存储字节码,反编译查看字节码里的信息。defineClass0生成代理类的方法这里就不分析了。接下来看看第三个参数InvocationHandler。

  • InvocationHandler:这是jdk为动态代理提供的第二个核心类/接口,首先还是看看它的源码:
    public interface InvocationHandler {
    
        /**
        *  proxy:代理类
        *  method:调用被代理对象的某个方法的Method对象
        *  args:调用被代理对象的方法需要的参数
        */
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }

InvocationHandler里只用一个invoke方法,从上面分析我们知道,通过反射获取代理类的构造时会传入构造参数InvocationHandler,InvocationHandler作为实际的被代理类的控制者,不管用户想要调用哪个被代理类的哪个方法,我们只需要通过代理类实例调用InvocationHandler的invoke方法,传入方法名和参数即可,要分析InvocationHandler在动态代理里起的作用,就需要先拿到代理类的字节码,下面就通过具体案例来说明。

>4)动态代理实例

JDK动态代理实现步骤:

  1. 创建被代理的类以及实现的接口;
  2. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法;
  3. 调用Proxy的newProxyInstance静态方法,创建一个代理类。
  4. 通过代理对象调用目标方法。
  • 1 创建一个Subject接口,里面有三个方法
public interface Subject {

    String speak();
}
  • 2  创建一个目标对象实现接口
public class TargetSubject implements Subject {

    @Override
    public String speak() {
        System.out.println("speak方法执行");
        return "结束";
    }
}
  • 3  创建handler,实现InvocationHandler,Proxy创建代理类建时需要这个handler参数。在handler的invoke方法里,执行被代理对象中参数为args的method方法,method是在代理类中通过反射获得,传入handler的,这个后面会说明。
public class DynamicInvocationHandler implements InvocationHandler {

    private Subject subject;

    public DynamicInvocationHandler(Subject subject) {
        this.subject = subject;
    }

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

        //增强处理
        System.out.println("增强处理--前");
    
       
        //通过反射获取到接口的method,执行subject目标对象中带参数args的method方法
        String response = (String) method.invoke(subject, args);

        
        //增强处理
        System.out.println("增强处理--后");

        return response;
    }
}
  • 4  在Main方法中创建代理类,测试代理类创建流程
    Subject targetSubject = new TargetSubject();
    InvocationHandler handler = new DynamicInvocationHandler(targetSubject);
  
    //创建代理类的实例
    Subject proxySubject = (Subject) Proxy.newProxyInstance(targetSubject.getClass()
            .getClassLoader()
            , targetSubject.getClass().getInterfaces()
            , handler);

      //调用被代理类的方法
    proxySubject.speak();
  • 查看结果:

增强处理--前
speak方法执行
增强处理--后

>5)InvocationHandler分析

接下来看看代理类的字节码文件里的信息,看看代理类是如何通过InvocationHandler来控制目标对象的。

  • 获取字节码文件的方法:上面的分析,通过ProxyGenerator.generateProxyClass里可以代理类的字节码,通过输出流可以写到本地文件如$Proxy0.class,然后直接通过反编译查看。
public final class $Proxy0
extends Proxy
implements Subject {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    // 反射时获取这个构参的构造函数来创建代理类的实例对象
    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }
  
    //代理类实现方法,会执行InvocationHandler的invoke方法
    public final String getResponse(String string) {
        try {
            return (String)this.h.invoke((Object)this, m3, new Object[]{string});
        }
        catch (Error | RuntimeException v0) {
            throw v0;
        }
        catch (Throwable var2_2) {
            throw new UndeclaredThrowableException(var2_2);
        }
    }

    //静态代码块,反射得到我通过代理类调用的方法
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName
                 ("java.lang.Object"));
            m3 = Class.forName("com.example.pver.proxydesign.dynamicproxy.Subject")
                .getMethod("getResponse", 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 var1) {
            throw new NoSuchMethodError(var1.getMessage());
        }
        catch (ClassNotFoundException var1_1) {
            throw new NoClassDefFoundError(var1_1.getMessage());
        }
    }

字节码文件分析

1,通过Proxy.newProxyInstance生成的代理类,执行接口里的方法,实际上是调用了InvocationHandler 的
invoke方法(参考上面直接码里的getResponse方法),参数分别为当前代理类、用户执行的方法和我们调用该方法时传入的参数。
2,InvocationHandler需要用户自定义,在invoke方法里执行method.invoke(subject, args),用来执行目标对象里参数为args的方法method,故InvocationHandler才是真正的被代理类的直接访问者。
3,代理类继承了Proxy,由于对象的单继承性质,所以代理类不能再继承其他类,所以JDK的动态代理只支持接口的方式代理,并不能支持具体实现类的代理。

通过上面的分析,给出动态代理的详细类图:

>6)总结下动态代理的流程:

  • 1,jdk获取RealSubject上的所有接口列表,确定要生成的代理类类名。
  • 2,jdk根据接口信息和类名动态创建代理类的字节码。
  • 3,通过类加载器将字节码转换成代理类class对象。
  • 4,创建InvocationHandler的实例handler,用来处理对被代理类的所有方法的访问。
  • 5,通过反射获取代理类中以handler类为参数的构造。
  • 6,使用构造创建代理类的实例。
  • 7,用户通过代理类的实例,调用接口里的方法,将方法参数和该方法传入handler的invoke方法。
  • 8,handler的invoke方法里,调用method.invoke(subject, args),用来执行目标对象subject里参数为args的方法method。
  • 9,目标对象RealSubject执行具体业务逻辑,如果有返回值,就将返回传值回给handler。

(二)cglib的动态代理

       实例1:

引用cglib的依赖包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>2.2</version>
</dependency>

为了方便思考它的原理,我把执行步骤按顺序写下

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //设置父类,被代理类(这里是Car.class)
        enhancer.setSuperclass(Car.class);
        //设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                //增强处理...
                Object o= proxy.invokeSuper(obj, args);//代理类调用父类的方法
                //增强处理...
                return o;
            }
        });
        //创建代理类并使用回调(用父类Car去引用)
        Car car = (Car) enhancer.create();
        //执行目标方法
        System.out.println(car.move());
    }

方法拦截器

实现MethodInterceptor接口的intercept方法后,所有生成的代理方法都调用这个方法。

intercept方法的具体参数有

  1. obj 目标类的实例
  2. method 目标方法实例(通过反射获取的目标方法实例)
  3. args 目标方法的参数
  4. proxy 代理类的实例

该方法的返回值就是目标方法的返回值。

特点

  1. cglib的动态代理是针对类来实现代理。
  2. 对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用。
  3. 因为是通过继承实现,final类无法使用。

       实例2:

静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
  • Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

Cglib子类代理实现方法: 

1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

代码示例:
目标对象类:UserDao.java

/**
 * 目标对象,没有实现任何接口
 */
public class UserDao {

    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

Cglib代理工厂:ProxyFactory.java

/**
 * Cglib子类代理工厂
 * 对UserDao在内存中动态构建一个子类对象
 */
public class ProxyFactory implements MethodInterceptor{
    //维护目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务...");

        //执行目标对象的方法
        Object returnValue = method.invoke(target, args);

        System.out.println("提交事务...");

        return returnValue;
    }
}

测试类:

/**
 * 测试类
 */
public class App {

    @Test
    public void test(){
        //目标对象
        UserDao target = new UserDao();

        //代理对象
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();

        //执行代理对象的方法
        proxy.save();
    }
}

在Spring的AOP编程中:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理

最后,作为一个搬砖者,再次感谢各位大神的分享,好人一生平安!

参考文档1:https://www.jianshu.com/p/5478f170d9ee(重要参考文献)

参考文档2:https://www.cnblogs.com/yueshutong/p/9500632.html(次重点)

参考文档3:https://www.cnblogs.com/cenyu/p/6289209.html(cglib参考文献)

 

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值