Readit News logoReadit News
MichaelMoser123 · 4 years ago
I remember the days, when the Spring framework was advertised as a lightweight alternative to Enterprise java beans (ejb); now Spring outgrew the pretence of being lightweight, don't know when that happened. A year and a half ago, i got back to work with java and spring boot, and i was overwhelmed by the prevalence of annotations in spring boot.

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.

geodel · 4 years ago
Indeed. I have a dozen or so microservices supported by team. Most are SpringBoot a couple of them I wrote myself with plain java and embedded tomcat. Needless to say Springboot stuff is rather complicated for such a simple business functionality. Errors are indecipherable being swamped by thousand line framework exception trace. But being an "enterprise standard" framework all projects must be move to this turd of a framework.
kaba0 · 4 years ago
And in 99% of cases that little microservice will suddenly need thread pooling, logging, some more advanced db management or God help some random messaging service and you are back to re-implementing the myriad features of spring in a shittier way.

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.

pshirshov · 4 years ago
> I have a dozen or so microservices supported by team.

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?..

bedobi · 4 years ago
I'm in an enterprise and have successfully lobbied people to use anything other than Spring. Such organizations and teams while rare do exist so don't give up hope! (or just move to a more progressive org)
heurisko · 4 years ago
> Errors are indecipherable being swamped by thousand line framework exception trace

Don't you just look at the top lines?

MichaelMoser123 · 4 years ago
Another big difficulty is the handling of depedencies, as spring boot is bringing in gprc, jpa, jdbc and countless other libraries. One really needs a dedicated team to figure out all these issues!
vbezhenar · 4 years ago
IBM WebSphere Application Server took several minutes to start or stop. Deploying war file took another 10-30 seconds. And you had to restart application server sometimes.

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.

lmm · 4 years ago
Spring Boot is not the same thing as Spring, and is indeed recapitulating all of the mistakes of EJB (I guess it's been long enough that the new generation of developers doesn't know about the problems). You can still use vanilla Spring though.
heurisko · 4 years ago
> Spring Boot is not the same thing as Spring

Spring Boot is an opinionated way to configure Spring applications.

> recapitulating all of the mistakes of EJB

Which mistakes is it repeating?

TedDoesntTalk · 4 years ago
Spring Boot has remoting like EJB did??
pshirshov · 4 years ago
Well, Spring has multiple fundamental problems. They've chosen wrong language and wrong techniques. Runtime metaprogramming is sick and slow.

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.

pjmlp · 4 years ago
It is always like that, eventually the revolutionaries become the government they set out to replace and the wheel of time turns again.
cies · 4 years ago
Annotations are a band aid. They easily move what otherwise were compile time errors into runtime errors. The advantage of using them is that you have to write less (repetitive) code.

I prefer code without them. They add magic. I dont like magic in my code.

ragnese · 4 years ago
I honestly think the big issue here is using Java for these use-cases. I know that sounds flame-baity, but I'm being sincere.

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.

fnord77 · 4 years ago
the pernicious thing about spring is there appears to be 15 different ways to do the same thing. Everyone's idea and enhancement request was thrown in. Plus things got left in that should have been deprecated after better ideas came along or the java language improved to allow new techniques.

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.

falcolas · 4 years ago
> the pernicious thing about spring is there appears to be 15 different ways to do the same thing.

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.

grishka · 4 years ago
I use http://sparkjava.com in my hobby project. It mostly does what I want, but I had to hack it a bit to be able to stream responses. It's also crazy fast and about as lightweight as these things get.
sitta · 4 years ago
Spring is certainly a divisive topic, and I think it's hard for people on different sides to fully understand each other's experiences.

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.

ragnese · 4 years ago
I think you hit the nail on the head with your reflection on your attitude with respect to Ruby on Rails.

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.

newtwilly · 4 years ago
Spring Data JDBC is, by default I believe, backed by a full on ORM, that being Hibernate.

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.

sitta · 4 years ago
This is not correct. You're thinking Spring Data JPA [1]. Spring Data JDBC [2] does not use any Hibernate nonsense.

[1] https://docs.spring.io/spring-data/jpa/docs/current/referenc...

[2] https://spring.io/projects/spring-data-jdbc

hocuspocus · 4 years ago
I haven't really touched Java in a while but I don't get why you'd want a lightweight DI container.

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.

WatchDog · 4 years ago
I think there are a lot of Java developers, that have just never worked without a DI framework, and just don't have a grasp on just how simple it can be to write code without one.
eximius · 4 years ago
As someone who hated Java, used it for a few years, and now occasionally misses it...

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.

EdwardDiego · 4 years ago
You say that, and I usually agree, I mean, constructor args are the simplest form of DI.

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.

yashap · 4 years ago
That REALLY depends on the size of your codebase. When it’s small, no need for a DI framework. But when it grows large, it becomes quite a pain, and a DI framework is nice, eliminates a bunch of boilerplate with every code change.
jillesvangurp · 4 years ago
A good DI framework just saves you from having to spell out all the glue code; or at least minimize that. DIY dependency injection is indeed a useful skill to have with other languages. Unfortunately, it's not what a lot of people do with other languages because they simply don't know that it would help them.

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.

hocuspocus · 4 years ago
I've used Spring DI. I understand the argument for it, when building bigger applications, though it invariably brings its own complexity too.

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.

stickfigure · 4 years ago
Sure, and eventually you end up rebuilding an [ad hoc, informally-specified, bug-ridden, slow] DI container because:

* 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.

benhoyt · 4 years ago
Why do "static references become a tangled mess"? In my (limited) experience with runtime DI libraries (albeit in Go) they turn clear, IDE- and debugging-friendly code where the compiler tells you at compile time if you got it wrong ... into a hard-to-debug magical soup.

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.

jupp0r · 4 years ago
Dependency injection does not have to be dynamic, it can totally be done at compile time. Boost DI is an example: https://boost-ext.github.io/di/
throwaway1492 · 4 years ago
This, it’s really exhausting to read this never ending wheel reinvention. Sure any one can use simpler non-spring frameworks, and other “non standard” frameworks and libraries for 1/10 or 1/100 of the functionality, and get 10-100x the bugs and much less or zero support. But we need netty! And then when you add thread pools, jdbc, logging, etc? Yep you’ve reimplemented spring. Just use spring, spend the time to learn it and reap the rewards.
throwaway894345 · 4 years ago
Agreed. Stuff like this feels like magic for magic’s sake, and as someone who has had to operate services that use these DI frameworks, they are a big pain.
kaba0 · 4 years ago
How about learning about the tools you use beforehand?

You don’t sit into a car without any knowledge about it and blame it that it is magical.

throwaway1492 · 4 years ago
What does “operate” mean in this context? I’m generally curious why one should consider this comment to be anything other than low effort flame bait?
lmm · 4 years ago
Plumbing the construction of your object graph manually does not have a particularly high cost/benefit - most of your services are singletons that depend on each other, and it's already clear enough which ones depend on which others without repeating yourself. A very basic "here's a bag of classes that depend on each other, wire them all together and then let me pull out the instances by type" is often worthwhile for avoiding all that boilerplate, even if it does break the rules of the language a little. Something like Picocontainer or even Guice is pretty good IME.
dep_b · 4 years ago
Not using Java but generally in OO languages I ended up passing forward dependencies in grouped and themed classes like LoginDependencies, InboxDependencies etcetera. Everything under an Interface so you just mock whatever you need. Never ran into serious issues.

Of course logging might be static but “true” dependencies like networking classes never are.

EdwardDiego · 4 years ago
I want lightweight, and compile time. But I'll take compile time only if need be.

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

waynesonfire · 4 years ago
here is the talk, good stuff, Dead-Simple Dependency Injection

https://www.youtube.com/watch?v=ZasXwtTRkio

eximius · 4 years ago
Replacing DI with Free Monads is not what I'd call simple. It's not even possible in a type safe way in most(?) languages.
ferdowsi · 4 years ago
I really like the Go-like simplicity of these libraries, without the cursed architecture astronomy from the 2000s.

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.

lopatin · 4 years ago
"upcoming Go-like concurrency" can you elaborate on this?

Java will have CSP at the language level? I find it hard to believe.

erik_seaberg · 4 years ago
Project Loom’s virtual threads (without dedicated OS threads and stacks), which will hopefully relieve devs from manually doing a CPS transform of procedural code into chains of futures for thread pool workers to complete.
UncleOxidant · 4 years ago
> it really feels like a renaissance for the language.

So is this the 2nd or 3rd Java renaissance?

vbezhenar · 4 years ago
I'd say that first Java renaissance is Java 5 with generics. Second Java renaissance is Java 8 with lambdas. IMO third Java renaissance will be with re-introduction of green threads.
mooreds · 4 years ago
dang · 4 years ago
Thanks! Macroexpanded:

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)

spuz · 4 years ago
I wonder if someone can recommend a lightweight http server library? I like Javalin but it's based on Jetty which is a fully JavaEE compliant framework and includes support for things like OSGI which I don't need. With the whole Log4j situation, I'm re-evaluating some the libraries I've previously relied on.
AmpsterMan · 4 years ago
If you have Java 11+ I presume you can't get any simpler than a standard library module:

https://docs.oracle.com/en/java/javase/17/docs/api/jdk.https...

nayuki · 4 years ago
This comes from way before SE 11. I was using it in 7. The doc says it comes from 6. https://docs.oracle.com/javase/7/docs/jre/api/net/httpserver...
vips7L · 4 years ago
That server is no where near production ready.
14u2c · 4 years ago
Vert.x

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=...

mooreds · 4 years ago
We looked around since we wanted to move off Tomcat and decided on Netty: https://netty.io/

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.

winrid · 4 years ago
EDIT - it seems maybe I was wrong here

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...)

inyorgroove · 4 years ago
Very Sinatra like: https://sparkjava.com/
xmodem · 4 years ago
also not actively maintained sadly.
vbezhenar · 4 years ago
https://github.com/NanoHttpd/nanohttpd

A bit outdated and not actively maintained, but it's truly small.

If you like async stuff, take a loot at Helidon.

pabl0rg · 4 years ago
If you’re on Kotlin, consider http4k

It can use netty, undertow, and others under the hood

dopidopHN · 4 years ago
OkHTTP or netty.
grishka · 4 years ago
Okhttp is a client, but a good one.
bayesian_horse · 4 years ago
Knock-Knock who's there? ... Long Pause ... Java!
sorokod · 4 years ago
Looks nice and clean. It does seem to be maintained by a single person (at least the JSON subproject [1]) which will be a major turn off for adoption by an "enterprise"

[1] https://github.com/oblac/jodd-json

ulrikrasmussen · 4 years ago
First thoughts: the JSON subproject seems to be very unprincipled. The documentation documents general usage through a few examples, but it doesn't really give you a good idea of the semantics of the library. It appears to scan your objects using reflection for things that it determines to be fields (what are the criteria?), but for some reason does not serialize collection types by default because "This plays well with some 3rd party libraries (like ORM) where collections represent lazy relationships". The library is configured by modifying the state of global objects which is just a disaster waiting to happen.
sorokod · 4 years ago
"just a disaster waiting to happen" - if I was the maintainer, I would appreciate a test that demonstrates the failure scenario.