DEV Community

Cover image for How to create Annotation in SpringBoot Java? [ENG/PT-BR]
Wagner Negrão 👨‍🔧
Wagner Negrão 👨‍🔧

Posted on

How to create Annotation in SpringBoot Java? [ENG/PT-BR]

How to create an @Annotation in Java SpringBoot?

Link to the post in Portuguese

I was doing a code for registration and came across two different classes but that had the same purpose. Perform a filter in the database to know if that information was already present. With this need I searched the internet and saw that a possible solution would be to create an Annotation for this validation, this way would not have the repeated code snippet.

Now to the code:

Initially you will need to create an interface, I will call ChechValue as in the file below.


@Documented
@Constraint(validatedBy = {ChechValueValidator.class})
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ChechValue {
    String message() default "default message";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String fieldName();
    Class<?> domainClass();
}

Enter fullscreen mode Exit fullscreen mode

To create the annotation you must import some information for it to be executed, first the constraint that will add to the spring bean the class that is being used for validation, according to the Target that is responsible for defining which elements we will obtain and finally the Retention that is responsible for defining how this annotation will be executed, in our case will be Runtime.

That done, we can create the standard fields of an annotation like:

message() // Responsible for having a standard message or receiving a message in case of error

groups() // Responsible for defining access groups

payload() // Responsible for receiving information, something similar to the body of a request

fieldName() // In our case is responsible for defining which field will be validated

domainClass() // Responsible for defining which class will be used as Base


Enter fullscreen mode Exit fullscreen mode

It is worth mentioning that it is an important convention to create the implementation file with the same interface name by adding only the Validator at the end of the class.

That done, we will implement the validation. Initially we will implement the ConstraintValidator, in it we will define the interface that will be used to create the annotation and also the object that will receive, as in the code below.


public class ChechValueValidator implements ConstraintValidator<ChechValue, Object> {

    private String domainAttribute;
    private Class<?> class;
    @PersistenceContext
    private EntityManager manager;

    @Override
    public void initialize(UniqueValue constraintAnnotation) {
        domainAttribute = constraintAnnotation.fieldName();
        class = constraintAnnotation.domainClass();
    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext context) {
        Query query = manager.createQuery(
            "Select 1 from " +
            klass.getName() +
            " Where " +
            domainAttribute +
            " =:value");

        query.setParameter("value", object);
        List<?> list = query.getResultList();

        Assert.state(list.size() <= 1, "More than one value was identified in the class " + class.getName());

        return list.isEmpty();
    }
}

Enter fullscreen mode Exit fullscreen mode

As mentioned before we will have a validation if the value is already present, soon we will need to implement EntityManager, class that we will make the filter and the attribute that will be made the filter.

In the class isValid() validation will be done, in this case the filter will be performed in the database to know if there is more than one information with the same data.

Now we have the example of Annotation being used:

public class AuthorRequest {
    @NotNull(message = "is required")
    private final String name;
    @NotNull(message = "is required")
    @Email
    @ChechValue(domainClass = Author.class, fieldName = "email")
    private final String email;
    @NotNull(message = "is required")
    @NotEmpty
    private final String description;
}

Enter fullscreen mode Exit fullscreen mode

Using this Annotation we stopped having several similar code snippets but at different points of the code, helping us in more code clarity and less repetition.


Como criar uma @Annotation em SpringBoot Java?

Estava fazendo um código para cadastro e me deparei com duas classes distintas porém que possuíam a mesma finalidade. Realizar um filtro no banco de dados para saber se aquela informação já era presente. Com essa necessidade procurei na internet e vi que uma possível solução seria criar uma annotation para essa validação, desta forma não teria o trecho de código repetido.

Agora vamos ao código:

Inicialmente você vai precisar criar uma Interface, irei chamar de ChechValue como no arquivo abaixo.


@Documented
@Constraint(validatedBy = {ChechValueValidator.class})
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ChechValue {
    String message() default "default message";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    String fieldName();
    Class<?> domainClass();
}

Enter fullscreen mode Exit fullscreen mode

Para criar a anotação deve-se importar algumas informações para que ela seja executada, primeiro o Constraint que irá adicionar ao bean do spring a classe que está sendo utilizada para validação, segundo o Target que fica responsável por definir quais elementos iremos obter e por fim o Retention que é responsável por definir como será executada essa anotação, em nosso caso será Runtime.

Feito isso, podemos criar os campos padrões de uma anotação como:

message() // Responsável por ter uma mensagem padrão ou receber uma mensagem em caso de erro

groups() // Responsável por definir grupos de acesso

payload() // Responsável por receber informação, algo similar ao body de uma request

fieldName() // Em nosso caso é responsável por definir qual campo será validado

domainClass() // Responsável por definir qual classe será utilizada como Base

Enter fullscreen mode Exit fullscreen mode

Vale ressaltar que é uma convenção importante criar o arquivo de implementação com o mesmo nome da interface adicionando apenas o Validator no fim da classe.

Feito isso, iremos implementar a validação. Inicialmente vamos implementar o ConstraintValidator, nele iremos definir a interface que será utilizada para criar a anotação e também a objeto que irá receber, como no código abaixo.


public class ChechValueValidator implements ConstraintValidator<ChechValue, Object> {

    private String domainAttribute;
    private Class<?> class;
    @PersistenceContext
    private EntityManager manager;

    @Override
    public void initialize(UniqueValue constraintAnnotation) {
        domainAttribute = constraintAnnotation.fieldName();
        class = constraintAnnotation.domainClass();
    }

    @Override
    public boolean isValid(Object object, ConstraintValidatorContext context) {
        Query query = manager.createQuery(
            "Select 1 from " +
            klass.getName() +
            " Where " +
            domainAttribute +
            " =:value");

        query.setParameter("value", object);
        List<?> list = query.getResultList();

        Assert.state(list.size() <= 1, "More than one value was identified in the class " + class.getName());

        return list.isEmpty();
    }
}

Enter fullscreen mode Exit fullscreen mode

Como mencionado anteriormente iremos ter uma validação se o valor já é presente, logo precisaremos implementar o EntityManager, classe que iremos fazer o filtro e o atributo que será feito o filtro.

Na classe isValid() será feito a validação, nesse caso será realizado o filtro no banco de dados para sabermos se existe mais de uma informação com o mesmo dado.

Agora temos o exemplo da annotation sendo utilizada:


public class AuthorRequest {
    @NotNull(message = "is required")
    private final String name;
    @NotNull(message = "is required")
    @Email
    @ChechValue(domainClass = Author.class, fieldName = "email")
    private final String email;
    @NotNull(message = "is required")
    @NotEmpty
    private final String description;
}

Enter fullscreen mode Exit fullscreen mode

Utilizando essa annotation deixamos de termos vários trechos de código parecidos porém em pontos diferentes do código, nos auxiliando em mais clareza de código e menos repetição.

Top comments (0)