JavaFX源码分析笔记(持续更新中)

本文是自己分析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,虚线是注解。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值