Using Prototype Beans in Spring Web Applications

design-patternsjavaobject-orientedspring

I have been working on few web applications/REST web services recently (Spring IoC/MVC/Data JPA etc) and they usually follow the same pattern: Controller classes –> Service classes (which have number of "utility"/business logic classes autowired) –> Spring Data Repositories.

Pretty much all of them classes above are Spring singletons, which sometimes I feel like it makes the code and some functions within a class dirtier (for example because I cant have a state in a class, I need to pass quite a lot parameters between methods, and I dont really like having more than 1-2 parameters, although I know sometimes its necessary).

I was wondering how is this problem overcome in the big (e.g. enterprise) kind of application.

  • Is it a common practice to use non-Spring managed classes in the Spring application? If so, how do you pass dependencies into it (the ones that would normally be autowired)? If I use constructor injection for example, then I need to autowire all necessary dependencies into the class that creates the object and I wanted to avoid that. Also I dont really want to be messing with load time weaving etc. to autowire beans into non-Spring objects
  • Is using prototype scoped beans a good solution? The only thing is that I need to use AOP's scoped proxies (or method injection etc) to make sure that I get a new instance for any bean thats autowired into singleton in the first place. Is that a "clean" and reliable option – i.e. is it certain that there will be no concurrency type of issues and can I still autowire any singletons into those classes with no issues?

If anyone that worked on a large system, that actually managed to keep the structure not "bloated" and clean have any recommendations? Maybe there are some patterns I am not aware and could use?

Any help appreciated.

Best Answer

I have been working on Spring projects of various size, fromtiny microservices (sometimes less than 100 lines of production code) till big monoliths (with 20+ full time developers working on single codebase).

First of all, my experience is that monoliths tend to be often over-complicated and full of accumulated accidental complexity. Therefore if you feel that your team is experiencing inefficiency caused by bloated codebase, you should consider splitting it into more autonomous and separately manageable components. It doesn't matter if you are using Spring.

Secondly, maybe you encountered Functional Programming movement that is taking over mainly alpha geek community. One of the fundamental ideas of Functional Programming is strict separation of code and data. Actually functional programmers believe strongly that it is terrible idea to combine data (state) with code into objects as it is done often in object oriented programming.

I personally also believe that encapsulating state into bunch of objects and defining logic on these objects to mutate this state will create huge mess in monolithic applications. No matter how great developers you have on the project, they will create codebase that is hard to maintain, as state mutations issues are often very tricky to reproduce and discover.

In that regard, Spring default singleton scope actually makes codebase much easier, because you should always strive for not defining any state on singleton beans. Think about them as a code only, where all of them are strictly stateless.

State in your application represents scalability problem, because if you have stateful application you can't have various instances of it. So state should always be handled carefully in caches or storages dedicated to storing state (e.g. distributed caches or data grids or databases).

Now to the first bullet point, do not try to inject non-Spring beans into Spring singleton beans. I work with Spring 5+ years and I can remember only one case where I used Prototype scope. Just create your DTOs via constructor and do not mix them with Spring context. It is wise idea to keep these DTOs as dumm as possible. That means use create only getters/setters/toString and hashcode/equals for them if needed. I highly recommend using Lombok project for generating these boilerplate methods, so that your DTOs are very readable.

To the point about a lot of parameters passing around. Just group these parameters into higher level DTO. E.g. if you are passing around parameters: customerName, customerId, customerCreditCardNumber, just create DTO Customer and pass that around. Easy enough. If don't want to pass around anything, just use Request scope to bind state to the current request. All the beans can autowire this Request scope bean and mutate its state. But this should be very rare and exceptional case as it may hide tricky behavior often. It's equivalent of global variable for request. Bad idea generally.

To the second bullet point. I believe it was covered already with my other points.

I know that these approaches are significantly deviating from what we've learnt on the college when OOP was the preferred approach. But industry slowly starting to realize that it wasn't right approach for writing complex web applications. I highly recommend looking into functional programming principles as they seem to be the future.