Spring源码解析之AOP篇(一)----代理模式详解

最近看了Spring中的AOP--面向切面编程,该功能极为强大,能帮我们实现服务调用性能监控,权限控制,日志增强,事务管理等功能,能在不改变原来业务代码的前提下,悄无声息的替我们动态织入,让我们能更加的专注业务的实现。当了解到这,我非常好奇,想一探究竟AOP到底是如何实现帮我们无声织入的。但是个人认为,经过一段时间的源码研读,AOP应该算是在Spring核心功能中最比较难懂得了,实现逻辑较为复杂。如果想学习AOP并了解其原理,那么首先你得对代理模式有所了解,因为说到底,Spring在底层都是通过代理目标对象,实现方法层面上的增强的。接下来,我想先从代理模式开始讲起,算是AOP的开篇吧,通过举例的方式来演示代理模式的三种实现方式,并分别说明其利弊。

一.静态代理

静态代理逻辑很简单,直接上代码,看后便知。

public interface IUserService {

    void sayHello();
}
-------------------------------
public class UserServiceImpl implements IUserService{

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

这里定义了一个IUserService接口,UserServiceImpl实现了该接口,sayHello方法像屏幕输出hello world。如果我想在不改变该方法的逻辑的前提下,怎么无声的进行增强呢?静态代理则是通过另外创建一个代理类,同样实现该接口,然后在需要增强的方法中进行相应的增强即可。

public class StaticProxyFatory implements IUserService {
    //目标对象,可以是该接口下实现类对象
    private IUserService userService;

    public StaticProxyFatory(IUserService userService) {
        //将代理对象和目标对象进行绑定
        this.userService = userService;
    }
    /**
     * 通过代理目标对象,对其进行功能的增强
     */
    @Override
    public void sayHello() {
        System.out.println("begin transaction");
        userService.sayHello();
        System.out.println("close transaction");
    }
}

这里定义了一个StaticProxyFatory类同样也实现了IUserService,其中有一个成员变量,即为目标对象,而该类中的sayHello方法对其进行了增强,假设是事务的增强,开启事务关闭事务的操作。测试一下。

public static void main(String[] args) {
	IUserService userService = new UserServiceImpl();
	StaticProxyFatory staticProxyFatory = new StaticProxyFatory(userService);
	staticProxyFatory.sayHello();
}
---------------------
结果如下:
begin transaction
hello world
close transaction

下面分析下利弊:利呢,自然是通过这种手动创建代理类的形式,让我们的目标类中方法得到了功能的增强。弊呢,也是非常明显。倘若我们需要增强的接口的个数不多,那还好说,有几个接口创建几个代理类即可。可是倘若我现在有这么一个功能,想记录web模块的请求和响应参数,那可不止一两个啊,难道要一个一个去创建吗?这是不可取的。即使你这么做了,假如这些接口在版本的迭代中需要新增方法,那么你创建的对应的代理类也得跟着变动,比较难维护。下面要说的一种代理模式,叫做动态代理,它能够解决这种弊端。

二.JDK动态代理

话不多说,举个例子,接口和目标实现类仍然以上面的IUserService和UserServiceImpl为例,看看JDK是如何实现代理模式的,然后再详细分析实现过程和利弊。

public class DynamicProxyInvocationHandler implements InvocationHandler {
    //目标对象
    private Object targetObject;

    public DynamicProxyInvocationHandler(Object targetObject) {
        this.targetObject = targetObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("begin transaction");
        Object result = method.invoke(targetObject, args);//调用目标方法
        System.out.println("close transaction");
        return result;
    }

    public Object getProxyInstance() {
        //通过目标对象创建代理对象
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }
}

这里创建了一个DynamicProxyInvocationHandler类,其实现了InvocationHandler接口,那么真正实现方法功能增强的地方是invoke方法。下面还有一个getProxyInstance方法,是用来返回当前目标对象的代理对象,实现方式是与当前的InvocationHandler建立绑定,当我们调用代理对象的方法时,实则为调用invoke方法。

public static void main(String[] args) {
	IUserService userService = new UserServiceImpl();
	DynamicProxyInvocationHandler proxy = new DynamicProxyInvocationHandler(userService);
	IUserService proxyInstance = (IUserService)proxy.getProxyInstance();
	proxyInstance.sayHello();

}
---------------------------------------
结果如下:
begin transaction
hello world
close transaction

测试了一下,确实实现了动态织入事务的功能。那么好奇JDK仅仅通过一个Proxy.newInstance就能帮我们构造一个代理对象,它是从何而来,原理又是怎么样的呢?是不是跟静态代理类似,也是创建一个代理类呢?接下来详细讲解下,从Proxy.newInstance入手。

//第一个参数:指定目标对象的类加载器
//第二个参数:该对象的所有实现接口
//第三个参数:目标对象所绑定的handler
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){

	//构建代理对象,此处生成了代理类
    Class<?> cl = getProxyClass0(loader, intfs);
		
	//通过代理对象获取到构造函数,通过构造函数反射生成实例
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    final InvocationHandler ih = h;
    return cons.newInstance(new Object[]{h});
}

此方法的逻辑清晰,做了两件事,第一生成代理类的class对象,第二创建代理对象。

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
		//省略其他代码.......
    /*
     * 此处动态生成class文件
     */
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces, accessFlags);
    
    return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}

走完这段逻辑,最终会生成一个名为$Proxy0的代理类,此类继承了Proxy,且实现了我们传入的所有接口。

package com.sun.proxy;

import com.seaway.curatorframework.aop.IUserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements IUserService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void sayHello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.seaway.curatorframework.aop.IUserService").getMethod("sayHello");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

由于此类在生成时,返回的是byte数组。如果大家想看到像我这样的类,可以在测试时配置一个系统参数,那么会在当前项目的com.sun.proxy目录下生成对应的class文件。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

再来分析下该文件,构造函数中传入了InvocationHandler,且sayHello方法中的逻辑实为父类的InvocationHandler的invoke方法的调用。即Proxy类中的InvocationHandler属性。那么这里也不难说明白下一步的构造函数进行反射生成代理实例了。此处传入的InvocationHandler对象正是我们自己传入的DynamicProxyInvocationHandler对象。当代理对象调用sayHello方法时,实则调用的是invoke方法,其中开启了事务,对目标对象进行了增强。

说到这里,我们可以看出其实静态代理和动态代理的本质是一样的,通过创建代理类来实现对目标方法的增强。区别就是动态代理是通过动态的形式,不拘泥于具体的某一类接口方法。而是在运行时通过类加载器在JVM中动态创建代理类的形式来完成增强的。这种代理模式的缺点也比较明显,必须依托于目标对象有实现接口的前提下,我们可以看到创建代理对象方法的第二个参数就能明白了,那如果我既不想实现接口,又想对目标方法进行增强呢?该怎么办,那么Cglib代理就来了。

三.Cglib代理模式

Cglib代理,是在目标类的基础上动态生成其子类,实现方法的拦截增强,下面以例子代入。

public class UserServiceImpl {

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

创建拦截类,实现增强。

public class CglibProxy implements MethodInterceptor {

    private Object targetObject;

    public CglibProxy(Object targetObject) {
        this.targetObject = targetObject;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("begin transaction");
        Object result = methodProxy.invokeSuper(o, args);//调用目标方法
        System.out.println("close transaction");
        return result;
    }

    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetObject.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
}

测试类,测试结果如下:

public static void main(String[] args) {
    UserServiceImpl userService = new UserServiceImpl();
    CglibProxy cglibProxy = new CglibProxy(userService);
    userService = (UserServiceImpl) cglibProxy.getProxyInstance();
    userService.sayHello();
}
----------------------------------------
结果如下:
begin transaction
hello world
close transaction

可以看到,同样得到了对目标方法的增强。先看下在内存中生成了一个怎样的类文件。

//可以看到这个代理类是继承我们的目标类UserServiceImpl,
并且顺便实现了一个Factory接口,这个接口就是一些设置回调函数和返回实例化对象的方法
public class UserServiceImpl$$EnhancerByCGLIB$$fbca2ec6 extends UserServiceImpl implements Factory{

	//这个方法是我们是我们要调用的,调用代理对象的sayHello方法就会到这个方法中
	public final void sayHello(){
	    //这里就是调用方法拦截器的intercept()方法
	    intercept();
	    return;
	    super.sayHello();
	    return;
	}
}

当调用代理对象的方法时,实则调用的是此处的方法。先执行拦截器中的方法。Cglib的实现逻辑相当的复杂,这里就不再展开细讲了,有兴趣的可以看看这篇文章Cglib动态代理

我们可以看到的是,Cglib是在目标对象上动态生成一个代理子类,然后对目标方法进行拦截。在java中继承有这么一说,如果当前类被定义为final,那么该类不能被继承;如果该类中的方法被定义为final,那么该方法是不能被重写的。所以说,如果你需要通过Cglib实现动态代理,那么不可讲此方法定义为final,不然无法增强。

四.总结

这仅仅只是一个AOP的开篇,大致讲述了代理模式的几种实现方式,为了之后讲解AOP原理做铺垫。因为在AOP中,默认是使用JDK的动态代理,毕竟我们的Service层都是通过实现接口的方式,这也算是一种规范。如果你想强制使用Cglib实现动态代理,可以进行参数配置proxy-target-class=true来实现。今天的介绍就到这了,接下来会继续分析AOP,感兴趣的可以持续关注,非常感谢!

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值