Spring, IoC Containers, and Static Code: Design Principles
Here, learn about the inversion of control patterns, static code within an IoC framework, and wiring utility methods in static code inside the IoC framework.
Join the DZone community and get the full member experience.
Join For FreeIn this article, I would like to discuss the inversion of control patterns, static code (can be legacy or newly created) within an IoC framework like spring, and the issue of wiring utility methods in static code inside the IoC framework.
Static Methods
For example, we have an HttpUtils class with inside a bunch of static methods. Should we remove the static keyword and annotate the class with a Spring stereotype, which would immediately turn it into a Spring Singleton?
In Java, the static keyword indicates that a member or method belongs to the type itself rather than an instance of that type. This means that only one instance of this static member is created and that it is shared between all instances of the class. If it is a static method within an IoC framework, this means that the way the method is run is unique for all the usages of this class and this method.
In this case, there are 3 possibilities:
- The method does not modify context data (data being handled by the application). So we can consider that despite its static character, it is thread-safe.
- The method modifies context data, so it is thread-unsafe.
- The method modifies context data, but it is thread-safe because it is "synchronized" (one execution at a time), which can severely penalize performance.
Static methods in Java are provided as is at build time. This is why it is never possible to overload them. As a result, abstract methods cannot be static, and they can never access class or context values without being passed as an argument (cannot access this or super).
So does static code apply to "Utils" classes? Yes, since we are in case 1, there will be no modified data from the context, the input is translated into output, and as a general rule, this type of class can be exported regardless of the context. However, if it is exportable, shouldn't this code be then squarely in a lib available to all projects? Yes, cross-functional teams would be happy to receive regular contributions from teams that have thought of making utility codes available to everyone that doesn't need context.
Does static code apply to anything else? Not if there is a better thing to do.
Scopes Usable on Spring
Scope |
Description |
---|---|
singleton | This scopes the bean definition to a single instance per Spring IoC container (default). |
prototype | This scopes a single bean definition to have any number of object instances. |
request | This scopes a bean definition to an HTTP request. Only valid in the context of a web-aware Spring ApplicationContext. |
session | This scopes a bean definition to an HTTP session. Only valid in the context of a web-aware Spring ApplicationContext. |
global-session | This scopes a bean definition to a global HTTP session. Only valid in the context of a web-aware Spring ApplicationContext. |
Singletons
If a scope is set to Singleton, the SpringIoC container creates exactly one instance of the object defined by that bean definition. This single instance is stored in a cache of these Singleton beans, and all subsequent requests and references for this bean return the object in the cache.
The engine manages any cache, an expiration, and regeneration of the bean if it is taken out of the stock. A cache exit is possible if the bean has no more or no state.
For example, when calling an httpsUtils.callUrl(), the container must decide if it wants to reuse an already instantiated bean or create a new one. How does the container decide? When registering the bean httpUtils in the container (on the framework), it is possible to indicate to this container how we would like it to behave. Also, the first instance can be created at the start of the application or the first call on the bean. That said, bean callers don't have to know about the bean's life cycle; they call httpUtils.callUrl().
This is the optimal way to provide class methods in an application that contains a quantity of classes and a quantity of methods per class.
We, therefore, have a first (global) performance argument to transform all the static methods into Singletons in order to benefit from the SpringIoC cache. Accessing a static method is faster in theory because the JVM doesn't need to look up the function before executing it, but in practice, most JDKs optimize instance method calls. That said, when it comes to performance, the most common cause is poor design. If static code is faster, then we could code the entire application in static code; why even declare classes and methods following the IoC pattern when that pattern and static code can do the same job well that they are constructed differently?
If we take again the example of the HttpUtils class, the principle of using static code is that I can directly access the callUrl() method from the class; we combine the definition of callUrl() and its implementation in the same place. IoC strives to separate this kind of detail, so instead, we define a method outside of it in an interface. This allows, for example, to decline this interface with several implementations. An interface implementation for HttpUtils would become a configuration of HttpUtils. Note that classes using HttpUtils will have no idea of the execution scope of callUrl() since the scopes used in the framework also depend on a configuration.
In summary, IoC and static are generally at odds. IoC promotes change while static, by definition, prevents it. Inject your dependencies according to the framework, and you will see that you can completely dispense with using static features.
Security
One more argument: according to Sonar, all members of a Spring bean should be injected.
Vulnerability: Critical
spring
owasp
Spring @Component, @Controller, @Service, and @Repository classes are singletons by default, meaning only one instance of the class is ever instantiated in the application. Typically such a class might have a few static members, such as a logger, but all non-static members should be managed by Spring. That is, they should have one of these annotations: @Resource, @Inject, @Autowired or @Value.
Having non-injected members in one of these classes could indicate an attempt to manage state. Because they are Singletons, such an attempt is almost guaranteed to eventually expose data from User1's session to User2.
This rule raises an issue when a Singleton @Component
, @Controller
, @Service
, or @Repository
, not annotated with @ConfigurationProperties
, has non-static members that are not annotated with one of:
- org.springframework.beans.factory.annotation.Autowired
- org.springframework.beans.factory.annotation.Value
- javax.annotation.Inject
- javax.annotation.Resource
Non-compliant code:
@Controller
public class HelloWorld {
private String name = null;
@RequestMapping("/greet", method = GET)
public String greet(String greetee) {
if (greete != null) {
this.name = greetee;
}
return "Hello " + this.name; // if greetee is null, you see the previous user's data
}
}
String is a class. Any class that is concerned, including those that contain static methods that would be called within a Spring bean.
Opinions expressed by DZone contributors are their own.
Comments