上一章节,我们体验了Spring AOP强大的能力的同时,是不是也想弄明白,它是怎么原理是什么呢?如果自己要做一个类似的框架,应该怎么做呢? 带着这样的疑问我们一起来深入学习下。生活中其实也有很多类似的情形,比如名星,一般都会有经纪人,由经纪人负责接洽各种工作,名星只在活动开始时参加即可。这种貌似拉皮条的,一般我们称之谓代理。 当我们说到代理, 你是不是马上想到那些出国旅游, 想翻墙看网页的场景?实际上,在编程世界中,代理默默的在背后给我们提供了强大的支撑,让我们能够可以优雅的战斗。今天,我们就要揭开Spring AOP中代理既神秘又迷人的面纱,为大家展现它的魅力和威力。

代理的主要作用就是在不改变目标对象的情况下,对目标对象的方法进行增强。JAVA中代理通常有两种实现方式,动态代理和CGLIB字节码增强代理,下面我们逐个介绍。

一、动态代理的实现原理

首先,我们要了解一个重要的角色,他就是Java的原生代理-动态代理。
假设我们有一个顾客(Customer)和一个代购(Agent)。顾客有一系列购物需求,但没有时间去购物,所以他把购物需求告诉代购,让代购帮他完成购物。这就是动态代理的实现原理,我们创建了一个代理对象(Agent),这个对象与被代理的对象(Customer)具有相同的行为。
下面我们用Java的动态代理来实现这个例子,把动态代理的魅力通过代码来展示。

public interface Shopping {
    void buyThings();
}

public class Customer implements Shopping {
    @Override
    public void buyThings() {
        System.out.println("The customer is buying things...");
    }
}

public class AgentHandler implements InvocationHandler {
    private Shopping customer;
    public AgentHandler(Shopping customer) {
        this.customer = customer;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("The agent starts to buy things...");
        Object result = method.invoke(customer, args);
        System.out.println("The agent finishes buying things...");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        Shopping customer = new Customer();
        AgentHandler handler = new AgentHandler(customer);
        // 如下是手工调用,一般都是框架自动完成,我们使用时注入原始类型即可。
        Shopping agent = (Shopping)Proxy.newProxyInstance(
                Shopping.class.getClassLoader(),
                new Class[]{Shopping.class},
                handler
        );
        agent.buyThings();
    }
}

在这段代码中,我们创建了一个Customer实例,并通过一个AgentHandler, 为它生成了一个代理的代购Agent。然后调用agent的buyThings方法时,实际上就是调用了handler中的invoke方法,我们在这个方法中实现了AOP, 对Customer购物行为进行了增强:在购物前后打印出开始和结束的标志,并实际执行了购物的行为。
运行结果如下:
Spring系列学习六、深入Spring AOP——揭开代理的神秘面纱-LMLPHP

请注意, Java的动态代理的限制是只能对接口进行代理,不能对具体的类进行代理,也就是说动态代理适用于类有实现接口的情况下,可以看到动态代理本质上是产生了一个类(代购),而这个类依赖于真实的对象(顾客)。

如果我们的目标类没有实现接口,那该怎么办呢? 别担心,Spring AOP帮我们解决了这个问题。

二、CGLIB字节码增强的实现原理

一般来说一个人变成另外一个人,可能就只有科幻电影中的情节了,但是在Spring AOP的世界里,CGLIB可以帮我们做到这一点。CGLIB利用字节码技术,为我们生成新的类,这些类不但继承了原类的所有功能,还增加了一些新的功能,就像原来的类突然变成了超人一样强大。
让我们用代码来看看CGLIB是如何实现这个功能的:

public class Customer2 {
    public void buyThings() {
        System.out.println("The customer is buying things...");
    }
}

public class AgentMethodInterceptor implements MethodInterceptor {
    private Object customer;
    public AgentMethodInterceptor(Object customer) {
        this.customer = customer;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("The agent starts to buy things...");
        Object result = proxy.invoke(customer, args);
        System.out.println("The agent finishes buying things...");
        return result;
    }
}

public class Test {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Customer2.class);
        enhancer.setCallback(new AgentMethodInterceptor(new Customer2()));
        Customer2 customer = (Customer2) enhancer.create();
        customer.buyThings();
    }
}

这段代码基本和之前的动态代理代码相似,首先创建一个Customer2实例,然后为它创建一个代理。区别在于,这次的代理是基于类的代理,而不仅仅是接口的代理,运行结果如下:
Spring系列学习六、深入Spring AOP——揭开代理的神秘面纱-LMLPHP
从Test2的代码中,我们可以看到,Cgilib这种增加,实际上是在运行期创建了目标类的子类对象,而子类对象是个代理对象,包含了目标类,同时加入了增强的内容(Callback),从而实现了代理功能(对目标对象运行时增加)。

三、结语

代理的两种实现方式,通过两个例子,我们已经讲清楚了; 通过这两个例子,其实很容易就能懂得,每种代理的应用场景。比方说,如果你的目标对象实现了接口,动态代理是您的第一选择(当然使用Cglib也没问题的)。反之,如果你的目标对象没有实现接口,CGLIB字节码增强方式是您的不二选择。
这篇文章为您深入洞察了Spring AOP背后的运行原理。掌握一项技术,不仅仅是要知道如何使用,而且要深入了解其背后的原理,才能游刃有余地使用它并发挥出它的最大效用。希望这篇文章能够照亮您攀爬学习Spring的险峻山峰的道路,让我们一起加油!

01-03 21:00