Episode 58
This week I was planning to publish introduction to Amazon Web Services, but since I’m working on preparing Spring-based web applications development course for Wrocław University of Technology students with my two colleagues, I decided to change the schedule a bit. I planned to write few articles about Spring framework anyway, but in order to fit everything before particular classes in March and April I need to start exactly now.
I wrote a Spring Hello World article over two years ago actually, but today I wanted to talk a bit more about some fundamental concepts: Inversion of Control and dependency injection. Let us take a closer look at application structure, configuration, components and wiring. There will be quite a lot of code snippets. If you would like to play with complete application working out of the box, visit my GitHub project Spring Angular Intro. The application is based on the one I prepared for Angular Intro article in September 2016, but now we will focus on back-end exclusively. This tag corresponds to project state at the time of writing this article.
Concepts
Classic approach to program structure is that we write custom, high-level code which calls lower level code. The flow of control goes from our code to a generic framework or libraries. Inversion of control is a technique, where we provide fragments of high-level code that is called by low-level code on various events, like receiving HTTP request, session timeout or server shutdown. The flow of control goes from generic framework instead of to it, thus the notion of inversion.
Dependency injection is a special case of that. Instead of creating required components by hand, we describe what we need, and let the generic framework take care of their lifecycle including creation, initiation, injection and destruction. We don’t have to take care of tree of objects that depend upon each other. The Spring container deals with that.
Spring Boot
Before we dive deep into dependency injection, let’s start with the basics. Our example code uses Spring Boot – what does it mean exactly? Spring Boot is one of Spring framework projects, much as Spring Data or Spring Social, which aims at simplifying web application development by supplying out of the box solutions for typical problems. The idea is to minimize number of choices that developer has to make to configure a production ready application by providing sensible defaults. The concept is called convention over configuration. Spring Boot also provides many tools like health checks, metrics or embedded servers. Time to look at some code now.
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
At the heart of Spring Boot application lies our class containing the main method and usually called Application.java. It’s annotated with @SpringBootApplication, which is actually an alias for three other annotations, namely:
- @Configuration – marks class containing bean definitions and other configuration that was kept in xml files before Java annotations become widespread. There can be many configuration classes.
- @EnableAutoConfiguration – tells Spring Boot to attempt to automatically configure Spring application based on existing dependencies.
- @ComponentScan – indicates that the package containing annotated class and all its sub-packages should be scanned for managed components – candidates for injection. Often put in application root package. We can explicitly provide only some package names to minimize startup time.
Autowiring
The easiest way to inject dependency into an object is to use @Autowired annotation. First, let’s define a component we would like to inject and an interface for it:
public interface InfoService { public Info getInfo(); } @Service public class InfoServiceDev implements InfoService { @Override public Info getInfo() { return new Info("Dev info"); } }
It will produce a simple Info object containing String message. The @Service annotation tells us that it’s a managed component and candidate for injection. Now we will inject it into another managed component – a controller.
@Controller public class MainController { @Autowired private InfoService infoService;
Both @Service and @Controllers are specialized sub-types of @Component objects. When we start a Spring application with such annotations, the framework will look for best matches for InfoService object, and since there is only one implementation of the interface, the InfoServiceDev, the object of such type will be instantiated and assigned to infoService property. There are two more ways to wire objects, by setter and by constructor, like that:
@Autowired public void setInfoService(InfoService infoService) { this.infoService = infoService; } @Autowired public MainController(InfoService infoService) { this.infoService = infoService; }
Ambiguities
What, if there are more than one candidates for wiring? If we do nothing about that, Spring will throw an exception during startup and decline any further cooperation. One thing that we can do is to add name parameter to our services and provide @Qualifier annotation along with @Autowired. Suppose, we have second service, let’s call it “prod”:
@Service("prod") public class InfoServiceProd implements InfoService { @Override public Info getInfo() { return new Info("Prod info"); } }
Let’s add name to our existing service:
@Service("dev") public class InfoServiceDev implements InfoService {
Now, we can choose which one should be injected using parameter of @Qualifier annotation, like that:
@Autowired @Qualifier("prod") private InfoService infoService;
Another method to solve wiring ambiguity is to introduce priority by marking one of the components with @Primary annotation:
@Primary @Service public class InfoServiceDev implements InfoService {
Configuration and Profiles
Instead of marking objects with @Service, we can describe them as managed components in our configuration class, adding methods annotated with named @Bean that actually constructs objects using new operator:
@Bean(name = "dev") public InfoService getInfoDev() { return new InfoServiceDev(); } @Bean(name = "prod") public InfoService getInfoProd() { return new InfoServiceProd(); }
Now, as I said before, there can be more than one @Configuration class. For example we can have different classes for development environment and production environments, that will produce different beans:
@Configuration @Profile("dev") public class DevConfiguration { @Bean public InfoService getInfoDev() { return new InfoServiceDev(); } } @Configuration @Profile("prod") public class ProdConfiguration { @Bean public InfoService getInfoProd() { return new InfoServiceProd(); } }
In order to choose profile, we have to provide an argument for our JVM. In our case it will be either -Dspring.profiles.active=dev or -Dspring.profiles.active=prod. This way we can change behavior of our application after restart, without changing the source code.
Spring is coming
My current immediate plan include quick overviews of few other Spring projects: Data REST / HATEOAS, Data JPA, Security and perhaps testing, then getting back to Amazon Web Services, but we will see how it goes, sometimes improvisation is the word of the day.
If you want to say hi in person, aside from Wrocław University of Technology in March and April, you can meet me on February 25 at Boiling Frogs conference in Wrocław. If you miss it, don’t worry, I will most likely write about the event. Stay tuned!
3 responses to “Spring Core Basics”