Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting Constants via Custom Annotation

I have a bunch of constants throughout my code for various adjustable properties of my system. I'm moving all of them to a central .properties file. My current solution is to have a single Properties.java which statically loads the .properties file and exposes various getter methods like this:

public class Properties {
    private static final String FILE_NAME = "myfile.properties";
    private static final java.util.Properties props;

    static {
        InputStream in = Properties.class.getClassLoader().getResourceAsStream(
                FILE_NAME);
        props = new java.util.Properties();
        try {
            props.load(in);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getString(Class<?> cls, String key) {
        return props.getProperty(cls.getName() + '.' + key);
    }

    public static int getInteger(Class<?> cls, String key) {
        return Integer.parseInt(getString(cls, key));
    }

    public static double getDouble(Class<?> cls, String key) {
        return Double.parseDouble(getString(cls, key));
    }
}

The only problem with that is that for every constant that I get from this file, I have some boilerplate:

private final static int MY_CONSTANT = Properties.getInteger(
    ThisClass.class, "MY_CONSTANT");

I don't think I want to use Spring or the like as that seems like even more boilerplae. I was hoping to use a custom annotation to solve the issue. I found this tutorial, but I can't really sort out how to get the functionality that I want out of the annotation processing. The Java docs were even less helpful. This should be a thing I should be able to do at compile time, though. I know the names of the class and field.

What I'm thinking is something like this:

@MyAnnotation
private static final int MY_CONSTANT;

Anyone know how I would go about doing this or at least best practices for what I want to do?

like image 578
Ellis Michael Avatar asked Mar 22 '15 16:03

Ellis Michael


1 Answers

First of all, you shouldn't do it. It's practical, but too hacky and if you ever want to write a test using different settings, you'll run into problems. Moreover, nobody's gonna understand how it works.

An annotation processor can probably do nothing for you. A Lombok-style-hacking processor can. You want to make

@MyAnnotation
private static final int MY_CONSTANT;

work like

private final static int MY_CONSTANT =
    Properties.getInteger(ThisClass.class, "MY_CONSTANT");

The original expression doesn't compile (due to the uninitialized final variable), but it parses fine and Lombok can do its job. There's already something related there:

  • @Value changes the modifiers to final private
  • @UtilityClass makes all fields static

So actually, you could write just

@MyAnnotation
int MY_CONSTANT;

and let your annotation change also the modifiers. I'd look at the eclipse and javac handlers for @UtilityClass, I guess all you need is to generate the initializer (which is quite some work because it's all damn complicated).

I don't think Lombok itself will implement this anytime soon, since

  • all the static stuff is non-testable and mostly bad style
  • and not everyone wants this in their code
  • it's not that much boilerplate
  • it also magically refers to the class Properties, but this could be solved via configuration

but I guess a contribution might be accepted.

like image 68
maaartinus Avatar answered Sep 28 '22 17:09

maaartinus