0
点赞
收藏
分享

微信扫一扫

第8章 Spring AOP概述及其实现机制(二)

君心浅语 2022-04-07 阅读 55
springjava

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最终为我们生成需要的代理对象实例。

本章小结

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

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

举报

相关推荐

0 条评论