3 min read

How spring boot annotations work?

August 30, 2020

Back in my Java 1.4 days, I used to generate Hibernate configurations files using Velocity templates in the ant build. This approach is one example of declarative programming. You direct the tool on how to generate code, and it will build it for you. Fast-forward to now, All you need is one @Entity annotation. Again, this is still declarative programming. But it just got easier. All thanks to the annotations and the libraries evolved around it.

Java Annotation is a feature introduced as part of Java 1.5. This feature provides a mechanism to mark information to Classes, Methods and Fields.

These annotations do not affect the programs directly. But these annotations provide hints that would change the way a tool or library looks at your code. For example, The @Entity class is a standard POJO class. When your application introduces a JPA framework, the @Entity annotation comes into effect.

Annotations can also help in compile-time like Project Lombok. A simple @Data annotation can generate getters, setters, equals, hashcode and other simple POJO methods.

A compiler will give errors if you use @Override and the actual method signature looks different. This error is another example of compile-time scoped annotations.

I’ll explain how annotations can be defined and used with a simple example.

public @interface Alert {

    String[] designation();

@Alert(designation = "Manager")
public class BusinessException extends Exception {
@Alert(designation = {"Administrator","Manager"})
public class UnauthorizedException extends Exception {
public class Main {

    public static void main(String[] args) {

        try {
            throw new BusinessException();
        } catch (BusinessException e) {
	try {
            throw new UnauthorizedException();
        } catch (UnauthorizedException e) {


    private static void processException(Exception e) {
        Alert annotation = e.getClass().getAnnotation(Alert.class);
        if (annotation == null) {
            System.out.print("Send out default notification");
        String[] designation = annotation.designation();
        for (String s : designation) {
            System.out.printf("Send out notification for %s to %s \n",
e.getClass(), s);

Here Alert annotation contains the information(meta-data) about whom to call in case of each Exception.

Note that without the whole processException part, The annotation is meaningless. And that is the ingenuity of annotations. They don’t fundamentally change how your class works. It is more about how you can utilize these metadata at runtime to add additional behaviour.

Classes annotated with a particular annotation can also be looked up with the help of reflection.

Reflections reflections = new Reflections(AnnotationLookup.class.getPackage());
Set<Class<?>> typesAnnotatedWith = reflections.getTypesAnnotatedWith(Alert.class);
for (Class<?> aClass : typesAnnotatedWith) {

If you look at the above two examples, You can get clarity on how spring boot internally handles these annotations.

A typical Spring boot application starts with the help of SpringApplication.run(Main.class) call. This method call is static and takes a class name Main.class as a parameter. At this point, spring boot has enough information to boot the application. It uses the main class’s package as root and looks for classes that hold spring-related annotations.

For Example, If you include spring-boot-starter-data-jpa it will look for all @Entity and @Repository annotated classes and interfaces. For spring-boot-starter-web Annotations like @RestController, @RequestMapping etc are processed.

What classes to look and which auto configurations to run are available in a special descriptor named META-INF/spring.factories. With all of these combined, you have your spring boot application.

We will speak about spring.factories later in this series.