动态代理与AOP(2)
Class.forName()扩展
Proxy类I
1. Class.forName()扩展
为了给Proxy的getProxyClass方法做铺垫,必须介绍Class的重载forName()方法以及ClassLoader的default访问权限的getCallerClassLoader()方法
java.lang.Class的forName方法
主要是结合类加载器来看待Class的静态方法forName方法如何加载类
(1). 通过指定的类加载器来加载指定字符串的全类名的Class对象
[1]. 源码声明
public static Class<?> forName(String name,booleaninitialize, ClassLoader loader);
[2]. 输入参数
{1}. String name:字符串表示的全类名
{2}. boolean initialize:是否必须对这个Class对象进行初始化
{3}. ClassLoaderloader:指定用来加载通过name表示的类的类加载器
[3]. 返回值类型:Class<?> 返回加载成功的Class对象
(2). 直接通过字符串指定的全类名来获取这个类的Class对象
public static Class<?> forName(StringclassName);
[1]. 和(1)中重载方法的等价性
调用Class.forName(String className)等价于调用Class.forName(className, true, currentLoader);
currentLoader:当前类的定义类加载器( API上翻译成 the defining class loader)
[2]. 何为定义类加载器 (the defining class loader )
仅仅在ClassLoader的loadClass源码中找到了对the definingclass loader的描述:
//…
//前面是loadClass的开始部分
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name,false);
} else {
c =findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// thisis the defining class loader; record the stats
/*
注意看上面的注释,写的是:这就是定义类加载器。
注释中的this这个指代很关键。this在英语中表示承上,that表示启下
这个this出现在findClass被调用之后的注释,所以这个this指的是真正找到指定的类的类加载器
说明哪一个类加载器找到了这个类,那个类就叫做这个定义类加载器
*/
//…
}
}
(3). 对重载方法的forName的举例
[1]. 测试代码1
Classclazz =Class.forName("java.lang.System", true,ClassLoader.getSystemClassLoader());
System.out.println(clazz.getClassLoader());
打印结果:null
【分析】
ClassLoader.getSystemClassLoader()返回的是AppClassLoader。这就是用AppClassLoader来加载java.lang.System这个类。由类加载的双亲委托机制得知,Bootstrap可以加载到这个Java核心类。由于Bootstrap类加载器不是Java类,所以返回null
[2]. 测试代码2
clazz=Class.forName("java.lang.System", true,ClassLoader.getSystemClassLoader().getParent());
System.out.println(clazz.getClassLoader());
打印结果:null
【分析】
ClassLoader.getSystemClassLoader().getParent()返回的是AppClassLoader的父类加载器ExtClassLoader。所以结果同样是Bootstrap类加载器加载到java.lang.System类,因此返回null。
**************
以下代码中的Fu.class和测试代码在同一个包下
[3]. 测试代码3
clazz=Class.forName("Fu", true,ClassLoader.getSystemClassLoader());
System.out.println(clazz.getClassLoader());
打印结果:[email protected]
【分析】
使用AppClassLoader类加载器去加载classPath下面的Fu.class。所以结果一定是AppClassLoader亲自加载到这个Fu.class。所以返回的是AppClassLoader
[4]. 测试代码4
clazz=Class.forName("Fu", true,ClassLoader.getSystemClassLoader().getParent());
System.out.println(clazz.getClassLoader());
使用AppClassLoader的父类加载器ExtClassLoader去加载classPath下面的Fu.class。一定加载不到,所以会抛出如下的异常:
1. Proxy类I
1). java.lang.reflect.Proxy类
(1).Proxy类基础知识
[1]. Proxy类所在的位置
Proxy位于java.lang.reflect反射子包中。
[2]. Proxy类的直接父类
Proxy类的直接父类就是java.lang.Object类
[3]. Proxy类的作用(含义)
与其说Proxy类的含义,倒不如说是Proxy类的作用。因为Proxy中的方法全部是静态的方法。
{1}. Proxy类为创建动态代理类提供了相应的静态方法;
{2}. Proxy类也是通过其自身的静态方法创建的动态代理类的父类
【结论】由Proxy的方法生成的动态代理类都是Proxy的子类。
[3]. Proxy类的核心骨架
public class Proxy implements java.io.Serializable {
//…
private final static String proxyClassNamePrefix= "$Proxy";
//…
private static long nextUniqueNumber= 0;
//…
}
【注意1】字段private final static String proxyClassNamePrefix = "$Proxy";是final static的,因此不能被修改并且被Proxy类所有的类的实例/子类的实例共用是所有动态代理类类名的前缀。也就是所有动态代理类(Proxy的子类) 的类名都以$Proxy开头。
【注意2】
字段proxyClassNamePrefix和nextUniqueNumber共同组成动态代理类的类名
【动态代理类类名的构成源码】getProxyClass片段
long num;
synchronized (nextUniqueNumberLock){
num = nextUniqueNumber++;
}
String proxyName =proxyPkg + proxyClassNamePrefix + num;
【总结】
每一个通过Proxy.getProxyClass()方法获取的动态代理类的类名都是“$Proxy数字”
(2). Proxy类的构造方法
[1]. private构造方法
private Proxy();
【分析】由于Proxy类中的public方法全部是static,所以私有化构造方法目的是为了让这个Proxy类起到工具类的作用。工具类要求私有化构造方法
-----这是前面提到的Proxy类的作用之一
[2]. protected构造方法
protected Proxy(InvocationHandler h);
【分析】由于通过Proxy类动态生成的子类都必须是java.lang.reflect.Proxy类的子类,同时子类在实例化的时候一定是在构造方法直接或者间接调用父类的构造方法之后才能实例化。但是如果仅仅提供了private访问权限的Proxy构造方法的话,是没有办法提供给子类来使用的。所以Proxy提供了一个protected的构造方法来为动态生成的代理类 (也是Proxy的子类)所调用。
-----这是前面提到的Proxy类的第二个作用
2). Proxy类的常用方法I(全部static)
(1). 获取动态代理类的Class对象 -----获取
[1]. 功能描述
给定类加载器和接口的数组的前提下,获取一个动态代理类(实际是Proxy的子类) 的Class对象。
【注意1】
这个动态代理类的Class对象将被指定的类加载器定义 (因为类加载器中的findClass方法根据特定的要求来返回Class对象) 并且实现所有通过第二个参数传递进来的指定的接口。
【注意2】
上面陈述中:“类加载器定义”中“定义”二字来历:通常类加载器的findClass要调用defineClass (define这个单词中文释义就是“定义”的意思)方法将byte[]转化为Class对象。
[2]. 方法原型
public static Class<?>getProxyClass(ClassLoader loader, Class<?>... interfaces);
[3]. 方法输入参数
{1}. Class<?>…intefaces:指定动态生成的代理类要实现哪些接口
{2}. ClassLoader loader
这个类加载器参数负责做两件事情:
{2}1. 使用这个指定的类加载器loader来加载Class<?>… intefaces中指定的所有接口
{2}2. 使用这个指定的类加载器loader来加载要生成的动态代理类
【一句话】loader起到的作用是双加载
loader既加载传入的接口对应的Class对象,又加载返回的动态代理类的Class对象。
[4]. 方法的返回值类型:Class<?>
(2). Proxy.getProxyClass的ClassLoaderloader的具体作用
[1]. 作用1:用来加载传入的接口对应的Class对象
{1}. 【源码体现】getProxyClass方法的片段
//…
for (int i = 0; i < interfaces.length; i++) {
//getProxyClass传入的第二个参数
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName,false,loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
}
{2}【注意】
分析语句:interfaceClass = Class.forName(interfaceName,false, loader);
传入的指定的loader循环地为每一个interfaceName使用Class.forName进行加载。
{3}【加载不成功】
{3}1. 如果指定的loader无法加载指定的接口类型,Class.forName抛出ClassNotFoundException异常。由于使用了catch对异常进行捕获,所以Class.forName抛出的异常(异常是对程序出现的问题的抽象)就被catch{}中的内容解决了。
{3}2. 因此程序会继续向下走,做判断if (interfaceClass != interfaces[i])
注意到每次循环的时候都执行Class<?>interfaceClass = null; 也就是每次做这个判断的时候,如果Class.forName抛出异常,interfaceClass就还保持原有的值,null。
{3}3. interfaces[i]是可变参数数组的某一个元素,参数类型是Class<?>。这个是用户为动态代理要实现的接口传入的接口对应的Class数组参数。现在假设这个循环已经进来了,说明循环的次数interfaces.length一定非0!!一定传入了Class对象的数组。所以interfaces[i]一定不是null。此时对interfaceClass!= interfaces[i]判断结果必然是true,就会抛出新的异常throw new IllegalArgumentException(interfaces[i] + " is not visible from classloader");异常一旦发生,这个动态代理类就没有办法创建!!
【loader的限制】为Proxy.getProxyClass()传入的类加载器一定要确保能够加载到这个动态代理类要实现的所有接口!!!,否则就会抛出IllegalArgumentException异常
{4}. 测试代码
{4}1. 自定义接口AppLoadedInterface.java
【说明】测试代码TestClassLoaderAndProxy.java与自定义接口AppLoadedInterface.java位于ClassLoader工程下的默认包中。
{4}2. 测试代码1
Class<?>proxyClass =Proxy.getProxyClass(ArrayList.class.getClassLoader(), Collection.class, AppLoadedInterface.class);
【分析】
这个位置抛出了java;langlIllegalArgumentException一定是传入的类加载器无法加载其中的某个接口
传入的类加载器是ArrayList.class.getClassLoader()。由于ArrayList是java的核心类库,位于rt.jar中,因此传入的类加载器是Bootstrap类加载器。
后面的传入的接口数组中的成员是Collection.class, AppLoadedInterface.class,其中第一个接口Collection.class也存在于rt.jar中,因此可以被Bootstrap类加载器成功加载。但是第二个接口的Class文件位于MyEclipse临时指定的classPath中,这个类的加载范围属于AppClassLoader的加载范围,因此Bootstrap无法加载这个接口的Class文件,所以抛出源码中给的异常。
{4}3. 测试代码2
Class<?>proxyClass =Proxy.getProxyClass(ClassLoader.getSystemClassLoader().getParent(),Collection.class,AppLoadedInterface.class);
测试结果:仍然抛出异常
【分析】
传入的是AppClassLoader的父类加载器ExtClassLoader,但是AppLoadedInterface.class是并不在ExtClassLoader的加载范围之内,因此仍然抛出异常
{4}4. 测试代码3
Class<?> proxyClass
=Proxy.getProxyClass(TestClassLoderAndProxy.class.getClassLoader(), Collection.class,AppLoadedInterface.class);
测试没有异常
【分析】
原因就是自定义的TestClassLoderAndProxy.class是被AppClassloader进行加载的,所以TestClassLoderAndProxy.class.getClassLoader()返回的是AppClassLoader,这个类加载器一定能够分别委托Bootstrap类加载器和自身分别加载Collection.class和AppLoadedInterface.class这两个字节码,因此测试通过。
{4}5. 测试代码4
Class<?>proxyClass
=Proxy.getProxyClass(Thread.currentThread().getContextClassLoader(),Collection.class, AppLoadedInterface.class);
测试没有异常
当前线程的默认类加载器在Bootstrap初始化Launcher的时候指定为AppClassLoader,因此Thread.currentThread.getContextClassLoader()返回的就是AppClassLoader。因此通过测试的理由同上
[2]. 作用2:用来加载动态生成的代理类的Class对象
{1}. 【源码体现】getProxyClass方法的片段
//…
byte[] proxyClassFile =ProxyGenerator.generateProxyClass(proxyName, interfaces);
try {
proxyClass = defineClass0(loader,proxyName,
proxyClassFile, 0,proxyClassFile.length);
}catch(ClassFormatError e) {
//....
}
//...
【说明】这里的执行的语句如下:
defineClass0(loader,proxyName, proxyClassFile, 0, proxyClassFile.length)是一个本地化方法。但是通过前面对ClassLoader这个类的方法
protected
finalClass<?> defineClass(String name, byte[] b,
int off, int len)的了解可知,defineClassxxx就是把byte[]数组中的数据构建成Class类对象。只不过这个方法被用在ClassLoader实例的loadClass调用的findClass中。
这里可以推断本地化方法defineClass0(loader,proxyName, proxyClassFile, 0, proxyClassFile.length)就是调用对传入的loader调用这个loader的loadClass方法并且将proxyName, proxyClassFile, 0,proxyClassFile.length作为实参传入到defineClass中的各个形参Stringname, byte[] b, int off, int len中。因此得出的结论是,getProxyClass传入的ClassLoader实例同样也用来加载动态生成的代理类Class对象。
【注意】无论是自定义的类加载器(通过继承java.lang.ClassLoader抽象类并重写defineClass()方法)还是默认类加载器都会通过双亲委托机制找到动态代理类的父类java.lang.reflect.Proxy类。因此只要指定的类加载器能够能加载所有指定的接口,就不会抛出异常。很可能要是自定义的类加载器可以加载指定路径的自定义类的字节码文件。
{2}. 测试代码1
{2}1. 自定义类加载器代码
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
}
【说明】仅仅重写了findClass( )方法,没有重写loadClass( )方法,因此加载类的时候仍然采用双亲委托机制,一定可以找到动态生成的代理类的父类java.lang.reflect.Proxy类。
{2}2. 使用自定义类加载器MyClassLoader做动态代理类的类加载器
Class<?>proxyClass =Proxy.getProxyClass(new MyClassLoader(), Collection.class, AppLoadedInterface.class);
System.out.println(proxyClass.getClassLoader());
【打印结果】[email protected]
打印结果说明传入的自定义类加载器MyClassLoader就是动态生成的代理类的类加载器
{3}. 测试代码2
Class<?>proxyClass
=Proxy.getProxyClass(Thread.currentThread().getContextClassLoader(),Collection.class, AppLoadedInterface.class);
System.out.println(proxyClass.getClassLoader());
【打印结果】[email protected]
打印结果说明传入的AppClassLoader就是动态生成的代理类的类加载器
{4}. 测试代码3
Class<?>proxyClass
=Proxy.getProxyClass(ClassLoader.getSystemClassLoader().getParent(),Collection.class, Cloneable.class);
System.out.println(proxyClass.getClassLoader());
【打印结果】[email protected]
打印结果说明传入的ExtClassLoader就是动态生成的代理类的类加载器
3). Proxy.getProxyClass( )的Cache (缓存) 机制
(1). 从现象看getProxtClass()的缓存机制
[1]. 测试代码
import java.lang.reflect.Proxy;
import java.util.Collection;
ClassclazzProxy =Proxy.getProxyClass(Thread.currentThread().getContextClassLoader(),Collection.class);
System.out.println("classProxy Name: "+clazzProxy.getName());
System.out.println("******");
//ClassLoader和接口数组都一致
ClassclazzProxyI=Proxy.getProxyClass(Thread.currentThread().getContextClassLoader(),Collection.class);
System.out.println("classProxyI Name: "+clazzProxyI.getName());
System.out.println("classProxy == clazzProxyI: "+ (clazzProxy == clazzProxyI));
System.out.println("******");
//ClassLoader不一致,但是接口数组一致
ClassclazzProxyII=Proxy.getProxyClass(Thread.currentThread().getContextClassLoader().getParent(),Collection.class);
System.out.println("classProxyII Name: "+clazzProxyII.getName());
System.out.println("classProxy == clazzProxyII: "+ (clazzProxy == clazzProxyI));
System.out.println("******");
//ClassLoader一致,但是接口数组不一致
ClassclazzProxyIII =Proxy.getProxyClass(Thread.currentThread().getContextClassLoader(),Collection.class,Cloneable.class);
System.out.println("classProxyIII Name: "+clazzProxyIII.getName());
System.out.println("classProxy == clazzProxyIII: "+ (clazzProxy == clazzProxyIII));
[2]. 打印结果
【注意】每一次调用Proxy.getProxyClass()方法不一定就创建新的动态代理类
{1}. 前后两次调用了Proxy.getProxyClass( )方法传入相同的类加载器和接口数组,那么getClassProxy返回的就是同一份字节码。【cache机制】
{2}. 如果传入的类加载器或者接口数组有一个不一样,Proxy.getProxyClass( )就会生成新的动态代理类的字节码。
4). 通过getProxyClass( ) + 反射技术生成动态类对象
(1). 获取动态代理类的直接父类 ----验证
[1]. 测试代码
ClassLoaderloader =Thread.currentThread().getContextClassLoader();
Classclazzproxy =Proxy.getProxyClass(loader, Collection.class);
System.out.println(clazzproxy.getSuperclass());
[2]. 打印结果
classjava.lang.reflect.Proxy
【结论】验证了产生的动态代理类的直接父类就是Proxy类
(2). 获取动态代理类的构造方法
[1]. 打印动态代理类的构造方法
public static void printConstructors(Class clazzproxy) {
Constructor[] cons=clazzproxy.getConstructors();
for(Constructor con: cons){
String name =con.getName();
StringBuilder sBuilder =new StringBuilder(name);
sBuilder.append("(");
Class[] conParams=con.getParameterTypes();
for(int i=0; i<conParams.length; i++){
sBuilder.append(conParams[i].getName());
if(i !=conParams.length -1)
sBuilder.append(", ");
}
sBuilder.append(")");
System.out.println(sBuilder.toString());
}
}
[2]. 测试代码
ClassLoaderloader =Thread.currentThread().getContextClassLoader();
Classclazzproxy =Proxy.getProxyClass(loader, Collection.class);
System.out.println(clazzproxy.getName());
System.out.println("The Constructors for Proxy Class...");
printConstructors(clazzproxy);
[3]. 打印结果
【结论】通过Proxy.getProxyClass( )获取的动态代理类只有一个构造方法。
动态代理类唯一构造方法的原型是:
$Proxy数字(java.lang.reflect.InvocationHandler)
(3). 获取动态代理类的普通方法
[1]. 打印动态代理类的普通方法
public static void printMethods(Class clazzproxy) {
Method[] methods =clazzproxy.getMethods();
for(Method method: methods){
String name =method.getName();
StringBuilder sBuilder =new StringBuilder(name);
sBuilder.append("(");
Class[] conParams=method.getParameterTypes();
for(int i=0; i<conParams.length; i++){
sBuilder.append(conParams[i].getName());
if(i !=conParams.length -1)
sBuilder.append(", ");
}
sBuilder.append(")");
System.out.println(sBuilder.toString());
}
}
[2]. 测试代码
ClassLoaderloader =Thread.currentThread().getContextClassLoader();
Classclazzproxy =Proxy.getProxyClass(loader, Collection.class);
System.out.println(clazzproxy.getName());
System.out.println("The Methods for Proxy Class...");
printMethods(clazzproxy);
[3]. 打印结果【getMethods会返回从父类继承的所有public和protected的方法】