-
Notifications
You must be signed in to change notification settings - Fork 0
Spring
Spring is a very large topic, so we will only be covering the essentials and hand-picked topics.
@SpringBootApplication - A class annotated as an application should be defined as an entry point. The annotation includes:
-
@EnableAutoConfiguration- Enable the auto-configuration feature, a core part of Spring Boot. -
@ComponentScan- Automatically scan this package and sub packages for components (configurable). -
@Configuration- This class is a configuration and will automatically load any beans defined.
As with any entry point, a main method needs to be defined - in this case calling the SpringApplication run method.
@SpringBootApplication
public class WebServerApp {
public static void main(String[] args) {
SpringApplication.run(WebServerApp.class, args);
}
}@Component - A class annotated as a component is automatically instantiated and registered as a bean if it is imported using @ComponentScan. Note that any parameters to the constructor selected will be injected by spring as if they were autowired, so need to be defined in a configuration.
There are a few special types of component:
-
@Configuration- Marks the class as part of the initialisation layer. -
@Controller- Marks the class as part of the presentation layer. -
@Repository- Marks the class as part of the persistence layer. -
@Service- Marks the class as part of the service layer.
-
@Controller- A class annotated as a controller is a component where each annotated method responds to HTTP requests. -
@RestController- A class annotated as a controller is a combination of@Controllerand@RequestMapping
| Annotation | Description |
|---|---|
| @RequestMapping | Specify the base url and content type |
| @GetMapping | Alias for an HTTP GET request. This is used to lookup data. It should not contain a body. |
| @PostMapping | Alias for an HTTP POST request. This is used to create data. |
| @PutMapping | Alias for an HTTP PUT request. This is used to update data. |
| @DeleteMapping | Alias for an HTTP DELETE request. This is used to delete data. |
| @PathVariable | A parameter that represents a variable in the HTTP request path. |
| @RequestParam | A parameter that represents a query parameter in the HTTP request. |
| @RequestHeader | A parameter that represents a header from the HTTP request. |
| @CookieValue | A parameter that represents a cookie from the HTTP request. |
| @RequestBody | A parameter that represents the body of the HTTP request. |
@RestController
@RequestMapping("/users", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
private List<User> users = new ArrayList<>();
@GetMapping
public List<User> getAllUsers() {
return users;
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable int id) {
Optional<User> user = users.stream().filter(u -> u.getId() == id).findFirst();
return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<String> createUser(@RequestBody User user) {
users.add(user);
return ResponseEntity.ok("User created successfully");
}
@PutMapping("/{id}")
public ResponseEntity<String> updateUser(@PathVariable int id, @RequestBody User updatedUser) {
for (int i = 0; i < users.size(); i++) {
if (users.get(i).getId() == id) {
users.set(i, updatedUser);
return ResponseEntity.ok("User updated successfully");
}
}
return ResponseEntity.notFound().build();
}
@DeleteMapping("/{id}")
public ResponseEntity<String> deleteUser(@PathVariable int id) {
if (users.removeIf(user -> user.getId() == id)) {
return ResponseEntity.ok("User deleted successfully");
}
return ResponseEntity.notFound().build();
}
}@Configuration - A class annotated as a configuration is used to define configuration for an aspect of the application. Configurations can be imported by the spring application and by each other using the @Import annotation.
The entire configuration can also be made optional, even if imported, using a conditional annotation.
@Bean - A method annotated as a bean will have its return value automatically registered as a spring bean. Note that any parameters to the method will be injected by spring as if they were autowired, so need to be defined in a configuration.
A bean can also be made optional using a conditional annotation.
A bean or even an entire configuration can be optionally loaded, by using a conditional annotation. For example:
-
@ConditionalOnProperty- if a property is set to a specific value -
@ConditionalOnExpression- if a spring expression evaluates to true -
@ConditionalOnResource- if a resource exists -
@ConditionalOnBean/@ConditionalOnMissingBean- if a bean has or has not been defined -
@ConditionalOnClass/@ConditionalOnMissingClass- if a class is or is not on the classpath
When starting up an application, configuration properties are loaded from various sources (typically files). A simple way to load properties is to use the @Value to annotate fields and parameters.
public class UpdateManager {
@Value("${app.update.frequency.amount}")
private long amount;
@Value("${app.update.frequency.unit}")
private TimeUnit unit;
}@ConfigurationProperties - A class annotated with this will be automatically instantiated and its fields populated directly as properties. This is the cleanest way to import properties and should be used in preference to @Value.
To import the properties so they are created as a bean, the @EnableConfigurationProperties annotation must be applied to a @Configuration.
// Read the properties 'app.update.frequency.amount' and 'app.update.frequency.unit'
@ConfigurationProperties("app.update.frequency")
public record UpdateFrequency(long amount, TimeUnit unit) {}
// Enable the properties to make then available as a bean
@Configuration
@EnableConfigurationProperties(UpdateFrequency.class)
public class UpdaterConfig {
@Bean
public UpdateManager updater(UpdateFrequency frequency) {
return new UpdateManager(frequency.amount(), frequency.unit());
}
}