在上边一篇文章中我们介绍了Spring AOP的基本概念,今天我们就来学习一下与AOP实现相关的修饰者模式和Java Proxy相关的原理,为之后源码分析打下基础。
修饰者模式
Java设计模式中的修饰者模式能动态地给目标对象增加额外的职责(Responsibility)。它使用组合(object composition),即将目标对象作为修饰者对象(代理)的成员变量,由修饰者对象决定调用目标对象的时机和调用前后所要增强的行为。
装饰模式包含如下组成部分:
-
Component: 抽象构件,也就是目标对象所实现的接口,有operation函数
-
ConcreteComponent: 具体构件,也就是目标对象的类
-
Decorator: 抽象装饰类,也实现了抽象构件接口,也就是目标类和装饰类都实现了相同的接口
-
ConcreteDecorator: 具体装饰类,其中addBeavior函数就是增强的行为,装饰类可以自己决定addBeavior函数和目标对象函数operation函数的调用时机。
修饰者模式调用的时序图如下图所示。程序首先创建目标对象,然后创建修饰者对象,并将目标对象传入作为其成员变量。当程序调用修饰者对象的operation函数时,修饰者对象会先调用目标对象的operation函数,然后再调用自己的addBehavior函数。这就是类似于AOP的后置增强器,在目标对象的行为之后添加新的行为。
Spring AOP的实现原理和修饰者模式类似。在上一篇文章中说到AOP的动态代理有两种实现方式,分别是JDK Proxy和cglib。
如下图所示,JDK Proxy的类结构和上文中修饰者的类图结构类似,都是代理对象和目标对象都实现相同的接口,代理对象持有目标对象和切面对象,并且决定目标函数和切面增强函数的调用时机。 而cglib的实现略有不同,它没有实现实现相同接口,而是代理对象继承目标对象类。
本文后续就讲解一下JDK Proxy的相关源码分析。
JDK Proxy
JDK提供了Proxy类来实现动态代理的,可通过它的newProxyInstance函数来获得代理对象。JDK还提供了InvocationHandler类,代理对象的函数被调用时,会调用它的invoke函数,程序员可以在其中实现所需的逻辑。
JDK Proxy的基本语法如下所示。先构造一个 InvocationHandler的实现类,然后调用 Proxy的 newProxyInstance函数生成代理对象,传入类加载器,目标对象的接口和自定义的 InvocationHandler实例。
public
class
CustomInvocationHandler implements
InvocationHandler
{
private Object
target;
public CustomInvocationHandler
(Object target
) {
this.target
= target;
}
@Override
public Object
invoke(Object
proxy,
Method method,
Object[]
args)
throws Throwable
{
System.out
.println(
"Before invocation");
Object retVal =
method.invoke
(target,
args);
System.out
.println(
"After invocation");
return retVal;
}
}
CustomInvocationHandler
customInvocationHandler =
new CustomInvocationHandler
(
helloWord
);
//通过Proxy.newProxyInstance生成代理对象
ProxyTest
proxy =
(ProxyTest)
Proxy.
newProxyInstance(
ProxyTest.class
.getClassLoader(),
proxyObj
.getClass().
getInterfaces(), customInvocationHandler
);
生成代理对象
我们首先来看一下 Proxy的 newProxyInstance函数。 newProxyInstance函数的逻辑大致如下:
-
首先根据传入的目标对象接口动态生成代理类
-
然后获取代理类的构造函数实例
-
最后将
InvocationHandler作为参数通过反射调用构造函数实例,生成代理类对象。 具体源码如下所示。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 1 动态生成代理对象的类
Class<?> cl = getProxyClass0(loader, intfs);
// ... 代码省略,下边代码其实是在try catch中的
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 2 获取代理类的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 3调用构造函数,传入InvocationHandler对象
return cons.newInstance(new Object[]{h});
}
getProxyClass0函数的源码如下所示,通过代理类缓存获取代理类信息,如果不存在则会生成代理类。
// 生成代理类
private
static
Class<?> getProxyClass0
(ClassLoader loader
,
Class<?>... interfaces
) {
if (
interfaces.length
> 65535
) {
throw new
IllegalArgumentException("interface limit exceeded"
);
}
// 如果已经有Proxy类的缓存则直接返回,否则要进行创建
return proxyClassCache.
get(loader
, interfaces);
}
生成代理类
JDK Proxy通过 ProxyClassFactory生成代理类。其 apply函数大致逻辑如下:
-
校验接口是否符合规范
-
生成代理类的名称和包名
-
生成代理类字节码
-
根据字节码生成代理类Class
// 生成代理类的工厂类
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 所有代理类名的前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 生成唯一类名的原子Long对象
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
// 通过loader找到接口对应的类信息。
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
// 判断找出来的类确实是一个接口
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
// 判断接口是否重复
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
// 代理类的包路径
String proxyPkg = null;
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// 记录非公开的代理接口,以便于生成的代理类和原来的类在同一个路径下。
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
// 如果没有非公开的Proxy接口,使用com.sun.proxy报名
if (proxyPkg == null) {
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
long num = nextUniqueNumber.getAndIncrement();
// 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,$Proxy1……依次递增
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 生成代理类字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 根据字节码返回相应的Class实例
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
其中关于字节码生成的部分逻辑我们就暂时不深入介绍了,感兴趣的同学可以自行研究。
$Proxy反编译
我们来看一下生成的代理类的反编译代码。代理类实现了 Object的基础函数,比如 toString、 hasCode和 equals,也实现了目标接口中定义的函数,比如说 ProxyTest接口的 test函数。
$Proxy中函数的实现都是直接调用了 InvocationHandler的 invoke函数。
public
final
class $Proxy0
extends Proxy
implements ProxyTest
// 会实现目标接口,但是由于集成了Proxy,所以无法再集成其他类
{
private static
Method m1;
private static
Method m0;
private static
Method m3;
private static
Method m2;
// 构造函数要传入一个InvocationHandler对象
public $Proxy0(InvocationHandler
paramInvocationHandler)
throws
{
super(paramInvocationHandler
);
}
// equal函数
public final
boolean equals(
Object paramObject
)
throws
{
try
{
return ((
Boolean)this.
h.invoke(
this,
m1,
new
Object
[]
{ paramObject
})).booleanValue
();
}
catch (
RuntimeException localRuntimeException
)
{
throw localRuntimeException
;
}
catch (
Throwable localThrowable
)
{
}
throw new
UndeclaredThrowableException
(localThrowable
);
}
public final
int hashCode
()
throws
{
try
{
return ((
Integer)this.
h.invoke(
this,
m0,
null
)).intValue
();
}
catch (
RuntimeException localRuntimeException
)
{
throw localRuntimeException
;
}
catch (
Throwable localThrowable
)
{
}
throw new
UndeclaredThrowableException
(localThrowable
);
}
// test函数,也就是ProxyTest接口中定义的函数
public final
void test(
String paramString
)
throws
{
try
{
// 调用InvocationHandler的invoke函数
this.h
.invoke(this
, m3,
new Object[]
{
paramString });
return;
}
catch (
RuntimeException localRuntimeException
)
{
throw localRuntimeException
;
}
catch (
Throwable localThrowable
)
{
}
throw new
UndeclaredThrowableException
(localThrowable
);
}
public final
String toString()
throws
{
try
{
return (
String)this.
h.invoke(
this,
m2,
null
);
}
catch (
RuntimeException localRuntimeException
)
{
throw localRuntimeException
;
}
catch (
Throwable localThrowable
)
{
}
throw new
UndeclaredThrowableException
(localThrowable
);
}
// 获取各个函数的Method对象
static
{
try
{
m1
= Class
.forName("java.lang.Object"
).getMethod
("equals"
,
new
Class[]
{
Class
.forName
("java.lang.Object"
)
});
m0
= Class
.forName("java.lang.Object"
).getMethod
("hashCode"
,
new
Class[
0]);
m3
= Class
.forName("com.proxy.test2.HelloTest"
).getMethod
("say"
,
new
Class[]
{
Class
.forName
("java.lang.String"
)
});
m2
= Class
.forName("java.lang.Object"
).getMethod
("toString"
,
new
Class[
0]);
return;
}
catch (
NoSuchMethodException localNoSuchMethodException
)
{
throw new
NoSuchMethodError
(localNoSuchMethodException
.getMessage
());
}
catch (
ClassNotFoundException localClassNotFoundException
)
{
}
throw new
NoClassDefFoundError
(localClassNotFoundException
.getMessage
());
}
}
后记
下一篇文章就是AOP的源码分析了,希望大家继续关注。