java中动态代理及基本原理基于《java编程的逻辑》

520 阅读4分钟

概念

    说到动态代理就会联想到热门的java框架spring中强大的AOP功能,使用AOP将切面,与主体相分离,增加了程序的灵活性。那到底什么是动态代理呢?其实所谓的动态代理跟反射一样,是java中的一个动态特性

  • 可以在运行时动态创建一个类,实现一个或者多个接口
  • 可以在不修改原有类的基础上动态为通过该类获取的对象添加方法修改行为,并广泛的应用于spring,mybatis等热门的框架中

在java中与动态代理相对应的就是静态代理,我们这里先讨论一下什么是静态代理

静态代理

静态代理是一种设计模式,代理背后一般至少有一个实际对象 我们来看一个例子:

public class SimpleStaticProxyDemo {
    static interface IService{
        public void sayHello();
    }

    //被代理的类
    static class RealService implements IService{
        @Override
        public void sayHello() {
            System.out.println("hello");
        }
    }

    //实际代理的类
    static class TraceProxy implements IService{
        //让代理的类就有被代理类的功能
        private IService realService;
        public TraceProxy(IService realService){
            this.realService = realService;
        }
        @Override
        public void sayHello() {
            //不改变原来的程序,并加入新的东西,下面同理
            System.out.println("进入 sayHello");
            realService.sayHello();
            System.out.println("离开 sayHello");
        }
    }

    public static void main(String[] args) {
        //实例化元类
        IService realService = new RealService();
        //实例化代理类
        IService proxyService = new TraceProxy(realService);
        proxyService.sayHello();
    }
}

代理和实际的对象一般会有一个相同的接口:IService
实际的对象:RealService
那自然代理它的就是:TraceProxy并且在构造方法中传入被代理类的对象

    我们简单的理解就是TraceProxy代理了RealService的功能,并且在它的基础上添加了打印日志的功能:System.out.println("进入 sayHello");和System.out.println("离开 sayHello");并且并没有修改原有的类。并且最后通过TraceProxy的对象调用sayHello()达到同样的功能。

程序运行结果:

aaaa.png

    但是这样其实很麻烦,比如我们有很多的类需要代理的话,那么就需要为每个类都要写一个代理类,这样就不太好了,那么此时动态代理就孕育而出了。

动态代理

先看一个动态代理的例子:

public class SimpleJDKDynamicproxyDemo {
    static interface IService{
        public void sayHello();
    }
    //元类
    static class RealService implements IService{
        @Override
        public void sayHello() {
            System.out.println("hello");
        }
    }
    //代理类
    static class SimpleInvocationHandler implements InvocationHandler {
        private Object realObj;
        public SimpleInvocationHandler(Object realObj) {
            this.realObj = realObj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("进入" + method.getName());
            //达到了调用实际对象对应的方法的目的
            Object result = method.invoke(realObj,args);
            System.out.println("离开" + method.getName());
            return result;
        }
    }

    public static void main(String[] args) {
        //实例化元类
        IService realService = new RealService();
        //实例化代理类
        IService proxyService = (IService) Proxy.newProxyInstance(
                IService.class.getClassLoader(),
                new Class<?>[]{IService.class},
                new SimpleInvocationHandler(realService));
        proxyService.sayHello();
    }
}

就是让代理类去implements一个接口InvocationHandler,并且重写接口里面的方法invoke,invoke中的参数意思如下:

  • proxy:表示代理对象本身,不是被代理的对象,这个参数一般用处不大
  • method: 表示正在被调用的方法
  • args: 表示方法的参数

    与静态代理不同的是,创建代理类对象的方式也不同了,变为了

IService proxyService = (IService) Proxy.newProxyInstance(
        IService.class.getClassLoader(),
        new Class<?>[]{IService.class},
        new SimpleInvocationHandler(realService));

在Proxy.newProxyInstance()方法中形参的意思是:

  • loader:表示类加载器
  • interface:表示代理要实现的接口列表,元素的类型只能是接口,不能是普通的类
  • h:类型为InvocationHandler对代理接口所有的方法调用都会传给该接口,注意这里就传入了InvocationHandler的实现类SimpleInvocationHandler

我们就使用proxyService就可以调用sayHello的方法了
程序运行结果:

3333.png
下面我们探讨一下动态代理的基本原理

基本原理

实际上,创建代理对象的方式也可以是这样的:

//创建代理类
Class<?> proxyClass = Proxy.getProxyClass(IService.class.getClassLoader(), new Class<?>[]{IService.class});
//获取代理类参数类型是InvocationHandler的构造器
Constructor<?> constructor = proxyClass.getConstructor(new Class<?>[]{InvocationHandler.class});
InvocationHandler handler = new SimpleInvocationHandler(realService);
//通过这个构造器去实例化对象
IService proxyService = (IService) constructor.newInstance(handler);
proxyService.sayHello();

    那么在第一步的时候,会动态生成一个类,这个代理类的名字是$Proxy,这个类继承了Proxy,并且实现了代理接口Iservice,这个类有个构造方法,它实际上调用的是父类的参数类型为InvocationHandler的构造方法,那么这个动态生成的代理类就可以使用这个h了,这个代理类又实现了Iservice接口,那么就可以通过它的对象调用接口的方法,怎么调用的呢?

    我们以sayHello来举例子,实际上就是通过h.invoke.(this,m3,null)来实现的,这个invoke是不是很熟悉?实际上就是例子中的代理类SimpleInvocationHandler中的invoke方法,这样就一目了然了。

    动态生成的代理类,在调用方法的时候,通过h.invoke实际上调用的就是在例子中定义的代理类中重写的invoke方法,通过重写的invoke中的Object result = method.invoke(realObj,args);真正调用该方法。代理类对象调用不同方法的时候给invoke传递的参数也是不同的

下一篇将说明动态代理的优点,以及实现一个简单的AOP功能