I'm not much of a Spring fan (overengineering) but that's how a mature stack looks like. There were plenty alternatives 10-20 years ago. What's a bit more surprising IMO is the prevalence of Spring MVC in 2020 which doesn't mix well with modern JS-centric frontend approaches, just as splitting frontend and backend work between JS and Java is flying in the face of agile job rotation.
We haven't used Spring MVC since 2015, all our services are running Spring Boot because it is the most mature solution on the market to create the webservice layer. Our Spring apps are then containerized and optimized with jib[1]. Our frontend is built on React Hooks + TypeScript, fully SPA. All the work nowadays in our company is focused on data engineering, by that I mean: databases, parsers, managing the data pipelines, orchestration, Python for creating the models and Java for inference in production, our teams consist of data scientists that are using Python and software engineers that are using Java and TypeScript.
I don't understand why people consider Spring to be overengineered. We find it very productive, time to market is huge and we can focus on other parts of development instead of reinventing the wheel. We are also considering Quarkus for some of our services that aren't supposed to run for a long time.
I'm not sure why they separated them. Spring is Spring imo. There's a lot of tooling that is very quickly accessible and "just works". We have 15 microservices and three or four UIs that are all Spring backend with React frontends and it isn't even a little cumbersome. We don't serve the static content from Spring as a webserver though (we use a separate nginx instance).
Nobody forces you to use it, there are a lot of alternatives out there.
But I have to admit that Spring Boot combined with Kotlin is the most productive development environment I have ever worked in. I am not surprised it is so popular among Java devs.
> Nobody forces you to use it, there are a lot of alternatives out there.
That assumes you're starting a greenfield development. If you're joining existing companies with a mature tech stack, you won't be able to unseat their existing choices because there's a lot of risk involved.
If there is no both WORTH to maintain and hard to maintain code in your language of choice, it's because that language of choice was not used to solve the type of problem Java is used for...
In this current post monolith, micro-service world, most java applications remain very maintainable though.
Actually my language of choice has been used mostly as a Java replacement. And while there is no perfectly maintainable language, little else is remotely close to the awfulness of trying to maintain a Spring Boot application.
Spring boot specifically; regular Spring is relatively ok. I'm using it professionally at the moment. It's just impossible to find where anything is coming from or understand how your application is wired up, because everything magically appears based on what's on the classpath - it's like the COME FROM statement joke. Even just changing your dependency versions can suddenly radically change your application's behaviour (e.g. now it's suddenly running a webserver).
I don't understand why is it better to have something wired up by annotations than explicitly writing java code. Is 'new' obsolete? At least tools (including IDE) can look for method calls, annotation strings not so much.
I'm completely baffled by the popularity of Spring.
> I don't understand why is it better to have something wired up by annotations than explicitly writing java code.
The argument is that most of your services will be global-ish singletons, and so the details of which services are used from which other services are ceremony rather than business logic; if you've made a code change that means service A now calls service C instead of service B, you want the diff to be about the actual code change in A rather than the plumbing of where it's getting service C from. Since there are problems with language-level singletons (e.g. testability) you want to use plain objects passed to the constructor, but since 99.9% of the time the FooService is always going to be the FooServiceImpl, you don't want the trouble of explicitly wiring it all up yourself. I don't necessarily think Spring is worth it, but wiring by hand certainly does involve writing a lot of code that's more or less irrelevant to your actual business logic.
> At least tools (including IDE) can look for method calls, annotation strings not so much.
Actually one of the things that makes Spring almost tolerable is that IDEs generally do have integration with it, and so "find references" will work properly even for things that are reflectively instantiated by Spring.
(That only applies as long as your wiring is "static" though. With Spring Boot's conditional bean annotations all that goes out of the window and you have no hope of understanding where anything is coming from or where anything is used).
Annotations make dependency injection simpler. If you have a Controller that has a Service autowired into the constructor then you can test it by passing a mock service into the constructor.
An alternative would be to have a Main method construct all of the objects and pass them into each other, but @Autowired takes care of that for you.
That is true, there's a lot of "magic" that comes with boot. It can be helpful to take a look at what all of that auto-configuration code is actually doing, but reading that stuff is pretty difficult as well.
> It can be helpful to take a look at what all of that auto-configuration code is actually doing
You can't tell by reading it either, because it's all dynamic: all of the autoconfigurations create beans conditionally only if other beans of that type aren't defined, if certain classes are present on the classpath, if particular values are defined in the configuration that's written in magical self-transforming YAML, or all three. The only way I've found is to breakpoint at the point where something is wired in to see what the concrete class is, then breakpoint again in the constructor of that class to find where it's being constructed from, so you have to restart your application and run the (slow) spring boot startup twice for each thing you care about. And even that doesn't always work because spring encourages doing AOP with bytecode-manipulation-based proxies.