Jim H
Jim H
  • Home
  • Resume
  • Code Review-BestPractices
  • Java Best Practices
  • Kotlin Best Practices
  • Homophones in English
  • Personal
  • Blog

Spring & Spring Boot

Back to Contents


Spring is a comprehensive framework for building Java applications. Spring Boot is an extension of Spring that further automates configuration of Spring's features, simplifies dependency declarations in Maven or Gradle, and adds some metrics and information points.


  • Use Spring Boot for new projects if at all practical.  It has lots of built-in magic.  (Start with the maven archetypes under org.springframework.boot; or try Spring Initializr https://start.spring.io/) 
  • If you are not using Spring Boot, try to upgrade Spring to at least version 4.3.  Spring 5 is better. 
    • Spring Boot 2 is based on Spring 5.
    • Spring Boot 3 is available now (2023) and is based on Spring 6; it requires JDK 17 or later.
  • If you are using a recent Spring version (4.3 or later), almost everything can be done with annotations, and leaving XML-based configuration behind. And good riddance!
  • When adding a new @Component/@Service/@Repository, make sure its package is included in the configuration’s @ComponentScan. (The default for @ComponentScan is to scan the entire hierarchy.)
  • Don’t want @ComponentScan? Use @SpringBootApplication on your main class, which implies @ComponentScan, as well as @Configuration and @EnableAutoConfiguration, with their default values, and allows explicit overrides.
  • Do not put the @Autowired annotation on a field.  Instead, put it, and if needed @Qualifier, on the constructor for the class (best), or the setter for the field (especially if you want to manually inject a bean).  Use All Caution if you choose the setter—like any other public method, other classes are free to use it. Why not on a field? Because it can hide a dependency:
    • The class API does not mention its private members. Yes, as the developer, you can see the private members, but a consumer of your class would not notice the dependency.
    • There is no way to unit test the dependency without reflection, or at least a Spring container. (Do you really want a Spring container in a unit test? Spoiler alert: No, you do not.)
    • A super class will hide private members from its derived classes. So to coin a phrase, this is a "super-hidden" dependency.
  • The very best reason to inject components with the constructor is, the fields can be declared final. Immutability can be a very Good Thing™, as now you won't accidentally modify the field (and lose the bean). 
  • If the class has multiple constructors, exactly one of them should be annotated with @Autowired. If you use a class’s only constructor to inject components, @Autowired is optional. 
  • Some teams like the Lombok library.  This prescribes where to Autowire, and writes a lot of repetitive code (accessors, constructors, builders, etc.). However, there are some issues, so I do Not advise Lombok for new projects. (January 2025: those articles are more than 5 years old.  Your mileage may vary.)
  • On the other hand, Lombok includes the annotation @AllArgsConstructor. Place that before the class declaration, and all private fields will be auto wired through the implicit constructor that Lombok generates. For quick and dirty projects, Lombok definitely saves enough time to be worth it. For real (production grade) projects, the time savings might be negligible.
  • Setup and teardown:
    • The @PostConstruct annotation can be added to any one bean method with the signature public void methodName()—with or without checked exceptions. This method can then do any desired setup, knowing that all @Values have been set, and all @Autowired references are initialized.  (Spring defines an interface called InitializingBean, with one method, void afterPropertiesSet(), that accomplishes the same thing.)
    • The @PreDestroy annotation can be added to any one bean method (with the same signature as @PostConstruct), to be called before the bean is destroyed, while auto-wires are still in effect.  This can be used to cleanly dispose of resources the bean acquired or created manually. (There is no analog of InitializingBean to use as an alternative to @PreDestroy.)
    • @ConfigurationProperties is a good way to get related properties into a single object (or nested objects). See the documentation here. There is a corresponding annotation, @EnableConfigurationProperties, to enable this feature.
  • A bean’s name will depend on its class name by default; but the convention is that the bean name looks like a variable name. So a bean class FooBar will result in a bean name “fooBar”. The @Name annotation will set the bean’s name explicitly.
  • The @DependsOn annotation may be used on a bean, to let Spring know that the bean depends upon other beans, which should therefore be initialized first.
  • Also, take a look at code snippets in the Appendix.

Serialization & Deserialization

Back to Contents


Sharing an object between processes, or between invocations of a process, or storing an object in a file or database, requires saving enough information to reconstruct the object later. The process of saving the object, writing each field (and each field of a member object) in such a form as to be savable, is called serialization. The reverse process is called deserialization.


Java has a "native" serialization scheme, using the marker interface Serializable. While still a valid choice for serialization, it has largely been replaced by JSON and XML for serialization, as those formats are widely used, both within and outside of the Java community.

General (two-way) tips

The de facto standard for serialization is the Jackson library (com.fasterxml.jackson.*). Jackson can handle either JSON or XML; it does most of what you need without any customization; and any customizations you need are fairly easy to apply. Some of these are described below, in the  Serialization and Deserialization sections.

  • Most applications only need a single object mapper.  I like to declare the object mapper as a bean in a @Configuration class; one could declare it as a static final field in a utility class, that is initialized lazily, and access it through a static method.  Declaring the utility class as a @Component is just as good. I am “officially agnostic” as to which way is best. Or, just use the ObjectMapper bean provided by Spring Boot (as long as you don’t need to define your own ObjectMapper bean.)
  • If you need to add customizations on the object mapper itself, you can define your own, and add the customizations at (or just after) construction time. Or, you can add some customizations on the default ObjectMapper with properties in spring.jackson.*.
  • Customizations can include registration of serializers and deserializers; or of properties of the object mapper. A very common customization is for serialization and deserialization of dates, and a description is here.

Deserialization tips

  • Use @JsonIgnoreProperties(ignoreUnknown = true) on a class if you don’t own the object, and it is subject to change.  This way, the code will continue to work if the owner adds a new field.  (Or, leave the annotation off if you want to be forced to compile/deploy whenever the object changes.  Either way, know what you’re doing.)
  • Custom deserializers are pretty easy to use.  You can deserialize a Foo by annotating class Foo with @JsonDeserialize(using FooDeserializer.class), and defining class FooDeserializer.  Examples are here.
  • You can add a custom deserializer for a class you don’t own.  See the same document as for the previous tip for how to register a deserializer.


Immutable Objects

Since Jackson (and other) deserializers use setters to deserialize, you can’t deserialize to an immutable object, right? Wrong.  Use @JsonCreator.

public class Immutable {

  private final String foo;

  private final String bar;


  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)

  public Immutable(@JsonProperty(“foo”) String foo,

                                          @JsonProperty(“bar”) String bar) {

    this.foo = foo;

    this.bar = bar;

  } 

}

There are other modes besides PROPERTIES; DEFAULT means use setters, and DELEGATING is useful for use with constructors that don’t specify all properties. There is also a paradigm for using builders.  See here for some examples.

Serialization tips

  • The @Transient annotation is used to mark a field as “not part of the state” of the object, and therefore not saved when the object is serialized.  (Therefore it would be left in its initial state–0 or null, or whatever is in the constructor or declaration–when the object is deserialized.)
  • The @JsonIgnore annotation is similar, but not identical.  It can be placed on a field, to skip that field on (de)serialization; or, it can be placed on the setter only, to ignore on deserialization, or the getter only, to just ignore on serialization.
  • The @JsonSerialize annotation is the counterpart of @JsonDeserialize. Or, a serializer could be registered with the object mapper. Here is the counterpart to the article linked above on custom deserialization.

Remote Debugging

Back to Contents


IntelliJ Idea and Eclipse both support remote debugging.  In IntelliJ, create a “Remote” configuration: Best to use “Attach to remote JVM”.  Just fill in “Host” and “Port” on the form.  IntelliJ will create a command-line option to use when launching Java on the remote.  Make sure you choose the correct JDK version on the popup menu.  That will insure that you can actually debug remotely.  (There was a change starting with Java 9; See this link for details: Java Application Remote Debugging | Baeldung )

Testing

Back to Contents


You're doing test-driven development, right? (Right?)

  • Every* public method should have a unit test.
    • *The exceptions are methods that are generated by the IDE, such as constructors and accessors. (If you write these by hand, you will add a unit test for them.)
  • Unit tests should use mocking objects for calls to other components.  If you actually want to test the integration with another component, it’s an integration test, not a unit test.
  • Use of libraries that you did not write is OK.  (You don’t need to test classes in java.lang or java.util or apache.commons)
  • All tests should run at (Jenkins) build time.
    • There are differing opinions on this.  However, if the rule that “all tests are run at build time” is relaxed, we need a rule that “all tests need to run before git commit/push.”  Since the first one can be automated more easily than the second, we should prefer that.
    • Tests that need another system (that is not on Jenkins) to be running are the ones that may need that rule relaxed.  Sometimes, this means that the two systems are too tightly integrated to be testable separately.  This could mean that the two are really a single system.  (Micro-services tend to fall into this trap when their designs are careless; this discussion is “out of scope” for this document.)
  • When building “random” objects for unit tests, use (pseudo)random data. Apache Commons provides RandomUtils and RandomStringUtils, which are useful for generating such data.
  • On the other hand, it would be nice if “random” tests were repeatable.  RandomUtils would be greatly improved if there were a way to set the seed for the internal Random object.  On the gripping hand, Apache Commons is open source. I just took a copy RandomUtils and added a static setSeed(long) method that modifies that object (called RANDOM): 

public static void setSeed(long seed) {

   RANDOM.setSeed(seed); 

}

Other General Tips

Back to Contents


These are just a few things that might not be obvious, even to an experienced Java programmer, but that I have picked up over the years.

  • instanceof is an operator that takes a reference on the left, and a class on the right. The Boolean expression reference instanceof class is used to tell if reference is an instance of class, or inherits from class. (Thank you, Captain Obvious.) What happens if reference is null?

String foo = null;

System.out.println(foo instanceof String); //  false

  • Even though foo is a reference of type String, because it’s null it is not a reference to an instance of String.
  • The way to get the sign of a numerical primitive (int, float, long, etc.) is to compare it with zero. Same for the boxed versions (Double, Float, Short). Interestingly, Integer, Long, BigInteger, and  BigDecimal each have methods to get the sign of a number:
    • BigInteger and BigDecimal have methods named signum(), which returns 1, 0, or -1 to represent the sign.  (There is a field called signum in each class, that has the same function.)
    • For Integer and Long, each has a static method signum(int) and signum(long), respectively. These are implemented using shift magic to quickly return 1, 0, or -1. Integer.signum(int a) returns the value of this expression:

a >> 31 | -a >>> 31

Try it out, prove to yourself that this works.

Don't Do This

Back to Contents


This section may look like a “hall of shame,” but the intent is to provide examples of what not to do.

  • Operations that always do the same thing.


int popularity;

doc.addField("popularity", Math.min(Integer.MAX_VALUE, popularity));


For this discussion, it does not matter what happens in addField(). The call to Math.min() will always return an int/Integer; if popularity happens to be the same as Integer.MAX_VALUE, you will get Integer.MAX_VALUE.  Otherwise, you will get whatever value popularity has.  In either case, you get popularity’s value.  Don’t make unnecessary method calls--they’re not free.  (Rather, if your compiler is "smart enough," they’re free. What does "smart enough" mean? It means, "smart enough to fix or ignore a method call that doesn't make sense to a smart human who understands all the issues." Don't ever count on your compiler being "smart enough" for that.) On the other hand, if popularity is a long, then Math.min((long)Integer.MAX_VALUE, popularity) returns a long, and makes perfect sense.

  • if and else do the same thing.

if (condition) {

 <statements> 

} else {

 <same statements as in the if block> 

}

  • That is, do the same thing in both cases.  This seems kind of obvious; but I’ve seen it in production code. The general principle “Don’t Repeat Yourself” (or DRY) applies, of course, but also remember that the test itself isn’t free.
  • Calling finalize() – finalize() is a method of java.lang.Object, intended to discard any resources the object holds.  It is called by the garbage collector (GC) when the GC determines that no other object or method will use the object again.  However, there is no guarantee when, or even whether, finalize will be called, or from which thread.  Because the method is “owned” by the garbage collector, no other class/method should invoke it.
  • On the same subject, don’t override finalize() to release resources for you. As stated above, there’s no guarantee that it will ever be called; it can be super slow; and it’s deprecated in Java 9 and later.

First PagePreviousNextHome

Copyright © 2019-2025 Jim Hamilton - All Rights Reserved.

Powered by

This website uses cookies.

We use cookies to analyze website traffic and optimize your website experience. By accepting our use of cookies, your data will be aggregated with all other user data.

DeclineAccept