本文是自己分析javaFX源码的一些笔记。
Application.launch(String… args) 方法
public static void launch(String... args) {
// Figure out the right class to call
StackTraceElement[] cause = Thread.currentThread().getStackTrace();
boolean foundThisMethod = false;
String callingClassName = null;
for (StackTraceElement se : cause) {
// Skip entries until we get to the entry for this class
String className = se.getClassName();
String methodName = se.getMethodName();
if (foundThisMethod) {
callingClassName = className;
break;
} else if (Application.class.getName().equals(className)
&& "launch".equals(methodName)) {
foundThisMethod = true;
}
}
if (callingClassName == null) {
throw new RuntimeException("Error: unable to determine Application class");
}
try {
Class theClass = Class.forName(callingClassName, false,
Thread.currentThread().getContextClassLoader());
if (Application.class.isAssignableFrom(theClass)) {
Class<? extends Application> appClass = theClass;
LauncherImpl.launchApplication(appClass, args);
} else {
throw new RuntimeException("Error: " + theClass
+ " is not a subclass of javafx.application.Application");
}
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
首先是第一句:
StackTraceElement[] cause = Thread.currentThread().getStackTrace();
Thread.currentThread()的意思是获取当前的线程,返回一个Thread对象。
后面的getStackTrace()意思是获取当前和之前调用的方法的名称栈,跟平时我们在try…catch里面打印的异常信息e.printStackTrace()差不多。(具体可以参考博客园-Java StackTraceElement的使用与理解https://www.cnblogs.com/jonzone/p/5501197.html)
然后是这块
boolean foundThisMethod = false;
String callingClassName = null;
for (StackTraceElement se : cause) {
// Skip entries until we get to the entry for this class
String className = se.getClassName();
String methodName = se.getMethodName();
if (foundThisMethod) {
callingClassName = className;
break;
} else if (Application.class.getName().equals(className)
&& "launch".equals(methodName)) {
foundThisMethod = true;
}
}
这部分的意思是获取当前这个launch方法的后一个元素(即调用了launch方法的元素),一般来说是我们的main方法,然后把main方法所在的类名保存到String callingClassName中。
第三块
if (callingClassName == null) {
throw new RuntimeException("Error: unable to determine Application class");
}
这部分意思大概就是防止直接把launch方法作为程序入口,一般是不会发生的。
第四块
try {
Class theClass = Class.forName(callingClassName, false,
Thread.currentThread().getContextClassLoader());
if (Application.class.isAssignableFrom(theClass)) {
Class<? extends Application> appClass = theClass;
LauncherImpl.launchApplication(appClass, args);
} else {
throw new RuntimeException("Error: " + theClass
+ " is not a subclass of javafx.application.Application");
}
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
这句代码Class.forName(callingClassName, false, Thread.currentThread().getContextClassLoader())
参数实际上是这样的:
参数1:String,类名
参数2:boolean,代表是否初始化这个类,即是否加载static字段。不过这个类的main都调用这个launch了,应该已经初始化了
参数3:ClassLoader,类加载器
然后返回一个Class对象。
Application.class.isAssignableFrom(theClass)
是判断Application是否是这个class的父类。所以说,调用launch的类应该继承Application,否则会报错。
最后,把theClass赋值给appClass作为参数传入LauncherImpl.launchApplication(appClass, args)
。
外层的try负责把这个块里面的其他异常转换为RuntimeException抛出。
小结
launch就是起到一个校验的作用,并没有对程序运行起到任何帮助。
LauncherImpl.launchApplication(appClass, args);
public static void launchApplication(final Class<? extends Application> appClass,
final String[] args) {
Class<? extends Preloader> preloaderClass = savedPreloaderClass;
if (preloaderClass == null) {
String preloaderByProperty = AccessController.doPrivileged((PrivilegedAction<String>) () ->
System.getProperty("javafx.preloader"));
if (preloaderByProperty != null) {
try {
preloaderClass = (Class<? extends Preloader>) Class.forName(preloaderByProperty,
false, appClass.getClassLoader());
} catch (Exception e) {
System.err.printf("Could not load preloader class '" + preloaderByProperty +
"', continuing without preloader.");
e.printStackTrace();
}
}
}
launchApplication(appClass, preloaderClass, args);
}
首先是第一句
Class<? extends Preloader> preloaderClass = savedPreloaderClass;
这句话的savedPreloaderClass第一次加载时是个null,所以后面的if条件才会成立。
第二块
if (preloaderClass == null) {
String preloaderByProperty = AccessController.doPrivileged((PrivilegedAction<String>) () ->
System.getProperty("javafx.preloader"));
if (preloaderByProperty != null) {
try {
preloaderClass = (Class<? extends Preloader>) Class.forName(preloaderByProperty,
false, appClass.getClassLoader());
} catch (Exception e) {
System.err.printf("Could not load preloader class '" + preloaderByProperty +
"', continuing without preloader.");
e.printStackTrace();
}
}
}
AccessController.doPrivileged()
可以给予类读取某个文件的权限,需要一个policy配置文件,不重要,不多研究。
System.getProperty("javafx.preloader")
可以获取一个系统属性,根据后文判断,似乎是一个预加载器的类名?我也不知道这是什么东西,后面再看吧。
然后是读取预加载器的Class并赋值给preloaderClass。
下一步方法
launchApplication(appClass, preloaderClass, args);
小结
这个方法用来缓存一个预加载器类的实例,用处未知,后文再看。
launchApplication(appClass, preloaderClass, args)
public static void launchApplication(final Class<? extends Application> appClass,
final Class<? extends Preloader> preloaderClass,
final String[] args) {
if (launchCalled.getAndSet(true)) {
throw new IllegalStateException("Application launch must not be called more than once");
}
if (! Application.class.isAssignableFrom(appClass)) {
throw new IllegalArgumentException("Error: " + appClass.getName()
+ " is not a subclass of javafx.application.Application");
}
if (preloaderClass != null && ! Preloader.class.isAssignableFrom(preloaderClass)) {
throw new IllegalArgumentException("Error: " + preloaderClass.getName()
+ " is not a subclass of javafx.application.Preloader");
}
// System.err.println("launch standalone app: preloader class = "
// + preloaderClass);
// Create a new Launcher thread and then wait for that thread to finish
final CountDownLatch launchLatch = new CountDownLatch(1);
Thread launcherThread = new Thread(() -> {
try {
launchApplication1(appClass, preloaderClass, args);
} catch (RuntimeException rte) {
launchException = rte;
} catch (Exception ex) {
launchException =
new RuntimeException("Application launch exception", ex);
} catch (Error err) {
launchException =
new RuntimeException("Application launch error", err);
} finally {
launchLatch.countDown();
}
});
launcherThread.setName("JavaFX-Launcher");
launcherThread.start();
// Wait for FX launcher thread to finish before returning to user
try {
launchLatch.await();
} catch (InterruptedException ex) {
throw new RuntimeException("Unexpected exception: ", ex);
}
if (launchException != null) {
throw launchException;
}
}
第一部分常规检测,launchCalled是AtomicBoolean(原子boolean), 此外没什么特别的,不讲了。
第二部分创建一个叫JavaFX-Launcher的线程,调用launchApplication1(appClass, preloaderClass, args)
方法。
第三部分等待JavaFX-Launcher线程执行完毕。(countDownLatch可参考简书-CountDownLatch)
主要方法-launchApplication1(appClass, preloaderClass, args)
private static volatile boolean error = false;
private static volatile Throwable pConstructorError = null;
private static volatile Throwable pInitError = null;
private static volatile Throwable pStartError = null;
private static volatile Throwable pStopError = null;
private static volatile Throwable constructorError = null;
private static volatile Throwable initError = null;
private static volatile Throwable startError = null;
private static volatile Throwable stopError = null;
private static void launchApplication1(final Class<? extends Application> appClass,
final Class<? extends Preloader> preloaderClass,
final String[] args) throws Exception {
startToolkit();
if (savedMainCcl != null) {
/*
* The toolkit was already started by the java launcher, and the
* main method of the application class was called. Check to see
* whether the CCL has been changed. If so, then we need
* to pass the context class loader to the FX app thread so that it
* correctly picks up the current setting.
*/
final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl != null && ccl != savedMainCcl) {
PlatformImpl.runLater(() -> {
Thread.currentThread().setContextClassLoader(ccl);
});
}
}
final AtomicBoolean pStartCalled = new AtomicBoolean(false);
final AtomicBoolean startCalled = new AtomicBoolean(false);
final AtomicBoolean exitCalled = new AtomicBoolean(false);
final AtomicBoolean pExitCalled = new AtomicBoolean(false);
final CountDownLatch shutdownLatch = new CountDownLatch(1);
final CountDownLatch pShutdownLatch = new CountDownLatch(1);
final PlatformImpl.FinishListener listener = new PlatformImpl.FinishListener() {
@Override public void idle(boolean implicitExit) {
if (!implicitExit) {
return;
}
// System.err.println("JavaFX Launcher: system is idle");
if (startCalled.get()) {
shutdownLatch.countDown();
} else if (pStartCalled.get()) {
pShutdownLatch.countDown();
}
}
@Override public void exitCalled() {
// System.err.println("JavaFX Launcher: received exit notification");
exitCalled.set(true);
shutdownLatch.countDown();
}
};
PlatformImpl.addListener(listener);
try {
final AtomicReference<Preloader> pldr = new AtomicReference<>();
if (preloaderClass != null) {
// Construct an instance of the preloader on the FX thread, then
// call its init method on this (launcher) thread. Then call
// the start method on the FX thread.
PlatformImpl.runAndWait(() -> {
try {
Constructor<? extends Preloader> c = preloaderClass.getConstructor();
pldr.set(c.newInstance());
// Set startup parameters
ParametersImpl.registerParameters(pldr.get(), new ParametersImpl(args));
} catch (Throwable t) {
System.err.println("Exception in Preloader constructor");
pConstructorError = t;
error = true;
}
});
}
currentPreloader = pldr.get();
// Call init method unless exit called or error detected
if (currentPreloader != null && !error && !exitCalled.get()) {
try {
// Call the application init method (on the Launcher thread)
currentPreloader.init();
} catch (Throwable t) {
System.err.println("Exception in Preloader init method");
pInitError = t;
error = true;
}
}
// Call start method unless exit called or error detected
if (currentPreloader != null && !error && !exitCalled.get()) {
// Call the application start method on FX thread
PlatformImpl.runAndWait(() -> {
try {
pStartCalled.set(true);
// Create primary stage and call preloader start method
final Stage primaryStage = new Stage();
primaryStage.impl_setPrimary(true);
currentPreloader.start(primaryStage);
} catch (Throwable t) {
System.err.println("Exception in Preloader start method");
pStartError = t;
error = true;
}
});
// Notify preloader of progress
if (!error && !exitCalled.get()) {
notifyProgress(currentPreloader, 0.0);
}
}
// Construct an instance of the application on the FX thread, then
// call its init method on this (launcher) thread. Then call
// the start method on the FX thread.
final AtomicReference<Application> app = new AtomicReference<>();
if (!error && !exitCalled.get()) {
if (currentPreloader != null) {
if (simulateSlowProgress) {
for (int i = 0; i < 100; i++) {
notifyProgress(currentPreloader, (double)i / 100.0);
Thread.sleep(10);
}
}
notifyProgress(currentPreloader, 1.0);
notifyStateChange(currentPreloader,
StateChangeNotification.Type.BEFORE_LOAD, null);
}
PlatformImpl.runAndWait(() -> {
try {
Constructor<? extends Application> c = appClass.getConstructor();
app.set(c.newInstance());
// Set startup parameters
ParametersImpl.registerParameters(app.get(), new ParametersImpl(args));
PlatformImpl.setApplicationName(appClass);
} catch (Throwable t) {
System.err.println("Exception in Application constructor");
constructorError = t;
error = true;
}
});
}
final Application theApp = app.get();
// Call init method unless exit called or error detected
if (!error && !exitCalled.get()) {
if (currentPreloader != null) {
notifyStateChange(currentPreloader,
StateChangeNotification.Type.BEFORE_INIT, theApp);
}
try {
// Call the application init method (on the Launcher thread)
theApp.init();
} catch (Throwable t) {
System.err.println("Exception in Application init method");
initError = t;
error = true;
}
}
// Call start method unless exit called or error detected
if (!error && !exitCalled.get()) {
if (currentPreloader != null) {
notifyStateChange(currentPreloader,
StateChangeNotification.Type.BEFORE_START, theApp);
}
// Call the application start method on FX thread
PlatformImpl.runAndWait(() -> {
try {
startCalled.set(true);
// Create primary stage and call application start method
final Stage primaryStage = new Stage();
primaryStage.impl_setPrimary(true);
theApp.start(primaryStage);
} catch (Throwable t) {
System.err.println("Exception in Application start method");
startError = t;
error = true;
}
});
}
if (!error) {
shutdownLatch.await();
// System.err.println("JavaFX Launcher: time to call stop");
}
// Call stop method if start was called
if (startCalled.get()) {
// Call Application stop method on FX thread
PlatformImpl.runAndWait(() -> {
try {
theApp.stop();
} catch (Throwable t) {
System.err.println("Exception in Application stop method");
stopError = t;
error = true;
}
});
}
if (error) {
if (pConstructorError != null) {
throw new RuntimeException("Unable to construct Preloader instance: "
+ appClass, pConstructorError);
} else if (pInitError != null) {
throw new RuntimeException("Exception in Preloader init method",
pInitError);
} else if(pStartError != null) {
throw new RuntimeException("Exception in Preloader start method",
pStartError);
} else if (pStopError != null) {
throw new RuntimeException("Exception in Preloader stop method",
pStopError);
} else if (constructorError != null) {
String msg = "Unable to construct Application instance: " + appClass;
if (!notifyError(msg, constructorError)) {
throw new RuntimeException(msg, constructorError);
}
} else if (initError != null) {
String msg = "Exception in Application init method";
if (!notifyError(msg, initError)) {
throw new RuntimeException(msg, initError);
}
} else if(startError != null) {
String msg = "Exception in Application start method";
if (!notifyError(msg, startError)) {
throw new RuntimeException(msg, startError);
}
} else if (stopError != null) {
String msg = "Exception in Application stop method";
if (!notifyError(msg, stopError)) {
throw new RuntimeException(msg, stopError);
}
}
}
} finally {
PlatformImpl.removeListener(listener);
// Workaround until RT-13281 is implemented
// Don't call exit if we detect an error in javaws mode
// PlatformImpl.tkExit();
final boolean isJavaws = System.getSecurityManager() != null;
if (error && isJavaws) {
System.err.println("Workaround until RT-13281 is implemented: keep toolkit alive");
} else {
PlatformImpl.tkExit();
}
}
}
把这个方法一路看下来,我们可以发现第一部分是定义一大堆变量,第二部分是调用我们定义的方法,第三部分是检查异常。接下来看各个Node子类的方法。
当我们打开BorderPane的继承类界面(ctrl(+shift)+alt+u),会发现它有一大堆的继承关系。因此,我们从Node开始看,以免遗漏什么重要的方法。
*蓝线是extends,绿线是implements,虚线是注解。