动态代理-两种实现方式

    代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。其中代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 
按照代理的创建时期,代理类可以分为两种。 
静态代理:就是为了在不入侵原本类的基础上,对功能进行扩展,或者过滤等等,是在编译期间已经存在于class文件里面。

动态代理:在程序运行时,运用反射机制动态创建。

今天我们只说动态代理,并分别讲述两种实现方式:

首先,我们先说明要被代理的对象,为接下来的工作做铺垫。

我们有这样一个接口(jdk动态代理必须有接口,后面说):

public interface MyInterface {
    public String getName();
    public int getAge();
}

有这么一个实现类:

public class MyInterfaceImpl implements  MyInterface {
    @Override
    public String getName() {
        System.out.println("tom");
        return "Tom";
    }

    @Override
    public int getAge() {
        System.out.println(11);
        return 11;
    }
}
代理就是在被代理的类的方法执行之前或者执行之后做一些事情,甚至过滤掉。所以我们在方法里输出一句话,便于展示。


JDK的动态代理

jdk的动态代理只有一个类和一个接口

InvocationHandler接口中只有一个方法

public interface InvocationHandler {
  public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
参数说明: 
Object proxy:指被代理的对象。 
Method method:要调用的方法 
Object[] args:方法调用时所需要的参数 


Proxy类是专门用来完成代理的操作类,可以为一个或多个接口生成实现类,具体方法为:

 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);
        }
参数说明: 
ClassLoader loader:类加载器 
Class<?>[] interfaces:全部的接口 
InvocationHandler h:InvocationHandler接口的实现类对象


动态代理类的字节码在程序运行时由Java反射机制动态生成,不需要手动编写代码,java反射机制可以生成任意类型的代理类,所以不仅用起来简单,而且扩展性得到了提高。我们看一个实例:

InvocationHandler接口的实现类:

public class MyInvocationHandler implements InvocationHandler {
    //要被代理的对象
    private Object target;
    MyInvocationHandler(){

    }
    MyInvocationHandler(Object target){
        super();
        this.target=target;
    }

    /**
     *
     * @param proxy 代理者
     * @param method 被执行的方法
     * @param args  执行方法时,要用到的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        if("getName".equals(name)){
            System.out.println("----before "+method.getName());
            Object res = method.invoke(target, args);
            System.out.println("----after"+method.getName());
            return res;

        }else{
            return method.invoke(target,args);
        }
    }
}

我们只在getName方法之前和之后输出一些东西。

测试代码:

 public static void main(String[] args) {
        MyInterface myInterface=new MyInterfaceImpl();
        MyInvocationHandler myInvocationHandler= new MyInvocationHandler(myInterface);
        MyInterface inter= (MyInterface) Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),myInterface.getClass().getInterfaces(),myInvocationHandler);
        inter.getName();
        inter.getAge();
}

结果:

----before getName
tom
----aftergetName

11

从上述测试代码可以看出,要想使用JDK的动态代理,被代理类一定要实现某个接口,这是一个很大的缺陷。对于没有实现接口的类,我们要动态代理,就要使用下面的方法。


Cglib动态代理

Cglib是针对实现类来代理的,被代理者不需要实现接口,它对目标类生成一个子类,并覆盖其中的方法,以实现方法的增强。

cglib的主要方法拦截接口 MethodInterceptor,需要用户自己实现:

public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

参数说明:

前三个参数与jdk中InvocationHandler中的Invoke相同:

Object var1:指被代理的对象。
Method var2:要调用的方法 
Object[] var3:方法调用时所需要的参数 

MethodProxy var4: JDK的java.lang.reflect.Method类的代理类,可以实现对源对象方法的调用。后面的例子将会帮助理解。


net.sf.cglib.proxy.Enhancer:cglib主要的增强类,下面简单看下其中方法:

setSuperclass: 设置被代理的对象。

setCallback: 设置回调函数,一般为MethodInterceptor的实现类。

creat: 创建一个代理对象,你可以直接使用创建的代理对象调用原本的方法。

看个实例:

public class MyCglibInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("----------before----"+method.getName());
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("----------after----"+method.getName());
        return o1;
    }
}

之前说了,cglib是创建一个子类,并覆盖目标类方法,所以我们调用方法时,是使用methodProxy.invokeSuper()。我们在每个方法之前之后都输出一些语句。

测试代码:

 MyCglibInterceptor myCglibInterceptor=new MyCglibInterceptor();
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(myInterface.getClass());
        enhancer.setCallback(myCglibInterceptor);
        MyInterface res = (MyInterface) enhancer.create();
        res.getName();
        res.getAge();

结果:

----------before----getName
tom
----------after----getName
----------before----getAge
11
----------after----getAge





猜你喜欢

转载自blog.csdn.net/wy9717/article/details/80210203