iTranslated by AI
N Things to Watch Out for When Reading 'Introduction to Spring' and 'Comprehensive Spring' in the Reiwa Era
About This Article
As I mention every time I write or speak, even now as we approach 2020, there are still only two reliable Spring-related books:
- Spring Tettei Nyumon (Spring Thorough Introduction)
- Kaitei Shinpan Spring Nyumon (Introduction to Spring, Revised Edition)
Both books (hereafter "the books") are excellent, but they were both released in 2016, and the supported Spring version is 4.2, which is now old.
The latest version as of the end of 2019 is Spring 5.2. In this article, I will introduce several points to be particularly careful about when reading these books in the current Reiwa era.
For all differences from 4.x to 5.x, please check the GitHub Wiki.
Use JDK 8 or Higher
Starting from Spring 5.0, the JDK baseline has been moved to 8 (Spring 4 was based on JDK 6). I don't think anyone trying to use Spring now would attempt to use JDK 6 or 7, but...
In Spring 5.2, support extends up to JDK 14. For details on the version compatibility between JDK and Spring, please refer to the GitHub Wiki.
Use Constructor Injection + Omission of @Autowired Instead of Field Injection
Since before Spring 4.2, there have been three ways to perform DI.
@Component
public class Hoge {
@Autowired
Fuga fuga;
}
@Component
public class Hoge {
private Fuga fuga;
@Autowired
public void setFuga(Fuga fuga) {
this.fuga = fuga;
}
}
@Component
public class Hoge {
private final Fuga fuga;
@Autowired
public Hoge(Fuga fuga) {
this.fuga = fuga;
}
}
Field injection is the method most commonly used in the books. This is likely because it requires the least amount of code (and perhaps due to space constraints on the page).
From Spring 4.3 onwards, if a class has only one constructor, the @Autowired annotation can be omitted.
@Component
public class Hoge {
private final Fuga fuga;
// Since there is only one constructor, @Autowired can be omitted!
public Hoge(Fuga fuga) {
this.fuga = fuga;
}
}
Since it allows the class to be immutable, you should use constructor injection whenever possible.
Use @GetMapping or Similar Instead of @RequestMapping
In Spring MVC, you use @RequestMapping to write controller classes and methods.
@Controller
@RequestMapping("/hoge")
public class HogeController {
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String index() {
return "index";
}
}
From Spring 4.3, annotations for each HTTP request method, such as @GetMapping and @PostMapping, were introduced. It's great because you can write them very concisely!
@Controller
@RequestMapping("/hoge") // It's fine to leave this as @RequestMapping
public class HogeController {
@GetMapping("/index") // Use @XxxMapping!
public String index() {
return "index";
}
}
Use Thymeleaf 3 and Write in HTML Format (Not XHTML)
The books use Thymeleaf 2. To write screens in Thymeleaf 2, you need to use the XHTML format.
By adding a library, it is possible to write in HTML format even with version 2.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Screen</title>
</head>
<body>
<form action="index.html" th:action="@{findByFirstName}">
First Name Keyword: <input type="text" name="firstName" />
<input type="submit" value="Search" />
</form>
...
In Thymeleaf 3, you can write in HTML format by default.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Screen</title>
</head>
<body>
<form action="index.html" th:action="@{findByFirstName}">
First Name Keyword: <input type="text" name="firstName">
<input type="submit" value="Search">
</form>
...
It's great that you no longer have to worry about runtime exceptions from forgetting a /!
Use the WebMvcConfigurer Interface Instead of the WebMvcConfigurerAdapter Class
In Spring 4.3 and earlier, when creating Java Config for Spring MVC, the WebMvcConfigurerAdapter class is often inherited.
@Configuration
@EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
...
}
...
}
From Spring 5.0, this class became deprecated, and it is now recommended to use the WebMvcConfigurer interface that this class implements.
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
...
}
...
}
The WebMvcConfigurerAdapter class was a class that implemented the WebMvcConfigurer interface and overrode all methods with empty implementations. With the move to a Java 8 baseline, all methods in the WebMvcConfigurer interface became default methods with empty implementations. Since its role was fulfilled, the WebMvcConfigurerAdapter class became deprecated.
Source code for WebMvcConfigurer
Use Bean Validation's @NotBlank Instead of Hibernate Validator's @NotBlank
Starting from Spring 5, with support for Java EE 8, the Bean Validation version has been upgraded from 1.x to 2.0 (Hibernate Validator 6.0 or later).
While it is possible to use Bean Validation 1.x with Spring 5, I generally recommend using the latest version.
In Bean Validation 2.0, new constraint annotations were added. Some of these were originally Hibernate Validator-specific annotations that have now been incorporated into the Bean Validation standard. Specifically, these are the following annotations (all in the javax.validation.constraints package):
@NotEmpty@NotBlank@Email
Accordingly, Hibernate Validator's proprietary @NotBlank, @NotEmpty, and @Email (all in the org.hibernate.validator.constraints package) have been deprecated.
Note the Lack of Compatibility in Spring Data's CrudRepository
From Spring Data 2.x onwards, the names and return values of methods defined in CrudRepository have changed.
The changes include support for java.util.Optional, method name updates, etc.
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> save(Iterable<S> entities);
T findOne(ID id);
boolean exists(ID id);
Iterable<T> findAll();
Iterable<T> findAll(Iterable<ID> ids);
long count();
void delete(ID id);
void delete(T entity);
void delete(Iterable<? extends T> entities);
void deleteAll();
}
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
Always Explicitly Configure Spring Security's PasswordEncoder
In Spring Security 4 and earlier, if you did not explicitly specify a PasswordEncoder, password hashing was not performed.
In Spring Security 5 and later, if you do not explicitly specify a PasswordEncoder, DelegatingPasswordEncoder is used. This reads the prefix of the password stored in the database or elsewhere and delegates the processing to the appropriate PasswordEncoder.
<img width="866" alt="Screenshot 2019-12-31 11.03.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58650/d1e1471a-46c5-11ca-22e3-93370139848e.png">
I don't think anyone is not specifying a PasswordEncoder in a production environment (at least I want to believe so). However, it might have been omitted for simplicity in study-related code. In Spring Security 5.x and later, make sure to always specify it. Defining the PasswordEncoder as a Bean is sufficient. Of course, ensure that passwords stored in the database are also hashed using the same algorithm's PasswordEncoder.
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Be Aware That the Content of Spring Boot Has Changed Significantly
Regarding the transition from Spring Boot 1.x to 2.x, there are too many changes to list them all (lol).
Just thinking off the top of my head:
- Update of various libraries
- Spring 4.x -> 5.x
- Spring Data 1.x -> 2.x
- Spring Security 4.x -> 5.x
- Thymeleaf 2 -> 3
- Hibernate -> 5.4
- Jackson -> 2.10
- Hibernate Validator 5.x -> 6.1
- Flyway 4 -> 6
- JDK 8 support
- thymeleaf-extras-java8time already added
- jackson-datatype-jdk8, etc., already added
- Simplification of security
- Most security-related properties were removed
- Improvements to Actuator
- Fundamental changes to internal architecture
- Changes to authentication and authorization
- Addition of Micrometer
- Property changes
- Renaming of properties
- Names of various properties have been significantly changed
...and so on. There might be many others.
Update: 2020-02-12
I am adding some materials presented at JSUG study sessions. Both are valuable resources, so please take a look!
Study Session Slides
- Upgrading a payment service's Spring Boot version to 2.x (@b1a9idps)
- Pitfalls encountered while upgrading Spring Boot from 1.5 to 2.1 (@kawakawaryuryu)
List of blog posts by @b1a9idps
- Upgraded a payment service's Spring Boot version to 2.x
- Upgraded Flyway from 3.x to 5.x
- Upgrading from Spring Boot 1.5.x to 2.0.x ~Spring Web Edition~
- Upgrading from Spring Boot 1.5.x to 2.0.x ~Spring Data Edition~
- Upgrading from Spring Boot 1.5.x to 2.0.x ~Spring Test Edition~
Update: 2020-10-16
In Spring Boot 2.3 and later, Hibernate Validator is no longer included in spring-boot-starter-web (Release Note).
If you want to use Hibernate Validator, you need to add spring-boot-starter-validation.
(Update: 2022-06-16) Spring Security's WebSecurityConfigurerAdapter Has Been Deprecated
The way security settings are written has changed significantly. Please refer to the article below 👇 for details.
Conclusion
I will continue to add more as I think of them.
Discussion