Image of 5 essential Spring Boot annotations

ADVERTISEMENT

Table of Contents

Introduction

In this article, we'll provide some background on annotations in Java and Spring Boot. Then we'll present our picks for 5 essential annotations that developers should be familiar with when building applications with Spring Boot.

Overview of Annotations in Java

The Java programming language includes annotations as a native feature. An annotation is a label that developers can add onto a specific piece of code to provide additional context or functionality. An annotation is prefixed with an "at-sign" @ and placed on the line before the code that it applies to, as follows:

@PretendAnnotation(name="name",  value="value")
public void pretendMethod() {
// Empty method
}

In the snippet above, the annotation is applied to a method. Depending on the purpose of an annotation, it could be applied to a class, method, variable, field, or other code structure. Parameters (like name and value in the previous snippet) can be specified after the annotation label. These parameters allow developers to customize the behavior of the annotated code, as we will see later when we get to the Spring Boot annotations.

One of the most common Java annotations that you have probably seen is @Override. This is a very simple annotation that tells the compiler that the annotated method must exist in the parent class. If it doesn't, the compiler will throw an error. However, this annotation doesn't affect the bytecode produced by the compiler. It just sets a condition that may or may not trigger a compiler error.

Furthermore, custom annotations can be created that do make it through the compiler and affect the bytecode. This is done by marking the annotations with a RUNTIME retention policy, which we won't discuss in detail now. The Spring Boot annotations we will cover all behave this way, allowing the developer's customized annotation parameters to define how their application works.

Overview of Annotations in Spring Boot

Spring Boot (and more generally the Spring Framework) provides a way for developers to write code that dictates what their applications do, while using Spring's set of custom annotations to offload boilerplate code to the framework.

A few examples of standard tasks that web applications perform are:

  • Defining mappings between URLs and code methods
  • Handling web requests and responses
  • Configuring mappings between code objects and database tables
  • Fetching data from and writing data to a database table

Spring Boot provides annotations that make these (and many, MANY other) functionalities very easy for developers to implement without having to worry about most of the technical details of the webserver, database, or arbitrary service in use. Note that we limited our list to the 4 points above since the annotations we'll discuss next apply primarily to these areas.

If you are curious enough to pop the hood and peek at Spring Boot's code (by following Javadoc links in your IDE), the annotations we will cover have a structure similar to the following:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PretendAnnotation {
	String name() default "";
}

This snippet is standard Java code that defines a made-up, custom annotation called PretendAnnotation. The @interface syntax is how a custom annotation is defined in Java. Somewhat confusingly, the custom annotation definition is itself annotated with the following standard Java annotations:

  • @Documented: Specifies that the annotation will show up in the Javadoc for the annotated object.
  • @Target: Specifies the type of objects that the annotation can be applied to.
  • @Retention: Specifies whether the annotation metadata can be accessed at runtime by the application (will determine whether the compiled bytecode is affected).

Spring Boot's annotations are often used to label objects as candidates for auto-detection when using annotation-based configuration and classpath scanning. This allows Spring Boot to identify the annotated objects and perform specific actions or add functionality accordingly. It is also vital for Spring's ability to autodetect and define Spring beans and inject them into other parts of the code as needed (dependency injection).

Note that all annotations used in Spring Boot can be more broadly defined as being a part of the Spring Framework.

Now that we've covered some background on how annotations work in Java and Spring Boot, let's move on to our list of the top 5 essential annotations in Spring Boot!

1. @Controller

In Web MVC, a controller is the piece of the puzzle that defines how a web application's routing works. A controller defines a mapping between the desired webapp URL endpoints and the code to run when a user requests each URL endpoint.

For example, when a user types a URL into their browser, such as https://initialcommit.com/blog, the browser sends a web request to the webserver pointed to by the initialcommit.com domain. When the webserver receives the request, it can see that the browser is requesting the /blog endpoint. But how does it know what to do when receiving requests at that endpoint?

When using Spring Boot, the answer is the controller. In Spring, a controller is usually a Java Class labelled with the @Controller annotation:

@Controller
public class PretendController {
       ...
}

When the webapp code is compiled, Classes labelled with @Controller are scanned to determine how to map URL endpoints to code execution. This is most commonly done at the method level, where each method in the Controller class is mapped to a particular URL endpoint. In the next section, we'll see how the @RequestMapping annotation works in conjunction with @Controller to achieve this.

2. @RequestMapping

The @RequestMapping annotation is typically applied at the method level of Controller classes, as follows:

@Controller
public class PretendController {

    @RequestMapping(value = "/blog", method = RequestMethod.GET)
    public ModelAndView blogMethod() {
        
        ModelAndView modelAndView = new ModelAndView();
        
        modelAndView.setViewName("blog");
        
        return modelAndView;
        
    }

}

Note that @RequestMapping is used as a label on the line before the method blogMethod(). The value parameter is used to specify the URL endpoint that will route to this method. The method parameter is used to specify the type of request (usually GET or POST) that the method will accept.

The code inside the blogMethod() curly braces is what will execute when a request is received at https://initialcommit.com/blog. The code could perform whatever logic is necessary to create and serve the blog page. In this case, it simply creates a new ModelAndView object, sets the view name to a template called blog.html, and returns the object so it can be included in the web response back to the browser.

Next, we'll discuss a couple of annotations for mapping code Classes to database tables.

3. @Entity

The @Entity annotation is used to label a Class. This tells Spring that a corresponding table with the same name as the Class exists in the database. Each column in the database table typically corresponds to a member variable of the Class. This enables us to create an instance of the Class, which Spring can use to store values from a database record in each corresponding member variable.

In the following snippet, we show how the @Entity annotation can be applied to a Class representing a person:

@Entity
public class Person {

    @Id
    private Integer id;
	
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

}

This example implies that there is a table in the web application's database called person with columns for id, and name. Each field in the Class maps to the corresponding column in the database.

The @Id annotation specifies the primary key of the table, so that Spring knows which column uniquely identifies each record in the table. This is important for data integrity and SQL generation purposes behind the scenes.

Note that each field (id and name) has a corresponding public Getter and Setter method used to fetch and retrieve those values on Person object instances. These so called accessor and mutator methods can be autogenerated using an IDE like Eclipse.

The @Entity annotation is important since it tells Spring how our plain-old Java objects (or POJOs) should map to the database schema. This enables database records to be represented by Class instances very conveniently.

4. @Repository

Now that we know how to create relationships between Java objects and database tables, let's learn how to fetch those records from the database table in order to store the values in a Java object instance.

The @Repository annotation is often applied to an interface as follows:

@Repository
public interface PersonRepository extends CrudRepository<Person, Integer> {
    
    List<Person> findAll();

    Person findById(Integer id);

}

This is one of multiple ways to create a repository in Spring Boot. In this case, we define a repository interface to interact with Person objects from the person table in the database.

The annotation indicates that an annotated Class is a Repository, originally defined by Domain-Driven Design (Evans, 2003) as "a mechanism for encapsulating storage, retrieval, and search behavior which emulates a collection of objects". (Quote from Spring Javadoc). Classes that implement an annotated interface can be auto-detected via classpath scanning.

In our example above, we defined two query methods called findAll() and findById() which derive queries from the method names themselves. When the findAll() query method is called, it tells Spring to write a SQL query that selects all unique records from the person table and return them as a List<Person> object. The findById() query method tells Spring to write a SQL query that selects a single record from the person table matching the supplied id parameter. The developer has a lot of flexibility to determine what data is selected simply by varying the name of the query method.

When the Spring Boot application starts, a singleton Spring bean (a single application-wide instance of a Class for the annotated repository) is created. This allows the repository to be linked into other parts of the application via dependency injection, as needed. We will see how this can be done in the next section.

5. @Service

A service in Spring is best thought of as an abstraction of a particular functionality. In the previous section, we saw how to create a repository that can be linked (injected) into other parts of our application as needed. This repository can be used to populate our Java Person instances from a database table. However, what if we later decide that we want to populate our Person data from a web service instead of the database. In a large application, it would be time consuming and messy to replace every occurrence of our PersonRepository injection with our new PersonWebService.

A better option is to create a middleman Class called a service. A service acts as a bridge between our application's business code and the provider of a particular functionality. It enables us to swap out resources behind the scenes without touching the business logic code, which is a good practice in modular design.

Here is what a service for our Person populator might look like:

@Service
public class PersonService {
    
    @Autowired
    PersonRepository personRepository;
    
    public List<Person> findAll() {
        return personRepository.findAll();
    }
    
    public Person findById(Integer id) {
        return personRepository.findById(id);
    }

}

As you can see, we defined a Class called PersonService labelled with the @Service annotation. This indicates that an annotated Class is a "Service", originally defined by Domain-Driven Design (Evans, 2003) as "an operation offered as an interface that stands alone in the model, with no encapsulated state." (Quote from Spring Javadoc). Classes annotated in this way can be auto-detected via classpath scanning.

The first line we encounter in our Class is the @Autowired annotation, which injects the singleton Spring bean for PersonRepository that we described in the previous section. We won't go into detail here, but this is accomplished behind the scenes via Spring's dependency injection functionality.

Next, we defined two methods, each simply calling one of the corresponding query methods in our PersonRepository. Now, in our business logic code, we can link in the PersonService (using @Autowired) instead of the PersonRepository. If we ever decide to change the data source for the Person data, we can simply update the PersonService to call methods from the new source (the webservice) instead of the repository.

Conclusion

In this article, we presented 5 Spring Boot annotations that are essential to know when creating applications with Spring Boot. You will certainly encounter these if you ever work on a Spring Boot app, so they are worth understanding at a basic level.

Final Notes