To cope with all this, i wrote this little project: https://github.com/MoserMichael/ls-annotations
It's a decompiler that is listing all annotations, so it becomes easier to grep a text file in order to detect the dependencies between annotations.
it is using the asm library https://asm.ow2.io/ to inspect the bytecode files, so as to extract the class signature, along with the reference and declaration of annotations included in a classpath, or class files included within a directory structure. A limitation/feature is, that it is inspecting already compiled bytecode files.
It is not an accident that things like ruby on rails are popular. These are well-tested toolboxes with a solution for almost every conceivable problem. There are exceptions where it is not needed, but for business applications those are not numerous.
Why do you need a dozen of microservices? Why not to use role-based monoliths? Why not to keep your "microservices" as independent modules, pack them as one app and let such app to configure itself with proper set of services and dependencies according to the config or CLI parameters?..
Don't you just look at the top lines?
Spring Boot application with few controllers starts in 2 seconds on my outdated laptop.
Spring is lightweight, compared to old tech.
Not the most lightweight, that's for sure. Simple Java web server which uses socket API to server requests, starts in few milliseconds. That's the bar.
Spring Boot is an opinionated way to configure Spring applications.
> recapitulating all of the mistakes of EJB
Which mistakes is it repeating?
Though that doesn't mean that anything is wrong with JVM as a platofrm.
spring-core can be replaced by, essentially, several hundred lines of LoC: https://izumi.7mind.io/distage/index.html And in fact these lines can do lot more than Spring.
I prefer code without them. They add magic. I dont like magic in my code.
Java is a very primitive language. For the vast majority of its life, it's basically been C + basic classes + garbage collection.
As a result, it's very verbose, which is totally fine for a low-level language. But, when building large, high-level, business apps, it's just a weird fit. I think that's why we see all of these annotation-heavy band-aids on top of Java (Lombok, Spring, JPA, etc)- it's because Java is actually not the right tool for the job, but instead of migrating (or inventing) a better tool, we just sunken-cost-fallacy ourselves to death.
I'm glad someone is working on a light weight replacement to spring. I had some ideas on a light weight DI framework but never got around to it.
Oh gods below this. I was half wondering if I was writing Perl with how much TMTOWTDI was floating around in the cesspool of Lombok and Spring.
I have used Spring for years. Yes, there are some things I don't like about it, for instances Spring Boots overeager auto configuration, but it provides an unparalleled level of flexibility and productivity. I have never encountered a behavior in Spring that I have not been able to read the source and figure out what's going on and then change the behavior to be what I want. Spring is absurdly flexible and you only need to use the parts that you want.
A few years ago, I decided to try an alternative and wrote an app in Vert.x with no Spring. It worked fine, but it was a hell of a lot more work than leveraging the Spring ecosystem. I later rewrote it using Boot, and it works better, is easier to understand, and uses less code.
Have you seen Spring Data JDBC? It's such a good idea that saves so much boilerplate and I'm not aware of anything else like it. It threads the needle between rolling your own SQL and descending into the hell of a full on ORM.
Anyway, the closets I can come to understanding why people hate Spring so much is to consider my own opinion of Rails. I don't like Ruby and I don't like Rails. I hate all of the magic and I don't want to learn it. But, I'm sure, like Spring, it's enormously productive if you do understand what it's doing and how to use it.
From my point of view, Java is an anemic language, and the "cure" appears to be to introduce a bunch of annotation-magic frameworks (Spring + JacksonXML + Hibernate/JPA/JDBC/whatever + Lombok?) that each have their own magic and inconsistencies, to the point that your Java code is more of a configuration file than actual logic (which sounds great), but with the downside that you don't actually know where anything is actually implemented and have little idea about what can fail and where.
As a "polyglot" dev, I just don't have the time or patience to learn all of the magic on top of the language itself.
On the topic of Vert.x, it's definitely a different philosophy than Spring, as you experienced. I'm honestly not sure what domains Vert.x would be superior in, but it seems like it's way overkill for your typical mostly-crud backend app. Vert.x is less of a framework and more like a "build-your-own-framework" toolkit.
I'm open to different opinions on this, but I dislike Hibernate because of the complexity and the pains it causes when trying to do simple things. Hibernate, and Spring's use of it, is a leaky abstraction. When running into bugs, just trying to use a flow like, read sql row to POJO -> update POJO -> Save POJO to DB, using Spring JPA repository interfaces, I find myself needing to know about Hibernate internals, like the persistence context, how it relates with transactions, when objects are merged vs saved vs are they already in the persistence context or not? Plus Hibernate docs suck in my opinion.
One time we hit a bug in Hibernate. This was within the last two years, using a newer version of Spring Boot. We read a row from SQL in a transaction. Later in time, in a whole different Transaction, we read the same row. We read it with an annotation on the query method, "@LockType(PESSIMISTIC_WRITE)". We were using MSSQL and this sends table hints like "(updlock, rowlock, holdlock)". So essentially we wanted exclusive access to this row for the length of the transaction. But the data we were getting in the row didn't make any sense? We could see the sql query with the table hints hitting the sql server, but Hibernate was giving us a cached POJO!? If we "evicted" the pojo before we queried then it worked right. Again, this was at the very beginning of a fresh transaction. Wtf.
[1] https://docs.spring.io/spring-data/jpa/docs/current/referenc...
You can just build your object graph and pass dependencies manually if you want a lightweight approach, no? That's just the way people do it in most languages.
I only miss DI. I miss being able to say "this system depends on these external things" and having a consistent, convenient way of sharing/swapping/testing those components and dependencies.
The solution in other languages? Unstructured globals, deep argument passing, or monkey patching with mocks?!
Yea, I can write simpler code without DI... By ignoring a bunch of stuff.
But then, working in a complex codebase, I introduce a new dependency that is instantiated early in the tree, used two disparate classes rather deep in the tree, suddenly I'm changing 10 different constructors just to get the new dependency where it needs to be.
The tree of constructors is where DI shines as an alternative.
Particularly in the javascript world there seem to be a lot of people struggling to write good, testable code mainly because they make the rookie mistake of not separating their glue code from their business logic. Basically they have bits of code that initialize whatever and they need to put it somewhere and it ends up in the wrong place and before you know it, it becomes impossible to isolate any functionality such that you can actually test it easily without booting up the entire application. Add global variables to the mix and you have basically an untestable mess.
I still use Spring (but very selectively). They've added multiple styles of doing DI over the years, which is confusing. The latest incarnation of that uses neither reflection nor annotations and is very similar to the type of code you'd write manually if you had the time to clean it up and make it nice to use. Another benefit is that it enables native compilation, which with the recent release of spring-native is now a thing people do. Spring is large and confusing but the DI part is actually pretty easy to use. If you've used Koin or Dagger on Android, it's similar to how that works.
What you say about compile-time DI to allow native images makes me feel like we've almost come full circle. I'm still not convinced you need automatic DI at all for smaller services.
* Static references become a tangled mess, and you start wanting some structure around that.
* You have to answer "how does ABC component get access to DEF?" for increasingly difficult combinations of ABC and DEF.
Excepting Spring, pretty much all Java DI containers are lightweight.
With static, using-the-language dependency injection, isn't the question of "how does ABC component get access to DEF?" answerable with the normal IDE/language tooling, rather than some magical library's way of doing it? You can just find the calls to a constructor and look at the arguments.
My experience is based on my bad experience with runtime DI libraries, and is definitely biased against them, but I must be missing something here.
You don’t sit into a car without any knowledge about it and blame it that it is magical.
Of course logging might be static but “true” dependencies like networking classes never are.
In terms of lightweight, I have never needed to use the @Alternative binding [0]. Nearly all of my needs are met by being able to define "this is a singleton, this is a dependency that you should always inject a new instance of, and this is a property."
But it's surprisingly hard to find DI that limits itself like that. The DI in Micronaut and Quarkus are probably the closest to my ideal. Compile time, and only implement a subset of CDI etc.
[0]: https://netbeans.apache.org/kb/docs/javaee/cdi-validate.html
In general it's interesting times for Java. With all of language improvements from Kotlin/Scala, and upcoming Go-like concurrency it really feels like a renaissance for the language.
Java will have CSP at the language level? I find it hard to believe.
So is this the 2nd or 3rd Java renaissance?
The Unbearable Lightness of Java - https://news.ycombinator.com/item?id=20063945 - May 2019 (6 comments)
Jodd – The Unbearable Lightness of Java - https://news.ycombinator.com/item?id=9278704 - March 2015 (108 comments)
Java lightweight framework - jodd - https://news.ycombinator.com/item?id=4084498 - June 2012 (33 comments)
https://docs.oracle.com/en/java/javase/17/docs/api/jdk.https...
It's built on top of Netty but has some additional niceties that make it more practical to use. It's also one of the fastest things out there: https://www.techempower.com/benchmarks/#section=data-r18&hw=...
I'm not on the engineering team so can't speak to the cost/benefit, but it seems to have been a pretty successful transition.
Netty copies the response body when sending to each client, so it's not as lightweight as I've found. For streaming large response bodies, it does not work well. I haven't found a good Java alternative yet (probably will switch to C++ and uWS...)
A bit outdated and not actively maintained, but it's truly small.
If you like async stuff, take a loot at Helidon.
It can use netty, undertow, and others under the hood
Having used not only traditional Java and Spring (including "modern" Spring boot) but also alternatives, like eg DropWizard, I MUCH prefer the alternatives.
DropWizard in particular seems to me a more neutral collection of some of the best tools for each job, and it's both simple and easy.
Spring is just Spring, Spring and more Spring, and while it's "easy", it's not simple- there's a lot of magic.
I'm glad to finally be in a team where people are open minded enough to look outside the Spring bubble. TBH these days, we don't even use Java anymore, we use Kotlin + Arrow which is amazing.
Is there a "Getting Started" guide or a list of examples anywhere? I'm on mobile so may have missed them. All I could see were links to the separate component docs.
1) Is this compatible with GraalVM? I'm mostly asking this out of curiosity.
2) Is it using "modern" Java features? Records, pattern matching, optionals.
https://mvnrepository.com/artifact/org.jodd/jodd-json
https://mvnrepository.com/artifact/com.fasterxml.jackson.cor...
why not: JsonParser().parse<Book>(json)
Not saying that's a good idea, though.