14

I see this scattered throughout code base:

@RequestMapping(value = "myValue")

I would prefer to use something like this:

@RequestMapping(value = Constants.myValue)

It seems to break DRY using the actual String value within @RequestMapping instead of constant. But is this good code practice? Should I use an enum instead? I may need to use Constants.myValue elsewhere in the code base.

buræquete
  • 14,226
  • 4
  • 44
  • 89
blue-sky
  • 51,962
  • 152
  • 427
  • 752
  • I am not able to understand how it breaks a DRY Principle. Can someone Please explain . IMO, RequestMapping we use only once to define api. In spring boot rest api, If there is a common prefix for our api then we can define it at controller or can be define in properties file. spring framework can take care of it. – nagendra patod May 19 '21 at 07:57

1 Answers1

24

Should I use an enum instead?

You can't. Annotation variables must be compile-time constants. Enums and String literals both are, but you can't create an enum that is a String and @RequestMapping needs a String (and if your enum has a method that returns a String or a String field, that's not a compile-time constant). Since there are multiple rounds of annotation processing it does work when the constant is in another class.

That said: yes, I'd say using a dedicated constants class (perhaps several, for different types of constants) is a good practice that I use whenever I can (and it works with annotations as long as the constant a) is not defined inside the same compilation unit that has the annotation and b) is initialized in the declaration (as opposed to a static initializer block)).

Here's an example:

Controller

@Controller @RequestMapping(value = Mappings.CUSTOMER_PAGE)
public class CustomerPageController{
    // methods here
}

Constants class

public static final class Mappings{
    private Mappings(){}
    public static final String CUSTOMER_PAGE = "path/to/customer/page"
    // more constants
}

And here are some versions that won't work:

a)

@Controller @RequestMapping(value = CUSTOMER_PAGE)
public class CustomerPageController{
    private static final String CUSTOMER_PAGE = "path/to/customer/page";
}

This won't compile because the annotation references a constant inside the class it annotates. This can't work because during compilation, annotations are processed in a separate round before the rest of the code, while the class needs the annotation to already be processed for compilation (i.e. there's a circular dependency between annotation and constant)

b)

public static final class Mappings{
    private Mappings(){}
    public static final String CUSTOMER_PAGE;
    static{
        CUSTOMER_PAGE = "path/to/customer/page"
    }

    // more constants
}

Although this is a static final field, it is not a compile-time constant, hence it can't be used as an annotation parameter

codebox
  • 19,927
  • 9
  • 63
  • 81
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • Ah, ok, I thought directly with an enum. You could also make the mappings class reference an enum – NimChimpsky Jul 31 '12 at 14:19
  • 2
    this works though: `@RequestMapping(MyController.PATH) public class MyController { protected static final String PATH = "/some-path"; ...` – Neil McGuigan Feb 11 '15 at 20:02
  • @SeanPatrickFloyd I have one doubt! Do we really need to declare CUSTOMER_PAGE with a static final modifier? Can't we just declare it as static? I think no need to declare CUSTOMER_PAGE as final. It may create overhead to JVM. – Akshay Pethani Mar 11 '19 at 09:10
  • @AkshayPethani I have never heard anybody express this concern about constants. They are even featured in the Oracle Java tutorial on this page: https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html – Sean Patrick Floyd Mar 11 '19 at 23:57