Guice injections on named string fields

26,340

Solution 1

Let me just summarize some of the comments already made here ...

  1. You forgot the @Inject Annotation
  2. I would strongly advise to keep Guice/Injector outside of FizzFuzz. Use the static main method to bootstrap your App (not run()).
  3. Binding String to a constant can easily be done via bindConstant.

Ths brings you to something like this:

public class FizzFuzz {
    @Inject
    @Named("red")
    private String service;

    public static void main(String[] args) {
        FizzFuzz fizzFuzz = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bindConstant().annotatedWith(Names.named("red")).to("red-service");
            }    
        }).getInstance(FizzFuzz.class);

        System.out.println(fizzFuzz.service);
    }
}

Solution 2

You've forgotten @Inject before @Named("Red"). Also using bindConstant() is prefferable for such things.

P.S. why do you receive String from inj instead of FizzBuzz?

Solution 3

It should be more pretty with my way.
Create first your annotation

@Retention(RetentionPolicy.RUNTIME)
public @interface InjectSetting {
     String value();
}

Create your guice module

@Slf4j
public class SettingModule extends AbstractModule {
    private final Properties properties;

    private SettingModule(Properties properties) {
        this.properties = properties;
    }

    @Override
    protected void configure() {
        binder().bindListener(Matchers.any(), listener(((type, encounter) -> {
            for (Field field : type.getRawType().getDeclaredFields()) {
                if (field.isAnnotationPresent(InjectSetting.class)) {
                    field.setAccessible(true);

                    encounter.register(injector(instance -> {
                        try {
                            Object value = properties.get(
                                    field.getAnnotation(InjectSetting.class).value());

                            field.set(instance, parse(value, field));
                        } catch (IllegalAccessException e) {
                            binder().addError(e);
                        }
                    }));
                }
            }
        })));
    }

    TypeListener listener(BiConsumer<TypeLiteral<?>, TypeEncounter<?>> consumer) {
        return consumer::accept;
    }

    MembersInjector<Object> injector(Consumer<Object> consumer) {
        return consumer::accept;
    }

    Object parse(Object value, Field field) {
        Type type = field.getType();

        if(type == boolean.class)
            value = Boolean.parseBoolean(value.toString());
        else if(type == int.class)
            value = Integer.parseInt(value.toString());

        return value;
    }

    public static Module of(String propertiesPath, String... more) {
        Properties properties = new Properties();

        try {
            properties.load(Files.newInputStream(Paths.get(propertiesPath, more)));
        } catch(Exception e) {
            log.error("can't load config file {}", propertiesPath);
            throw new RuntimeException(e);
        }

        return new SettingModule(properties);
    }
}

And then inject your field

@InjectSetting("database.port")
private int port;
Share:
26,340
IAmYourFaja
Author by

IAmYourFaja

my father is a principal at burgoyne intnl and got me this job programming lisp and development. I aspire to unittesting with a concentration in mobile platforms.

Updated on July 10, 2022

Comments

  • IAmYourFaja
    IAmYourFaja almost 2 years

    I have a class:

    public class FizzBuzz {
        @Named("Red") private String redService;
    
        public static void main(String[] args) {
            GuiceTest testApp = new GuiceTest();
    
            testApp.run();
        }
    
        private void run() {
            Injector inj = Guice.createInjector(new MyModule());
    
            redService = (String)inj.getInstance(String.class);
    
            // Should print "red-service" but is instead an empty string!
            System.out.println("redService = " + redService);
        }
    
        // ... Rest of class omitted for brevity
    }
    
    public class MyModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(String.class).annotatedWith(Names.named("Red")).toInstance("red-service");
        }
    }
    

    In my module I instruct Guice to bind all String.class instances @Named "Red" to the string instance "red-service", however I'm not seeing that in the outputted print statement. How am I using Guice incorrectly?