As an example, Micronaut[1] also uses annotations a lot, but their implementation is a lot easier to reason about, because there is less indirection with proxy objects and other weird stuff that Spring uses.
Micronaut does not implement nearly as many annotations as Spring though, which basically means less functionality pre-built. I'm not sure that's a bad thing, but it could be.
I think that this is sometimes a hard shift for developers who otherwise have spent their lives with an ability to puzzle out the constructs that they come across.
Way too many developers try to write spring (but also jpa and many other useful, but complex tool) by trial and error, which let’s be honest, not a good tactic even if one can easily inspect the source. (The recently posted microsoft blog post “even if the precondition doesn’t do anything, you still have to call it” comes to mind)
However, there's another dimension here, and while it's not totally unique to Java, it's definitely present in larger magnitude in Java, in my experience. There are two parts:
1. None of these frameworks are 100% consistent. I haven't used Spring{,Boot} in years, but I can tell you that JPA/JDBC are full of little "surprises" and rough edges, like handling nullable database columns. If you are not careful with your annotations, you'll get a `0` value for your `Int` object field instead of the `null` that was in the database. You can then go for quite a while before you figure out that's what happened. Similarly, JacksonXML has all kinds of little gotchas when it comes to date-time types and timezones, primitives and null-ness, etc.
2. Most projects have more than one of these complex frameworks. See above. I listed JacksonXML and JPA/JDBC. Odds are that you have AT LEAST these three frameworks (including Spring) in your Java project, which means you have to study all three and learn all of their intricacies before being confident in the code you write. That's on top of learning how to write half-decent Java, which is hard enough with its type-erased generics, bug-prone null-handling, and very verbose class definition syntax. If it were just one thing, I'd be sympathetic and tell people to just RTFM. But, unless you plan on only writing Java code for the next decade+, I have come to believe that it's probably not worth it.
Also, java is solid as a language. Sure, it is not the most modern one, but it is reasonably productive, has great tooling, is very performant and perhaps most importantly, it is observable in a very fine way.
The GNAT Ada compiler has an option to output a much-simplified code. Not compilable Ada, but very inspectable unrolled, expanded code. Makes for a great teaching tool. Aaaaaaah this generic mechanism does that!
Edit: link https://docs.adacore.com/gnat_ugn-docs/html/gnat_ugn/gnat_ug... look up -gnatG[=nn]... Good stuff.
I think you're missing the point.