设计模式:代理模式 、(不同代理模式剖析比较、jdk 动态代理源码深度解析)

版权声明:如需转载,请标明转载出处哦! https://blog.csdn.net/Z0157/article/details/82025837

目录

 

一、代理模式:

二、不同代理模式详解

1、静态代理

2、动态代理模式

源码解析:

3、动态代理的另外一种实现:cglib代理

三、CGLIB和Java动态代理的区别


一、代理模式:

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

二、不同代理模式详解

1、静态代理

创建步骤:

(1)、分别创建一个行为接口A,和该接口的实现类 B。实现接口中的具体行为业务方法;

(2)、创建一个代理类C实现行为接口A,并且该类通过构造函数引用了实现了行为接口的类B的对象。

(3)、代理类C中实现的接口A中的方法实现,是通过对象B的引用调用自己的所实现的业务方法sayHello。


代码实现:

public interface ISayHello {
    void sayHello();
}
public class SayHelloImpl implements ISayHello{
    @Override
    public void sayHello() {
        System.out.println("hello world");
    }
}
public class SayHelloProxy implements ISayHello {
    private ISayHello iSayHello;
    public SayHelloProxy(ISayHello iSayHello) {
        this.iSayHello = iSayHello;
    }
@Override
    public void sayHello() {
        System.out.println("开始事务");
        iSayHello.sayHello();
        System.out.println("结束事务");
    }
}
public class StaticProxyTest {
    public static void main(String[] args) {
        SayHelloImpl sayHello = new SayHelloImpl();
		  SayHelloProxy sayHelloProxy = new SayHelloProxy(sayHello);
        sayHelloProxy.sayHello();
    }
}

结果:
	开始事务
	hello world
	结束事务

缺点:

  • (1)、因为代理对象需要与目标对象同时要实现相同的接口,造成一个代理类对应一个目标类的局面,或者都集中写在一个                  代理类   中那么没增加一个行为接口的代理,就要增加implemets,和注入该目标对象的引用。如果目标类太多,则不                可控;
  • (2)、并且如果行为接口中增加行为,或者变更行为,那么代理对象和目标对象都要同时维护;维护成本也会越来越高;

解决方案:动态代理就是为了解决静态代理的缺点的。

2、动态代理模式

实现步骤:

(1)、通过实现InvocationHandler接口创建自己的调用处理器。

(2)、通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类。

(3)、通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型。

(4)、通过构造函数创建动态代理类实例,构造时调用处理器对象作。

代码实现如下:

源码解析:

  • target.getClass().getClassLoader()  :  获取代理对象的类加载器。
  • target.getClass().getInterfaces(): 获取代理对象实现的接口数组。
  • new InvocationHandler():创建执行代理对象指定方法的事件处理接口的实例。
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        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);
            }

            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);
        }
    }

我们通过解析源码来了解,动态代理是一个怎么的实现;

(1)、Objects.requireNonNull(h); 的实现如下:

    public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

如果传入的h,是null则会抛出空指针的异常;保证了h不为null才能继续走下去;

(2)、final Class<?>[] intfs = interfaces.clone();

首先我们来解析interfaces是怎么来的,通过发射获得代理对象实现的接口组;

源码如下:

 public Class<?>[] getInterfaces() {
        ReflectionData<T> rd = reflectionData();
        if (rd == null) {
            // no cloning required
            return getInterfaces0();
        } else {
            Class<?>[] interfaces = rd.interfaces;
            if (interfaces == null) {
                interfaces = getInterfaces0();
                rd.interfaces = interfaces;
            }
            // defensively copy before handing over to user code
            return interfaces.clone();
        }
    }



private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        // Intermediate results for getFields and getMethods
        volatile Field[] declaredPublicFields;
        volatile Method[] declaredPublicMethods;
        volatile Class<?>[] interfaces;

        // Value of classRedefinedCount when we created this ReflectionData instance
        final int redefinedCount;

        ReflectionData(int redefinedCount) {
            this.redefinedCount = redefinedCount;
        }
    }


ReflectionData为了提高反射的性能,缓存显然是必须的。class类内部有一个useCaches静态变量来标记是否使用缓存,这个值可以通过外部配置项sun.reflect.noCaches进行开关。
class类内部提供了一个ReflectionData内部类用来存放反射数据的缓存,并声明了一个reflectionData域,由于稍后进行按需延迟加载并缓存,所以这个域并没有指向一个实例化的ReflectionData对象。

从class内部的reflectionData缓存中读取数据interfaces,如果没有缓存数据,那么就从jvm中去请求数据,然后设置到缓存中,供下次使用。这种情况下是不需要interfaces.clone(); 如果缓存中有数据则需要interfaces.clone();一份返回;

(3)、克隆一份接口实例组;

final Class<?>[] intfs = interfaces.clone(); 

    找出缓存proxyClassCache中的代理类/接口,  如果没有就生成一个;代理接口数组不能大于65535,否则抛异常;

Class<?> cl = getProxyClass0(loader, intfs);

(4)、SecurityManager 是安全验证,正常情况下是null,不需要验证;

(5)、final Constructor<?> cons = cl.getConstructor(constructorParams);

private static final Class<?>[] constructorParams =
    { InvocationHandler.class };

constructorParams:标明代理类构造器的参数类型。

调用代理对象的构造函数(代理对象的构造函数$Proxy0(InvocationHandler h);通过反射工厂类,copy一份代理类的构造器出来;

(6)、生成代理类的实例,并把MyInvocationHander的实例作为构造函数参数传入;

return cons.newInstance(new Object[]{h});

看完源码我们已经了解代理类的实例的创建过程了,但是我们可能还有个疑问就是

invoke方法是怎么调用的?

其实刚才我已经说了,“ 生成代理类的实例,并把MyInvocationHander的实例作为构造函数参数传入;

通过字节码反编译可以查看生成的代理类:

生成的代码文件是:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.sjms.ISayHello;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements ISayHello {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void sayHello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.sjms.ISayHello").getMethod("sayHello");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

看到代理对象,编译后的代码,就明白为什么invoke方法被调用了;

JDK动态代理有个缺点,那就是不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,

那么就不能使用这种方式进行动态代理(因为$Proxy()这个类集成了Proxy,Java的集成不允许出现多个父类)。

3、动态代理的另外一种实现:cglib代理

(1)、定义:

有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理。(也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展)。

(2)、应用场景:

 Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用;

      例如Spring AOP和synaop,为他们提供方法的interception(拦截) Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.

Cglib子类代理实现方法:

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

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Description:
 * User: zhurong
 * Date: 2018-08-24  22:58
 */
public class SayHello {


    public static void main(String[] args) {
        SayHello sayHello = new SayHello();
        SayHelloCglib  cglib = new SayHelloCglib();
        SayHelloCglib cglibProxy=(SayHelloCglib)cglib.getInstance(sayHello);
        cglibProxy.sayHello();
    }
    public void sayHello(){
        System.out.println("hello world");
    }


    class SayHelloCglib implements MethodInterceptor {
        private Object target;//业务类对象,供代理方法中进行真正的业务方法调用

        //相当于JDK动态代理中的绑定
        public Object getInstance(Object target) {
            this.target = target;
            //创建加强器,用来创建动态代理类
            Enhancer enhancer = new Enhancer();
            //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
            enhancer.setSuperclass(this.target.getClass());
            //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
            enhancer.setCallback(this);
            // 创建动态代理类对象并返回
            return enhancer.create();
        }

        // 实现回调方法
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("处理前");
            proxy.invokeSuper(obj, args); //调用业务类(父类中)的方法
            System.out.println("处理后");
            return null;
        }
    }
}



总结:

三、CGLIB和Java动态代理的区别

  1.   Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);
  2. CGLIB能够代理普通类;
  3.  Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效。
     

猜你喜欢

转载自blog.csdn.net/Z0157/article/details/82025837