I've been out of the loop on the AoT side of Java for a while now. How do these systems handle dynamic class-loading / code generation and things like hot patching?

They don't. They explicitly refuse to compile an app that takes advantage of things that can't statically be determined to be available at compile time. There are a few exceptions, but it is best not to rely on them.

Given the reliance of most, if not all, major Java apps on reflection, factory patterns, type parameters/generics, and other dynamisms a la Spring (which had been practiced and preached for the longest time), I'd expect most apps won't benefit from AOT compilation then.

Yeah, it is not going to compile everything. For most Java apps, which were built in a world where startup time was a known disadvantage of the JVM, they will probably be better off with a JITed VM anyway. This is more of a new capability, allowing JVM languages the ability to move into usecases where they have never shined before. Like CLI apps, for example. I would expect most usage of AOT compilation will be for new software, not existing software.

That being said, generics and factory patterns aren't a problem for graal at all. Reflection and dynamic class loading are the biggest offenders.

How about GUI? Does JavaFX rely on dynamic classloading? (I presume so, given its XML-driven approach.) If so, would it be possible to refactor it away? Or perhaps introduce a preprocessing phase of some sort?

Gluon put out substrate which is supposed to do this:

https://github.com/gluonhq/substrate