此文章的java环境:1.8.0_131
本人出于学习阶段,如有不正请指正
方法剖析
getInstanceOf(Object bean, Class<?> targetType)
来自API的解释:从给定 bean 中获得表示源对象的指定类型视图的对象。
public static Object getInstanceOf(Object bean, Class<?> targetType) {
return bean;
}
当前方法给出两个参数,直接返回第一个参数,产生了疑问,但是,在注释当中有这样一句话:
This method is provided in Beans 1.0 as a hook to allow the addition of more flexible bean behaviour in the future.
翻译则是,当前方法在1.0版本中的Beans中仅仅提供一个钩子,如果以后有了更灵活了特性了则允许添加。换句话说,这个方法以后如果有新特性了能用到,现在只是作为一个占位符写在这。
instantiate(ClassLoader cls, String beanName)
instantiate(ClassLoader cls, String beanName, BeanContext beanContext)
instantiate(ClassLoader cls, String beanName, BeanContext beanContext, AppletInitializer initializer)
来自API的解释:实例化 bean。
前两个方法最终都是调用的第三个方法,没有的参数则是传入的null,下面来分析一下第三个实例化方法的流程
这三个方法都是实例化bean,在系统给出的注释当中,有这样一句话:
The bean is created based on a name relative to a class-loader.This name should be a dot-separated name such as "a.b.c".
意思是说,当前传入的bean的名称应该用“.”来分割,并且简历好的bean是基于传入的名字和类加载器来确定。
需要注意的是,这个bean的名字可以是类名或者是一个序列化对象的名字,寻找的顺序则是:序列化后的对象名->类名。
而且加载序列化后的对象名的时候会用beanName加上".ser"的后缀的形式去加载资源,在举例中给了这样一句话:
For example, given a beanName of "x.y", Beans.instantiate would first try to read a serialized object from the resource "x/y.ser" and if that failed it would try to load the class "x.y" and create an instance of that class.
也就是说,如果一个beanName是x.y,则会先去找x/y.ser,找不到则会去找x.y这个类
如果说一个类是 java.applet.Applet 的子类,则会进行特殊的初始化:首先,会提供一个默认的 AppletStub 和 AppletContext ,然后会执行这个小程序的"init"方法,但是如果这个bean已经反序列化了,则会跳过这一步。如果当前bean是Applet,那么应该是调用方去调用"start"方法,并且应该在这个小程序被添加到了一个可见的AWT容器之后再调用。
Applets通过 beans.instantiate来启动和内置启动的方式略有不同,前者没办法直接访问属性,需要通过get/set方法来访问,并且建议用JDK appletviewer和BDK BeanBox来进行测试。
下面是源码部分:
public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext, AppletInitializer initializer)
throws IOException, ClassNotFoundException {
InputStream ins;
ObjectInputStream oins = null;
Object result = null;
boolean serialized = false;
IOException serex = null;
// 如果classloder为null,那么会在System的classloder可用的时候进行替代
// 系统类加载器上的调用将首先查找引导类加载器
if (cls == null) {
try {
cls = ClassLoader.getSystemClassLoader();
} catch (SecurityException ex) {
// 出于系统安全,不允许加载classloder
}
}
// 尝试寻找一个序列化对象
final String serName = beanName.replace('.','/').concat(".ser");
//尝试从当前类加载器和System的加载器去读取资源
if (cls == null)
ins = ClassLoader.getSystemResourceAsStream(serName);
else
ins = cls.getResourceAsStream(serName);
if (ins != null) {
try {
//这个地方读取对象的输入流时调用的两个方法中,else分支中调用的ObjectInputStreamWithLoader是Beans的内部类,这个内部类实现了ObjectInputStream接口,其中新增了一个成员变量loader,用于存放当前对象的类加载器
if (cls == null) {
oins = new ObjectInputStream(ins);
} else {
oins = new ObjectInputStreamWithLoader(ins, cls);
}
//此处获取Object的方法将在IO篇的时候给出
result = oins.readObject();
serialized = true;
oins.close();
} catch (IOException ex) {
ins.close();
// 此处产生的异常是在读取对象的时候抛出,不对这个地方的异常做处理,但是后面可能会用到这个异常,所以先进行记录下来
serex = ex;
} catch (ClassNotFoundException ex) {
//此处的异常由readObject抛出,对象获取不成功则直接抛出异常
ins.close();
throw ex;
}
}
//ObjectInputStream有一个名为enableOverride的属性,如果为true,则oins.readObject()过程中会使result为null,也说明没有序列化后的对象,则尝试去初始化这个类
if (result == null) {
Class<?> cl;
try {
cl = ClassFinder.findClass(beanName, cls);//此处会调用ClassLoader去加载类对象,最终调用的是Class.forName,在调用之前会对beanName做初步检查
} catch (ClassNotFoundException ex) {
// 找不到类,在前面如果有一个被记录下来的异常,那么抛出它,否则重新抛出ClassNotFoundException。
if (serex != null) {
throw serex;
}
throw ex;
}
// 如果类没有被public所修饰,那么则会抛出异常
if (!Modifier.isPublic(cl.getModifiers())) {
throw new ClassNotFoundException("" + cl + " : no public access");
}
/*
* 开始实例化对象
*/
try {
result = cl.newInstance();
} catch (Exception ex) {
throw new ClassNotFoundException("" + cl + " : " + ex, ex);
}
}
if (result != null) {
// 如果这个类是applet,则初始化
AppletStub stub = null;
if (result instanceof Applet) {
Applet applet = (Applet) result;
//如果当前参数中,applet的初始化参数不存在,那么就需要当成applet来处理
boolean needDummies = initializer == null;
if (needDummies) {
// 此处寻找资源名称的规则与前面赘述的x.y的查找规则一致
final String resourceName;
if (serialized) {
// 实例化后的Bean
resourceName = beanName.replace('.','/').concat(".ser");
} else {
// 普通的类
resourceName = beanName.replace('.','/').concat(".class");
}
URL objectUrl = null;
URL codeBase = null;
URL docBase = null;
// 开始获取资源
if (cls == null) {
objectUrl = ClassLoader.getSystemResource(resourceName);
} else
objectUrl = cls.getResource(resourceName);
// 如果资源名称是 "a/b/c.class" 那么我们会设置对象URL是 "file://bert/classes/a/b/c.class" ,代码路径是 "file://bert/classes/"
// 上下文路径是 "file://bert/classes/a/b/"
if (objectUrl != null) {
String s = objectUrl.toExternalForm(); // 转换URL
//开始查找资源
if (s.endsWith(resourceName)) {
int ix = s.length() - resourceName.length();
codeBase = new URL(s.substring(0,ix));
docBase = codeBase;
ix = s.lastIndexOf('/');
if (ix >= 0) {
docBase = new URL(s.substring(0,ix+1));
}
}
}
//设置一个默认的 context 和 stub,默认的上下文则是一个匿名类BeansAppletContext和一个匿名类BeansAppletStub
BeansAppletContext context = new BeansAppletContext(applet);
stub = (AppletStub)new BeansAppletStub(applet, context, codeBase, docBase);
applet.setStub(stub);
} else {
//如果不是applet,则直接初始化
initializer.initialize(applet, beanContext);
}
// 如果存在一个BeanContext,那么在可能的情况下将其加入(有关BeanContext会单独赘述)
if (beanContext != null) {
unsafeBeanContextAdd(beanContext, result);
}
//如果当前已经反序列化,那么代表已经初始化了,否则还需要进行初始化操作
if (!serialized) {
// 如果是applet则需要设置初始化大小
applet.setSize(100,100);
applet.init();
}
if (needDummies) {
((BeansAppletStub)stub).active = true;
} else initializer.activate(applet);
} else if (beanContext != null) unsafeBeanContextAdd(beanContext, result);
}
return result;
}
isDesignTime()
来自API的解释:测试是否正处于设计模式。
此处的API主要是用于获取当前在Applet是否处于设计模式,一般的class不会用到这个方法
setDesignTime(boolean isDesignTime)
来自API的解释: 用于设置是否处于设计模式。
注意,如果有安全管理器,则会调用其checkPropertiesAccess方法,并很可能会抛出Security Exception
isGuiAvailable()
来自API的解释:确定 bean 是否可以假定某个 GUI 是可用的。
这通常在windowing环境中返回true,并且通常在服务器环境中或者应用程序作为批处理作业的一部分运行时返回false。
setGuiAvailable(boolean isGuiAvailable)
来自API的解释:用于设置是否正运行在可进行 GUI 交互的环境中,与设置设计模式的方法一样,如果有安全管理器,则会调用其checkPropertiesAccess方法,并很可能会抛出Security Exception
isInstanceOf(Object bean, Class<?> targetType)
来自API的解释:检查是否可以将 bean 视为给定目标类型。
此处直接调用Introspector.isSubclass()方法,用于判断bean的class是否是目标类型的子类
有一点要注意的,上面有两处获取SecurityManager是通过System.getSecurityManager();来获取