What makes it necessary?
The enforced rules of Java compilation make it necessary. Java needs to know what is in the domain of code in order to validate the rules are followed. The domain is the set of jar files in your classpath.
If you implement an interface, the non-abstract implementing class or the non-abstract children of a class implementing an interface need to implement the interface. Override annotations need to be satisfied. References to other methods need to be visible, from both the code perspective and the human perspective. If a method uses a method in a different class or interface, the signature of the method needs to be known by the calling class.
How would I approach it?
I'll use "classifiers" to refer to both classes and interfaces.
You want to start with a set of known validated classifiers and known unvalidated classifiers.
Start with the known unvalidated classifiers are all the classifiers in your target project and known validate classifiers is empty.
Go through each of the unvalidated classifiers and identify any classifiers used that are not already known and add them to the unvalidated classifiers set.
Validate that the current unvalidated classifier is on the classpath. Error if not found.
Move the processed classifier from unvalidated to validated.
Repeat this process until all classifiers have been validated.
Classifiers used includes field types, method return types, method parameter types, variable types in parameters, extended classes, implemented interfaces, annotations, and in some cases Javadoc references. Depending on your project rules, going solely by imports could miss some things. An interface may be in the same package but sourced from a different project.
Why do reflection and Spring-configuration need to be out of scope?
Such actions use text-based handoffs to get code done. Reflection can create a classes using the string "Package.classname" without being identified as a "used class". These concepts would require extra handling beyond the scope of a pure source code analysis or bytecode analysis.
If you want to dive deeper, the Java Virtual Machine specification can give you a deeper understanding of what a class file needs to run. Specifically "4. The class File Format" in the Java 10 spec and "5. Loading, Linking, and Initializing".
https://docs.oracle.com/javase/specs/