Abstraction vs runtime performance
Nowadays Java provide lots of abstracions to develops way faster than old C at the cost of a bit of performance yes, but those usually don't matter for what you're doing.
Frameworks like Spring, server like Tomcat are product that have been developped for years with equip of developpers that are way more competent than the average developers. Spring beans & transaction management produces quite ugly stacks since it has to create proxy of your class yet it is not slow.
If you use the web filter from the JSR and chain multiple chains of them your stacks will look like this (from top to bottom!) :
at [.apache..].doFilter(ApplicationFilterChain.java:207)
[.apache..].internalDoFilter(ApplicationFilterChain.java:240)
[...your filter..].doFilter(DelegatingFilterProxy.java:262)
[.apache..].doFilter(ApplicationFilterChain.java:207)
[.apache..].internalDoFilter(ApplicationFilterChain.java:240)
[...your 2nd filter..].doFilter(DelegatingFilterProxy.java:262)
...
So if you chain filter for :
- Logging stuff
- Authentication
- Authorization
- Start database transanction
- Some framwork filter stuff
You can have already 20 line of that in your stacks, but most of those filter usually perform very few operations. Even if you get up to 1000 operations (Java more in assembly but still doesn't matter), it's nothing nowadays.
Furthermore if you have a fewer call stacks, that doesn't mean you're getting faster, you can just have a flatter hierarchy of function, which execute the same number of instructions.
Worrying about the length of your call stacks is useless even in C, unless your doing something extremely specific (kernel ?), you're wasting your energy by worrying about that. Reducing the number of parameter of a function by using a struct/class is more usefull yet unecessary optimisation most of the time (maybe if you loop throught that function 1 000 000 000 times ?)
Having a hard time reading a java stack ? 99% of the time you need to read it your stack will look like this :
Caused by : bla bla bla
at bla bla bla
at bla bla
at bla bla bla
...
Caused by : bla bla bla 2
at bla bla bla
at bla bla
at bla bla bla
...
Caused by : bla bla bla 3
at bla bla bla
at bla bla
at bla bla bla
...
Where [...] is about 10-40 lines of stacks. Those are usually useless, find in each caused by you need to dig through the first call of your class and you will find what is wrong most of the times easily. I hardly read more than 10 lines of stacks in 200 hundreds+, when the error is not in the first "caused by", of them to find what is wrong.
I have done only web developments until now, and I have yet to see my code going slow because of tomcat/spring instead of myself.
Aside from that :
You still think that tomcat is slow and prove by benchmarking that you need to earn some time from his layer ? There is the native connector APR,that switch the Java connector implementation to a native one.
You can also tune others parameters, and JVM, this requires knowledge yes, but isn't it the same when you use specific gcc/g++/VS compiler options ?
Abstraction vs development time
One could say easily the following : more abstractions = more classes = slower development time.
This is easy to say and I will try to address that through some points.
Abstraction* have the goal to hide unecessary thing and force you to use it properly without worrying about implementations details, so it means when coding, your code will tend be more stable. It will take more time to think properly how to write your code with those layers of abstraction, but because they're lot less details to worry about : you will write shorter code and more stable. So yes you will write less lines of codes per hour, but in the end you will end with less lines of code, less times to debug.
If there is a trap with abstractions it is not about raw development time, it is about the learning curve to be able to know how to use them properly : each new framework/library increase the learning curve and make turn over more difficult to handle which can end up with more development time because you end up with novices that don't stay long enough.
Because when you don't know how the abstractions is supposed to work, you will pass lot of time to write those 5 lines of codes and debug them at start.
For me the best way to handle that learning curve is :
- Avoid exotic/"cleaver" usage of your tools keep it to standard usage.
- Keep it simple as possible. Don't add unecessary framework and isolate the components that use each of them in differents layers.
- Put a standard way of doing some common stuff through the app. It must be short so people will refer to it more easily. Moreover they will remember when they will be about to do something that it is documented.
Be (or the one with the knowledge) available to answer questions, don't only answer to "how make it work" but why it has to be done like this.
: This is of course considering that the job has been done good enough, not poor/over-engineer abstractions .