计算机程序的思惟逻辑 (86) - 动态代理

本系列文章经补充和完善,已修订整理成书《Java编程的逻辑》(马俊昌著),由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买:京东自营连接 html

前面两节,咱们介绍了反射注解,利用它们,能够编写通用灵活的程序,本节,咱们来探讨Java中另一个动态特性 - 动态代理。java

动态代理是一种强大的功能,它能够在运行时动态建立一个类,实现一个或多个接口,能够在不修改原有类的基础上动态为经过该类获取的对象添加方法、修改行为,这么描述比较抽象,下文会具体介绍,这些特性使得它普遍应用于各类系统程序、框架和库中,好比Spring, Hibernate, MyBatis, Guice等。git

动态代理是实现面向切面的编程(AOP - Aspect Oriented Programming)的基础,切面的例子有日志、性能监控、权限检查、数据库事务等,它们在程序的不少地方都会用到,代码都差很少,但与某个具体的业务逻辑关系也不太密切,若是在每一个用到的地方都写,代码会很冗余,也难以维护,AOP将这些切面与主体逻辑相分离,代码简单优雅的多。程序员

和注解相似,在大部分的应用编程中,咱们不须要本身实现动态代理,而只须要按照框架和库的文档说明进行使用就能够了。不过,理解动态代理有助于咱们更为深入的理解这些框架和库,也能更好的应用它们,在本身的业务须要时,也能本身实现。github

理解动态代理,咱们首先要了解静态代理,了解了静态代理后,咱们再来看动态代理。动态代理有两种实现方式,一种是Java SDK提供的,另一种是第三方库如cglib提供的,咱们会分别介绍这两种方式,包括其用法和基本实现原理,理解了基本概念和原理后,咱们来看一个简单的应用,实现一个极简的AOP框架。数据库

静态代理

咱们首先来看代理,代理是一个比较通用的词,做为一个软件设计模式,它在《设计模式》一书中被提出,基本概念和平常生活中的概念是相似的,代理背后通常至少有一个实际对象,代理的外部功能和实际对象通常是同样的,用户与代理打交道,不直接接触实际对象,虽然外部功能和实际对象同样,但代理有它存在的价值,好比:编程

  • 节省成本比较高的实际对象的建立开销,按需延迟加载,建立代理时并不真正建立实际对象,而只是保存实际对象的地址,在须要时再加载或建立
  • 执行权限检查,代理检查权限后,再调用实际对象
  • 屏蔽网络差别和复杂性,代理在本地,而实际对象在其余服务器上,调用本地代理时,本地代理请求其余服务器

代理模式的代码结构也比较简单,咱们看个简单的例子,代码以下:swift

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("entering sayHello");
            this.realService.sayHello();
            System.out.println("leaving sayHello");
        }
    }

    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = new TraceProxy(realService);
        proxyService.sayHello();
    }
}
复制代码

代理和实际对象通常有相同的接口,在这个例子中,共同的接口是IService,实际对象是RealService,代理是TraceProxy。TraceProxy内部有一个IService的成员变量,指向实际对象,在构造方法中被初始化,对于方法sayHello的调用,它转发给了实际对象,在调用先后输出了一些跟踪调试信息,程序输出为:设计模式

entering sayHello
hello
leaving sayHello
复制代码

咱们在54节介绍过两种设计模式,适配器和装饰器,它们与代理模式有点相似,它们的背后都有一个别的实际对象,都是经过组合的方式指向该对象,不一样之处在于,适配器是提供了一个不同的新接口,装饰器是对原接口起到了"装饰"做用,多是增长了新接口、修改了原有的行为等,代理通常不改变接口。不过,咱们并不想强调它们的差异,能够将它们看作代理的变体,统一看待。数组

在上面的例子中,咱们想达到的目的是在实际对象的方法调用先后加一些调试语句,为了在不修改原类的状况下达到这个目的,咱们在代码中建立了一个代理类TraceProxy,它的代码是在写程序时固定的,因此称为静态代理。

输出跟踪调试信息是一个通用需求,能够想象,若是每一个类都须要,而又不但愿修改类定义,咱们须要为每一个类建立代理,实现全部接口,这个工做就太繁琐了,若是再有其余的切面需求呢,整个工做可能又要重来一遍。

这时,就须要动态代理了,主要有两种方式实现动态代理,Java SDK和第三方库cglib,咱们先来看Java SDK。

Java SDK动态代理

用法

在静态代理中,代理类是直接定义在代码中的,在动态代理中,代理类是动态生成的,怎么动态生成呢?咱们用动态代理实现前面的例子:

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("entering " + method.getName());
            Object result = method.invoke(realObj, args);
            System.out.println("leaving " + 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();
    }
}
复制代码

代码看起来更为复杂了,这有什么用呢?别着急,咱们慢慢解释。IService和RealService的定义不变,程序的输出也没变,但代理对象proxyService的建立方式变了,它使用java.lang.reflect包中的Proxy类的静态方法newProxyInstance来建立代理对象,这个方法的声明以下:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 复制代码

它有三个参数:

  • loader表示类加载器,下节咱们会单独探讨它,例子使用和IService同样的类加载器
  • interfaces表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类,例子中只有一个IService
  • h的类型为InvocationHandler,它是一个接口,也定义在java.lang.reflect包中,它只定义了一个方法invoke,对代理接口全部方法的调用都会转给该方法

newProxyInstance的返回值类型为Object,能够强制转换为interfaces数组中的某个接口类型,这里咱们强制转换为了IService类型,须要注意的是,它不能强制转换为某个类类型,好比RealService,即便它实际代理的对象类型为RealService。

SimpleInvocationHandler实现了InvocationHandler,它的构造方法接受一个参数realObj表示被代理的对象,invoke方法处理全部的接口调用,它有三个参数:

  • proxy表示代理对象自己,须要注意,它不是被代理的对象,这个参数通常用处不大
  • method表示正在被调用的方法
  • args表示方法的参数

在SimpleInvocationHandler的invoke实现中,咱们调用了method的invoke方法,传递了实际对象realObj做为参数,达到了调用实际对象对应方法的目的,在调用任何方法先后,咱们输出了跟踪调试语句。须要注意的是,不能将proxy做为参数传递给method.invoke,好比:

Object result = method.invoke(proxy, args);
复制代码

上面的语句会出现死循环,由于proxy表示当前代理对象,这么调用又会调用到SimpleInvocationHandler的invoke方法。

基本原理

看了上面的介绍是否是更晕了,不要紧,看下Proxy.newProxyInstance的内部就理解了。上面例子中建立proxyService的代码能够用以下代码代替:

Class<?> proxyCls = Proxy.getProxyClass(IService.class.getClassLoader(),
        new Class<?>[] { IService.class });
Constructor<?> ctor = proxyCls.getConstructor(new Class<?>[] { InvocationHandler.class });
InvocationHandler handler = new SimpleInvocationHandler(realService);
IService proxyService = (IService) ctor.newInstance(handler);
复制代码

分为三步:

  1. 经过Proxy.getProxyClass建立代理类定义,类定义会被缓存
  2. 获取代理类的构造方法,构造方法有一个InvocationHandler类型的参数
  3. 建立InvocationHandler对象,建立代理类对象

Proxy.getProxyClass须要两个参数,一个是ClassLoader,另外一个是接口数组,它会动态生成一个类,类名以$Proxy开头,后跟一个数字,对于上面的例子,动态生成的类定义以下所示,为简化起见,咱们忽略了异常处理的代码:

final class $Proxy0 extends Proxy implements SimpleJDKDynamicProxyDemo.IService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
    }

    public final boolean equals(Object paramObject) {
        return ((Boolean) this.h.invoke(this, m1,
                new Object[] { paramObject })).booleanValue();
    }

    public final void sayHello() {
        this.h.invoke(this, m3, null);
    }

    public final String toString() {
        return (String) this.h.invoke(this, m2, null);
    }

    public final int hashCode() {
        return ((Integer) this.h.invoke(this, m0, null)).intValue();
    }

    static {
        m1 = Class.forName("java.lang.Object").getMethod("equals",
                new Class[] { Class.forName("java.lang.Object") });
        m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService")
                .getMethod("sayHello",new Class[0]);
        m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
    }
}
复制代码

Proxy0的父类是Proxy,它有一个构造方法,接受一个InvocationHandler类型的参数,保存为了实例变量h,h定义在父类Proxy中,它实现了接口IService,对于每一个方法,如sayHello,它调用InvocationHandler的invoke方法,对于Object中的方法,如hashCode, equals和toString,Proxy0一样转发给了InvocationHandler。

能够看出,这个类定义自己与被代理的对象没有关系,与InvocationHandler的具体实现也没有关系,而主要与接口数组有关,给定这个接口数组,它动态建立每一个接口的实现代码,实现就是转发给InvocationHandler,与被代理对象的关系以及对它的调用由InvocationHandler的实现管理。

咱们是怎么知道$Proxy0的定义的呢?对于Oracle的JVM,能够配置java的一个属性获得,好比:

java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true shuo.laoma.dynamic.c86.SimpleJDKDynamicProxyDemo
复制代码

以上命令会把动态生成的代理类Proxy0保存到文件Proxy0.class中,经过一些反编译器工具好比JD-GUI就能够获得源码。

理解了代理类的定义,后面的代码就比较容易理解了,就是获取构造方法,建立代理对象。

动态代理的优势

相比静态代理,动态代理看起来麻烦了不少,它有什么好处呢?使用它,能够编写通用的代理逻辑,用于各类类型的被代理对象,而不须要为每一个被代理的类型都建立一个静态代理类。看个简单的示例:

public class GeneralProxyDemo {
    static interface IServiceA {
        public void sayHello();
    }

    static class ServiceAImpl implements IServiceA {

        @Override
        public void sayHello() {
            System.out.println("hello");
        }
    }

    static interface IServiceB {
        public void fly();
    }

    static class ServiceBImpl implements IServiceB {

        @Override
        public void fly() {
            System.out.println("flying");
        }
    }

    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("entering " + realObj.getClass().getSimpleName() + "::" + method.getName());
            Object result = method.invoke(realObj, args);
            System.out.println("leaving " + realObj.getClass().getSimpleName() + "::" + method.getName());
            return result;
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> T getProxy(Class<T> intf, T realObj) {
        return (T) Proxy.newProxyInstance(intf.getClassLoader(), new Class<?>[] { intf },
                new SimpleInvocationHandler(realObj));
    }

    public static void main(String[] args) throws Exception {
        IServiceA a = new ServiceAImpl();
        IServiceA aProxy = getProxy(IServiceA.class, a);
        aProxy.sayHello();

        IServiceB b = new ServiceBImpl();
        IServiceB bProxy = getProxy(IServiceB.class, b);
        bProxy.fly();
    }
}
复制代码

在这个例子中,有两个接口IServiceA和IServiceB,它们对应的实现类是ServiceAImpl和ServiceBImpl,虽然它们的接口和实现不一样,但利用动态代理,它们能够调用一样的方法getProxy获取代理对象,共享一样的代理逻辑SimpleInvocationHandler,即在每一个方法调用先后输出一条跟踪调试语句。程序输出为:

entering ServiceAImpl::sayHello
hello
leaving ServiceAImpl::sayHello
entering ServiceBImpl::fly
flying
leaving ServiceBImpl::fly
复制代码

cglib动态代理

用法

Java SDK动态代理的局限在于,它只能为接口建立代理,返回的代理对象也只能转换到某个接口类型,若是一个类没有接口,或者但愿代理非接口中定义的方法,那就没有办法了。有一个第三方的类库[cglib(https://github.com/cglib/cglib)能够作到这一点,Spring,Hibernate等都使用该类库。咱们看个简单的例子:

public class SimpleCGLibDemo {
    static class RealService {
        public void sayHello() {
            System.out.println("hello");
        }
    }

    static class SimpleInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("entering " + method.getName());
            Object result = proxy.invokeSuper(object, args);
            System.out.println("leaving " + method.getName());
            return result;
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> T getProxy(Class<T> cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setCallback(new SimpleInterceptor());
        return (T) enhancer.create();
    }

    public static void main(String[] args) throws Exception {
        RealService proxyService = getProxy(RealService.class);
        proxyService.sayHello();
    }
}
复制代码

RealService表示被代理的类,它没有接口。getProxy()为一个类生成代理对象,这个代理对象能够安全的转换为被代理类的类型,它使用了cglib的Enhancer类,Enhancer类的setSuperclass设置被代理的类,setCallback设置被代理类的public非final方法被调用时的处理类,Enhancer支持多种类型,这里使用的类实现了MethodInterceptor接口,它与Java SDK中的InvocationHandler有点相似,方法名称变成了intercept,多了一个MethodProxy类型的参数。

与前面的InvocationHandler不一样,SimpleInterceptor中没有被代理的对象,它经过MethodProxy的invokeSuper方法调用被代理类的方法:

Object result = proxy.invokeSuper(object, args);
复制代码

注意,它不能这样调用被代理类的方法:

Object result = method.invoke(object, args);    
复制代码

object是代理对象,调用这个方法还会调用到SimpleInterceptor的intercept方法,形成死循环。

在main方法中,咱们也没有建立被代理的对象,建立的对象直接就是代理对象。

基本实现原理

cglib的实现机制与Java SDK不一样,它是经过继承实现的,它也是动态建立了一个类,但这个类的父类是被代理的类,代理类重写了父类的全部public非final方法,改成调用Callback中的相关方法,在上例中,调用SimpleInterceptor的intercept方法。

Java SDK代理与cglib代理比较

Java SDK代理面向的是一组接口,它为这些接口动态建立了一个实现类,接口的具体实现逻辑是经过自定义的InvocationHandler实现的,这个实现是自定义的,也就是说,其背后都不必定有真正被代理的对象,也可能多个实际对象,根据状况动态选择。cglib代理面向的是一个具体的类,它动态建立了一个新类,继承了该类,重写了其方法。

从代理的角度看,Java SDK代理的是对象,须要先有一个实际对象,自定义的InvocationHandler引用该对象,而后建立一个代理类和代理对象,客户端访问的是代理对象,代理对象最后再调用实际对象的方法,cglib代理的是类,建立的对象只有一个。

若是目的都是为一个类的方法加强功能,Java SDK要求该类必须有接口,且只能处理接口中的方法,cglib没有这个限制。

动态代理的应用 - AOP

利用cglib动态代理,咱们实现一个极简的AOP框架,演示AOP的基本思路和技术。

用法

咱们添加一个新的注解@Aspect,其定义为:

@Retention(RUNTIME)
@Target(TYPE)
public @interface Aspect {
    Class<?>[] value();
}
复制代码

它用于注解切面类,它有一个参数,能够指定要加强的类,好比:

@Aspect({ServiceA.class,ServiceB.class})
public class ServiceLogAspect 复制代码

ServiceLogAspect就是一个切面,它负责类ServiceA和ServiceB的日志切面,即为这两个类增长日志功能。

再好比:

@Aspect({ServiceB.class})
public class ExceptionAspect 复制代码

ExceptionAspect也是一个切面,它负责类ServiceB的异常切面。

这些切面类与主体类怎么协做呢?咱们约定,切面类能够声明三个方法before/after/exception,在主体类的方法调用前/调用后/出现异常时分别调用这三个方法,这三个方法的声明需符合以下签名:

public static void before(Object object, Method method, Object[] args) public static void after(Object object, Method method, Object[] args, Object result) public static void exception(Object object, Method method, Object[] args, Throwable e) 复制代码

object, method和args与cglib MethodInterceptor中的invoke参数同样,after中的result表示方法执行的结果,exception中的e表示发生的异常类型。

ServiceLogAspect实现了before和after方法,加了一些日志,其代码为:

@Aspect({ ServiceA.class, ServiceB.class })
public class ServiceLogAspect {

    public static void before(Object object, Method method, Object[] args) {
        System.out.println("entering " + method.getDeclaringClass().getSimpleName()
                + "::" + method.getName() + ", args: " + Arrays.toString(args));
    }

    public static void after(Object object, Method method, Object[] args, Object result) {
        System.out.println("leaving " + method.getDeclaringClass().getSimpleName()
                + "::" + method.getName() + ", result: " + result);
    }
}
复制代码

ExceptionAspect只实现exception方法,在异常发生时,输出一些信息,代码为:

@Aspect({ ServiceB.class })
public class ExceptionAspect {
    public static void exception(Object object, Method method, Object[] args, Throwable e) {
        System.err.println("exception when calling: " + method.getName()
        + "," + Arrays.toString(args));
    }
}
复制代码

ServiceLogAspect的目的是在类ServiceA和ServiceB全部方法的执行先后加一些日志,而ExceptionAspect的目的是在类ServiceB的方法执行出现异常时收到通知并输出一些信息。它们都没有修改类ServiceA和ServiceB自己,自己作的事是比较通用的,与ServiceA和ServiceB的具体逻辑关系也不密切,但又想改变ServiceA/ServiceB的行为,这就是AOP的思惟。

只是声明一个切面类是不起做用的,咱们须要与上节介绍的DI容器结合起来,咱们实现一个新的容器CGLibContainer,它有一个方法:

public static <T> T getInstance(Class<T> cls) 复制代码

经过该方法获取ServiceA或ServiceB,它们的行为就会被改变,ServiceA和ServiceB的定义与上节同样,这里重复下:

public class ServiceA {
    @SimpleInject
    ServiceB b;
    
    public void callB(){
        b.action();
    }
}

public class ServiceB {
    public void action(){
        System.out.println("I'm B");
    }
}
复制代码

经过CGLibContainer获取ServiceA,会自动应用ServiceLogAspect,好比:

ServiceA a = CGLibContainer.getInstance(ServiceA.class);
a.callB();
复制代码

输出为:

entering ServiceA::callB, args: []
entering ServiceB::action, args: []
I'm B leaving ServiceB::action, result: null leaving ServiceA::callB, result: null 复制代码

实现原理

这是怎么作到的呢?CGLibContainer在初始化的时候,会分析带有@Aspect注解的类,分析出每一个类的方法在调用前/调用后/出现异常时应该调用哪些方法,在建立该类的对象时,若是有须要被调用的方法,则建立一个动态代理对象,下面咱们具体来看下代码。

为简化起见,咱们基于上节介绍的DI容器的第一个版本,即每次获取对象时都建立一个,不支持单例。

咱们定义一个枚举InterceptPoint,表示切点(调用前/调用后/出现异常):

public static enum InterceptPoint {
    BEFORE, AFTER, EXCEPTION
}
复制代码

在CGLibContainer中定义一个静态变量,表示每一个类的每一个切点的方法列表,定义以下:

static Map<Class<?>, Map<InterceptPoint, List<Method>>> interceptMethodsMap = new HashMap<>();
复制代码

咱们在CGLibContainer的类初始化过程当中初始化该对象,方法是分析每一个带有@Aspect注解的类,这些类通常能够经过扫描全部的类获得,为简化起见,咱们将它们写在代码中,以下所示:

static Class<?>[] aspects = new Class<?>[] { ServiceLogAspect.class, ExceptionAspect.class };
复制代码

分析这些带@Aspect注解的类,并初始化interceptMethodsMap的代码以下所示:

static {
    init();
}

private static void init() {
    for (Class<?> cls : aspects) {
        Aspect aspect = cls.getAnnotation(Aspect.class);
        if (aspect != null) {
            Method before = getMethod(cls, "before", new Class<?>[] {
                Object.class, Method.class, Object[].class });
            Method after = getMethod(cls, "after",
                    new Class<?>[] {
                Object.class, Method.class, Object[].class, Object.class });
            Method exception = getMethod(cls, "exception",
                    new Class<?>[] {
                Object.class, Method.class, Object[].class, Throwable.class });
            Class<?>[] intercepttedArr = aspect.value();
            for (Class<?> interceptted : intercepttedArr) {
                addInterceptMethod(interceptted, InterceptPoint.BEFORE, before);
                addInterceptMethod(interceptted, InterceptPoint.AFTER, after);
                addInterceptMethod(interceptted, InterceptPoint.EXCEPTION, exception);
            }
        }
    }
}
复制代码

对每一个切面,即带有@Aspect注解的类cls,查找其before/after/exception方法,调用方法addInterceptMethod将其加入目标类的切点方法列表中,addInterceptMethod的代码为:

private static void addInterceptMethod(Class<?> cls, InterceptPoint point, Method method) {
    if (method == null) {
        return;
    }
    Map<InterceptPoint, List<Method>> map = interceptMethodsMap.get(cls);
    if (map == null) {
        map = new HashMap<>();
        interceptMethodsMap.put(cls, map);
    }
    List<Method> methods = map.get(point);
    if (methods == null) {
        methods = new ArrayList<>();
        map.put(point, methods);
    }
    methods.add(method);
}
复制代码

准备好了每一个类的每一个切点的方法列表,咱们来看根据类型建立实例的代码:

private static <T> T createInstance(Class<T> cls) throws InstantiationException, IllegalAccessException {
    if (!interceptMethodsMap.containsKey(cls)) {
        return (T) cls.newInstance();
    }
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(cls);
    enhancer.setCallback(new AspectInterceptor());
    return (T) enhancer.create();
}
复制代码

若是类型cls不须要加强,则直接调用cls.newInstance(),不然使用cglib建立动态代理,callback为AspectInterceptor,其代码为:

static class AspectInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        //执行before方法
        List<Method> beforeMethods = getInterceptMethods(
                object.getClass().getSuperclass(), InterceptPoint.BEFORE);
        for (Method m : beforeMethods) {
            m.invoke(null, new Object[] { object, method, args });
        }

        try {
            // 调用原始方法
            Object result = proxy.invokeSuper(object, args);

            // 执行after方法
            List<Method> afterMethods = getInterceptMethods(
                    object.getClass().getSuperclass(), InterceptPoint.AFTER);
            for (Method m : afterMethods) {
                m.invoke(null, new Object[] { object, method, args, result });
            }
            return result;
        } catch (Throwable e) {
            //执行exception方法
            List<Method> exceptionMethods = getInterceptMethods(
                    object.getClass().getSuperclass(), InterceptPoint.EXCEPTION);
            for (Method m : exceptionMethods) {
                m.invoke(null, new Object[] { object, method, args, e });
            }
            throw e;
        }
    }
}
复制代码

这个代码也容易理解,它根据原始类的实际类型查找应该执行的before/after/exception方法列表,在调用原始方法前执行before方法,执行后执行after方法,出现异常时执行exception方法,getInterceptMethods方法的代码为:

static List<Method> getInterceptMethods(Class<?> cls, InterceptPoint point) {
    Map<InterceptPoint, List<Method>> map = interceptMethodsMap.get(cls);
    if (map == null) {
        return Collections.emptyList();
    }
    List<Method> methods = map.get(point);
    if (methods == null) {
        return Collections.emptyList();
    }
    return methods;
}
复制代码

这个代码也容易理解。

CGLibContainer最终的getInstance方法就简单了,它调用createInstance建立实例,代码以下所示:

public static <T> T getInstance(Class<T> cls) {
    try {
        T obj = createInstance(cls);
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            if (f.isAnnotationPresent(SimpleInject.class)) {
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                Class<?> fieldCls = f.getType();
                f.set(obj, getInstance(fieldCls));
            }
        }
        return obj;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
复制代码

完整的代码能够在github上获取,文末有连接。这个AOP的实现是很是粗糙的,主要用于解释动态代理的应用和AOP的一些基本思路和原理。

小结

本节探讨了Java中的代理,从静态代理到两种动态代理,动态代理普遍应用于各类系统程序、框架和库中,用于为应用程序员提供易用的支持、实现AOP、以及其余灵活通用的功能,理解了动态代理,咱们就能更好的利用这些系统程序、框架和库,在须要的时候,也能够本身建立动态代理。

下一节,咱们来进一步理解Java中的类加载过程,探讨如何利用自定义的类加载器实现更为动态强大的功能。

(与其余章节同样,本节全部代码位于 github.com/swiftma/pro…,位于包shuo.laoma.dynamic.c86下)


未完待续,查看最新文章,敬请关注微信公众号“老马说编程”(扫描下方二维码),从入门到高级,深刻浅出,老马和你一块儿探索Java编程及计算机技术的本质。用心原创,保留全部版权。

相关文章
相关标签/搜索