Sring AOP的实现机制

动态代理

JDK1.3之后引入了一种称之为动态代理(Dynamic Proxy)的机制。使用该机制, 我们可以为指定的接口在系统运行期间动态地生成代理对象 ,从而帮助我们走出最初使用静态代理实现AOP的窘境。

动态代理机制的实现主要由一个类和一个接口组成,即java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。

下面,让我们看一下,如何使用动态代理来实现之前的“request服务时间控制”功能。虽然要为ISubjectIRequestable两种类型提供代理对象,但因为代理对象中要添加的横切逻辑是一样的,所以,我们只需要实现一个InvocationHandler就可以了,其定义见下方。

 
 
    public class RequestCtrlInvocationHandler implements InvocationHandler {
        private static final Log logger = LogFactory.getLog(RequestCtrlInvocationHandler.class);
 
        private Object target;
 
        // 构造器接受目标对象
        public RequestCtrlInvocationHandler(Object target) {
            this.target = target;
        }
 
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("request")) {
                TimeOfDay startTime = new TimeOfDay(0, 0, 0);
                TimeOfDay endTime = new TimeOfDay(5, 59, 59);
                TimeOfDay currentTime = new TímeOfDay();
                // 根据事件判断是否需要执行
                if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) {
                    logger.warn("service is not available now.");
                    return null;
                }
                return method.invoke(target, args);
            }
            return null;
        }
    }
 
 

然后,我们就可以使用Proxy类,根据RequestCtrlInvocationHandler的逻辑,为ISubjectIRequestable两种类型生成相应的代理对象实例,见下方代码。

 
    public static void main(String[] args) {
    	ISubject subject = (ISubject) Proxy.newProxyInstance(
        ProxyRunner.class.getClassLoader(),
        new Class[]{ISubject.class},
        new RequestCtrlInvocationHandler(new SubjectImpl()));
    	subject.request();
 
      	IRequestable requestable = (IRequestable) Proxy.newProxyInstance(
        ProxyRunner.class.getClassLoader(),
        new Class[]{IRequestable.class},
        new RequestCtrlInvocat ionHandler(new RequestableImpl()));
      	requestable.request();
    }
 
 

即使还有更多的目标对象类型,只要它们依然织入的横切逻辑相同,用RequestCtrlInvocationHandler一个类并通过Proxy为它们生成相应的动态代理实例就可以满足要求。

Proxy动态生成的代理对象.上相应的接口方法被调用时,对应的InvocationHandler就会拦截相应的方法调用,并进行相应处理。InvocationHandler就是我们实现横切逻辑的地方,它是横切逻辑的载体,作用跟Advice是一样的。所以,在使用动态代理机制实现AOP的过程中,我们可以在InvocationHandler的基础上细化程序结构,并根据Advice的类型,分化出对应不同Advice类型的程序结构。我们将在稍后看到SpringAOP中的不同Advice类型实现以及结构规格。

动态代理虽好,但不能满足所有的需求。因为动态代理机制只能对实现了相应Interface的类使用,如果某个类没有实现任何的Interface,就无法使用动态代理机制为其生成相应的动态代理对象。对于没有实现任何Interface的目标对象,我们需要寻找其他方式为其动态的生成代理对象。

默认情况下,如果 SpringAOP 发现目标对象实现了相应 Interface,则采用动态代理机制为其生成代理对象实例。而如果目标对象没有实现任何 Interface,Spring AOP 会尝试使用一个称为 CGLIB(Code Generation Library)的开源的 动态字节码生成类库,为目标对象生成动态的代理对象实例

动态字节码生成

使用 动态字节码生成技术扩展对象行为的原理是 ,我们可以对目标对象进行 继承扩展,为其生成相应的子类,而子类可以通过覆写来扩展父类的行为,只要将横切逻辑的实现放到子类中,然后让系统使用扩展后的目标对象的子类,就可以达到与代理模式相同的效果了。

SubClass instanceof SuperClass == true,不是吗?(图8-5演示了一个使用CGLIB进行对象行为扩展的示例。)

image-20220407213108903

但是,使用继承的方式来扩展对象定义,也不能像静态代理模式那样,为每个不同类型的目标对象都单独创建相应的扩展子类。所以,我们要借助于CGLIB这样的动态字节码生成库, 在系统运行期间动态地为目标对象生成相应的扩展子类 。为了演示CGLIB的使用以及最终可以达到的效果,我们定义的目标类如下所示:

 
 
    public class Requestable {
      public void request() {
        System.out.println("rq in Requestable without implement any interface");
      }
    }
 

CGLIB可以对实现了某种接口的类,或者没有实现任何接口的类进行扩展。但我们已经知道,可以使用 动态代理机制来扩展实现了某种接口的目标类,所以,这里主要演示没有实现任何接口的目标类是如何使用CGLIB来进行扩展的。

要对Requestable类进行扩展,首先需要实现一个net.sf.cglib.proxy.Callback。不过更多的时候,我们会直接使用net.sf.cglib.proxy.MethodInterceptor接口(MethodInterceptor扩展了Callback接口)。

下方代码给出了针对我们的Requestable所提供的Callback实现。

 
 
    public class RequestCtrlCallback implements MethodInterceptor {
        private static final Log logger = LogFactory.getLog(RequestCtrlCallBack.class);
 
        public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            if (method.getName().equals("request"))   {
                TimeOfDay startTime = new TimeOfDay(0, 0, 0);
                TimeOfDay endTime = new TimeOfDay(5, 59, 59);
                TimeOfDay currentTime = new TímeOfDay();
                // 根据事件判断是否需要执行
                if (currentTime.isAfter(startTime) && currentTime.isBefore(endTime)) {
                    logger.warn("service is not available now.");
                    return null;
                }
                return proxy.invokeSupert(object, args);
            }
            return null;
        }
    }
 
 

这样,RequestctrlCallback就实现了对request()方法请求进行访问控制的逻辑。现在我们要通过CGLIB的Enhancer为目标对象动态地生成一个子类,并将RequestCtrlCallback中的横切逻辑附加到该子类中,代码如下所示:

 
 
 
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Requestable.class);
    enhancer.setCallback(new RequestCtrlCallback());
 
    Requestable proxy = (Requestable) enhancer.create();
    proxy.request();
 
 

通过为enhancer指定需要生成的子类对应的父类,以及Callback实现,enhancer最终为我们生成需要的代理对象实例。

使用CGLIB对类进行扩展的唯一限制就是无法对final方法进行覆写。

本章小结

在进入Spring AOP的腹地之前,我们先对Spring AOP的概况进行了介绍。接着,一起探索了Spring AOP的实现机制,包括最原始的代理模式,直至最终的动态代理与动态字节码生成

在了解这些内容之后,我们将深入了解Spring AOP的方方面面,先从Spring AOP的最初版本说起…