Java的动态代理和他的应用

Java的动态代理有JDK原生的代理和cglib两种代理模式, 在我们分析源代码的时候,经常可以看到动态代理的身影,所以这部分是必需要理解并且要记住的一个知识点和常识。

 

一、代理模式

想要了解java的动态代理,我首先复习一下代理模式. 代理模式的意思是,当你有一个事情要做的时候,不自己去做而交给一个代理去做,这样做的好处是,在做这件事情之前和之后,能够添加一些业务逻辑。这样做的好处是,将代理和核心逻辑分离,也就是说之前和之后的逻辑以及要去做的核心逻辑是分离的,可以分别交给不同的人去写这个代码,改动也不会相互影响,举例如下:
a. 首先是本来的类

/**
 * Created by longan.rtb on 15/6/2.
 */
public  class Test {

    public void test() {
        System.out.print("main method!");
    }

}

b. 然后代理的类

/**
 * Created by longan.rtb on 16/11/23.
 */
public class TestProxy {

    private Test test;

    TestProxy(Test test) {
        this.test = test;
    }

    public void ProxyTest() {
        System.out.print("before call method!");
        test.test();
        System.out.print("after call method!");

    }
}

 

c. 最后是客户端的使用方法

/**
 * Created by longan.rtb on 15/6/2.
 */
public class TestTest {

    public static  void  main(String[] args) {
        Test test = new Test();
        TestProxy testProxy = new TestProxy(test);
        testProxy.ProxyTest();
    }
}

 

二、如果我们的运行模式before\after是相同的,例如打日志, 仅仅是持有的Test对象不同,调用的方法不同,那么我们是不是每一个类都必须得有一个绑定的代理类呢? 这样就出现了大量的重复代码,java的动态代理就是解决这个问题的,他的实现一共有两种方式。

  1. JDK的动态代理实现方法,这个需要靠你记忆住了.

a. 首先是定义一个接口

/**
 * Created by longan.rtb on 16/11/23.
 */
public interface TestInterFace {
     void test();
}

 

b. 接着是这个接口的实现

/**
 * Created by longan.rtb on 15/6/2.
 */
public  class Test implements TestInterFace{

    public void test() {
        System.out.print("main method!");
    }

}

 

c. 规定:需要一个动态代理的实现(你要在代理之前、之后干啥事情),必须实现InvocationHandler接口

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * Created by longan.rtb on 16/11/23.
 */
public class TestProxy implements InvocationHandler {

    private Object target;

    TestProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
        System.out.print("before call method!");

        method.invoke(target, args);

        System.out.print("after call method!");

        return null;

    }
}

 

d. 最后是调用的方法client看下

import java.lang.reflect.Proxy;

/**
 * Created by longan.rtb on 15/6/2.
 */
public class TestTest {

    public static  void  main(String[] args) {
        Test test = new Test();
        TestProxy testProxy = new TestProxy(test);
        TestInterFace subject = (TestInterFace) Proxy.newProxyInstance(
                testProxy.getClass().getClassLoader(),
                test.getClass().getInterfaces(),
                testProxy);
        subject.test();
    }
}

 

那么回到我们刚刚提出的那个问题,这里是Test,你也可以代理Test1, Test2, Test3, TestN....等等,而不用写那么多的代理类了,我这里一个代理类足够了
那么proxy.newProxyInstance的方法返回的是个啥玩意儿呢?他是Test对象吗?显然不是,那么他是TestProxy对象吗?也不是,否则的强制转换都要出错,他是由jvm生成的一个动态对象,该对象实现了TestInterface接口当然就可以直接调用test方法喽,这要得益于newProxyInstance这个方法中的第二个参数,明确的告诉他,被代理的target对象是个什么玩意儿的接口.

三、 我们继续提出下一个问题,你这个Test类必须得实现接口,才能工作,那么我可不可以就针对一个类来创建一个动态代理,不需要传入接口类型呢? 答案是肯定的,他就是cglib干的事情

a. 一个类,没有实现任何接口

/**
 * Created by longan.rtb on 15/6/2.
 */
public  class Test {

    public void test() {
        System.out.print("main method!");
    }

}

b. 一个代理类, 必须按规定写

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * Created by longan.rtb on 16/11/23.
 */
public class TestProxy implements MethodInterceptor {

    private Object target;

    public Object getProxyObject(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    @Override
    // 回调方法
    public Object intercept(Object obj, Method method, Object[] args,
                            MethodProxy proxy) throws Throwable {
        System.out.println("before method call");
        proxy.invokeSuper(obj, args);
        System.out.println("after method call");
        return null;


    }
}

 

getProxyObject是为了获得一个对象,这个对象则是最终执行方法的主体,这个对象是你传入的对象(并不关心代理的到底是谁)
intercept则是你必须要实现的方法,你可以在这个方法里做你想做的前和后逻辑

c. 客户端的实现

/**
 * Created by longan.rtb on 15/6/2.
 */
public class TestTest {

    public static  void  main(String[] args) {
        Test test = new Test();
        TestProxy testProxy = new TestProxy();
        Test bookCglib=(Test)test.getProxyObject(test);
        test.test();

    }
}

 

将test传给了testProxy就可以得到一个Test对象,因为他返回的是Test的一个子类,这样你在调用test的方法的时候,其实是偷偷的执行了动态代理里面的代码

四、 具体引用

实际上动态代理在很多地方都有用,例如Spring的AOP的原理就是动态代理,我们看看mybatis的mapper发现他只有接口没有方法,他是怎么工作的呢?
我们看下这个类MapperProxyFactory,里面有个方法

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

 

他为每个mapper创建了一个动态代理,这个动态代理就是实现了interface的对象,可以直接调用. 而通过注解可以获取mysql的具体信息,所以根本不需要具体的实现类.

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注