I'm using a FileHandler class that gets a File as a ctor parameter.
I want to use it with 2 different Files.
How can configure the injector to inject a different File each time?
Like getInstance with annoation that will return the desired concrete instance?
Annotation wouldn't help here as If I put it in the ctor, there will still be one annotation for all the usages of this ctor.
public class StringFileWriter implements IResponsesStorage {
File file;
#Inject
#Singleton
public StringFileWriter(#myAnnotation File file) {
this.file = file;
}
and
public class MainModule extends AbstractModule {
#Override
protected void configure() {
File resultBaselineFile = new File(Constants.RESULTS_BASELINE_FILE);
bind(File.class).annotatedWith(ResultBaselineFile.class).toInstance(resultBaselineFile);
File logLatencyFile = new File(Constants.LATENCY_FILE);
bind(File.class).annotatedWith(LatencyLogFile.class).toInstance(logLatencyFile);
edit
the main code calls 2 repositories: one for log files, second for result files. Each repository has it's own file handler. That's the problem
This is a variation on the so-called "robot legs" problem. As the comment above asks, the right solution really depends on where the decision is made.
Here's how I would solve this problem in this case:
public final class StringFileWriter implements IResponsesStorage {
private final File file;
// Note: constructor is not annotated with #Inject
StringFileWriter(File file) {
this.file = file;
}
// ...
}
public final class StringFileWriterModule extends AbstractModule {
#Override protected void configure() {}
#Provides
#Singleton
#ResultBaselineFile
StringFileWriter provideResultBaselineFileWriter(
#ResultBaselineFile File resultBaselineFile) {
return new StringFileWriter(resultBaselineFile);
}
#Provides
#Singleton
#LatencyLogFile
StringFileWriter provideLatencyLogFileWriter(
#LatencyLogFile File latencyLogFile) {
return new StringFileWriter(latencyLogFile);
}
}
And then at the point of injection you would choose which StringFileWriter you wanted:
final class MyWorkerClass {
private final StringFileWriter resultBaselineFileWriter;
#Inject MyWorkerClass(#ResultBaselineFile StringFileWriter writer) {
resultBaselineFileWriter = writer;
}
// ...
}
Related
Having the following classes:
public interface Step<C extends Config> {
void setConfig(C config);
}
and
public class ValidationStep implements Step<ValidationConf> {
public void setConfig(ValidationConf conf) {}
// implementation
}
and
public class ProcessStep implements Step<ProcessConf> {
public void setConfig(ProcessConf conf) {}
// implementation
}
and
public interface Config {
Class<? extends Step> type();
}
and
public class ValidationConf implements Config {
public Class<? extends Step> type() {
return ValidationStep.class;
}
}
and
public class ProcessConf implements Config {
public Class<? extends Step> type() {
return ProcessStep.class;
}
}
so, the application needs to dynamically instantiate Step subclasses objects, set the configuration accordingly and run the step, like this.
List<Config> configs = loadConfigsFromRepository(); // contain all subtypes of Config
for (Config conf: configs) {
Step<? extends Config> step = conf.type().getDeclaredConstructor().newInstance();
step.setConfig(conf); // compiler complains
}
Error message:
"The method setConfig(capture#8-of ? extends Config) in the type
Step<capture#8-of ? extends Config> is not applicable for the
arguments (Config)".
Checking the documentation, looks like Java won´t be friendly in this case:
https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html
What are the possible solutions to overcome this code restriction step.setConfig(conf);?
EDITED [SOLUTION]
Code can be viewed here: https://github.com/danieldestro/cucumber-salad/tree/generics/src/main/java/my/generics
Because Step.setConfig( Config ) is a „consumer“, one way to resolve the „is not applicable for the arguments (Config)“ error you get is to use a lower bound like I demonstrate here…
…
List< ? extends Config > configs = loadConfigsFromRepository( ); // contain all subtypes of Config
for ( Config conf: configs ) {
Step< ? super Config > step = conf.type( ).getDeclaredConstructor( ).newInstance( );
step.setConfig( conf ); // *set* makes Step a „consumer“
}
…
That way you don't need the cast that the other answer proposes.
My loadConfigsFromRepository( ) is implemented like…
static List< ? extends Config > loadConfigsFromRepository(){
return of( new ValidationConf( ), new ProcessConf( ) );
}
Get rid of the wildcard. You don't need it.
Step<Config> step = (Step<Config>) conf.type().getDeclaredConstructor().newInstance();
Your approach is not fully correct. I reccomend you not to use Reflections until you really need it.
Pay attention, that all these implementation should be hidden inside the package and only Step interface should be public. Config implementation holds all data to create Step class, so just delegate it to this.
package steps
public interface Step<C extends Config> {
void run(Context context);
}
private final class ValidationStep implements Step<ValidationConf> {
private final ValidationConf config;
public ValidationStep(ValidationConf config) {
this.config = config;
}
}
private class ProcessStep implements Step<ProcessConf> {
private final ProcessConf config;
public ValidationStep(ProcessConf config) {
this.config = config;
}
}
public interface Config {
Step<? extends Config> createStep();
}
private class ValidationConf implements Config {
public Step<ValidationConf> createStep() {
return new ValidationStep(this);
}
}
private class ProcessConf implements Config {
public Step<ValidationConf> createStep() {
return new ProcessConf(this);
}
}
package foo
List<Config> configs = loadConfigsFromRepository();
for (Config config : loadConfigsFromRepository()) {
config.createStep().run(context);
}
I just started looking at Guice for a new project. I have something like this
the ConfigImpl class ans Config interface
interface Config{...}
class ConfigImpl implements Config {
private static final Map<> propMap;
public ConfigImpl(Map<> propMap) {
this.propMap = someProps;
}
}
Guice injection I came up with
public class MyInjector extends AbstractModule {
protected void configure() {
bind(Config.class).to(ConfigImpl.class)
}
}
and finally
public SomeClass {
Config someConfig;
Injector injector = Guice.createInjector(new MyInjector());
someConfig = injector.getInstance(Config.class);
}
Now I am very confused as I can't find a way to pass propMap into ConfigImpl class. I'd like to know the proper way of doing it in Guice. Thanks!
You should inject propMaps from your module:
public class MyInjector extends AbstractModule {
private final Map<String,String> mapProps;
public MyInjector(Map<String,String> mapProps) {
this.mapProps = mapProps;
}
protected void configure() {
bind(Config.class).to(ConfigImpl.class).in(Scope.SINGLETON); // You most than likely want this
bind(new TypeLiteral<Map<String,String>>() {}).toInstance(mapProps); // binding for the map.
}
}
And use it like this:
public class SomeClass {
void doSomething() {
Map<String,String> mapProps = ... ;
Injector injector = Guice.createInjector(new MyInjector(mapProps));
Config someConfig = injector.getInstance(Config.class);
}
}
Also, you should fix your ConfigImpl class:
class ConfigImpl implements Config {
private final Map<String,String> propMap;
#Inject // mandatory since you use a non-default constructor
public ConfigImpl(Map<String,String> propMap) { // add the generic type of the map
this.propMap = propMap;
}
}
Im trying to build some ConfigurationService which will supply configuration with some injections.I want to make one of the property to be volatile while running.
Id like to achive something like this but I don 't know how to do this in Guice.
Guice module:
public class ConfModule extends AbstractModule {
protected void configure() {
// how to bind class to itself, without any specific implementation
bind(Property1.class).to(Property1.class);
bind(IProperty2.class).to(Iproperty2DefaultImplementation1.class);
bind(IProperty2.class).annotatedWith(Names.named("setterOfproperty2").to( ? ? ? );
}
}
Conf class:
public class Conf {
#Inject // just consatant injected property1.class
private Property1 property1;
// variable property impl of interface
private IProperty2 property2;
// default impl of interface from module binding
#Inject
public Conf(IProperty2 property2) {
this.property2 = property2;
}
// method for changing implementation
#Inject
public setIProperty2(#Named("setterOfproperty2") IProperty2 property2) {
this.property2 = property2;
}
}
Conf service class:
public class ConfService {
#Inject
Conf configuration;
public Conf getConf() {
return configuration;
}
}
Usage:
public static main() {
// return Conf with default IProperty2 impl
Injector injector = Guice.createInjector(new ConfModule());
ConfService c = injector.getInstance(ConfService.class);
// change to other Iproperty2Implementation
c.getConf().setIProperty2(new Iproperty2Implementation2());
}
Would anyone want to help me ?
I use Spring Framework. I want to make service for create an archive. I have two types of archive. I would like to know another way how to do it not using if-else statement.
Interface
public interface Archive {
public String makeArchive();
}
Implements 1
#Component
public class ArchiveRAR implements Archive {
#Override
public String makeArchive() {
return "I made archive RAR.";
}
}
Implements 2
#Component
public class ArchiveZIP implements Archive {
#Override
public String makeArchive() {
return "I made archive ZIP.";
}
}
Service
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
#Qualifier("archiveRAR")
Archive archiveRAR;
#Autowired
#Qualifier("archiveZIP")
Archive archiveZIP;
#Override
public void run(String... strings) throws Exception {
Report report = new Report("ZIP");
//HERE
if ("RAR".equals(report.getType())) {
System.out.println(archiveRAR.makeArchive());
} else if ("ZIP".equals(report.getType())) {
System.out.println(archiveZIP.makeArchive());
}
}
}
MainApp
#SpringBootApplication
public class MainApp {
public static void main(String[] args) throws Exception {
SpringApplication.run(MainApp.class, args);
}
}
Thank you.
I suggest you to use here factory pattern instead of if-else statement:
#Component
public class ArchiveFactory {
#Autowired
private Map<String, Archive> archives;
public Archive getArhive(String archiveType) {
return archives.get(archiveType);
}
}
By this way your main method will be:
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
private ArchiveFactory archiveFactory;
#Override
public void run(String... strings) throws Exception {
Report report = new Report("ZIP");
Archive archive = archiveFactory.get(report.getType());
System.out.println(archive.makeArchive());
}
}
Of course you need also to define bean of type Map<String, Archive>.
One other way..
#Component("RAR")
public class ArchiveRAR implements Archive{...}
#Component("ZIP")
public class ArchiveZIP implements Archive {...}
And the service class to inject BeanFactory
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
BeanFactory beans;
#Override
public void run(String... strings) throws Exception {
String archiveType = "ZIP/RAR";
Archive archive = beans.getBean(archiveType,Archive.class);
archive.makeArchive();
}
There is a cleaner way to do it without basically if-else and String comparison if you can decide beforehand which one you would like to use: just pass the appropriate Archive reference to the Report bean when you create it and also define a method that will generate your archive:
#Component
class Report {
private Archive archive;
// declare that Archive dependency is required
#Autowired
public Report(#Qualifier("archiveRar") Archive archive) {
// or public Report(#Qualifier("archiveZip") Archive archive) {
// depending on which one you would like to inject and use
this.archive = archive;
}
public void generateArchive() {
archive.makeArchive();
}
}
Also annotate your 2 Archive implementations with meaningful qualifier names:
#Component
#Qualifier("archiveRar")
public class ArchiveRAR implements Archive {
#Override
public String makeArchive() {
return "I made archive RAR.";
}
}
and
#Component
#Qualifier("archiveZip")
public class ArchiveZIP implements Archive {
#Override
public String makeArchive() {
return "I made archive ZIP.";
}
}
The Service will now look simpler:
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
private Report report;
#Override
public void run(String... strings) throws Exception {
report.generateArchive();
}
}
If you need run-time injection, you can get rid of the if-else but not String value qualifier for the bean, for example by a accessing the Spring bean container using BeanFactory.
Keep the above code for Archive and its implementations and delete the Report class which will no longer be used. In the Service use this:
#Service
public class RunnerApp implements CommandLineRunner {
#Autowired
private BeanFactory beanFactory;
#Override
public void run(String... strings) throws Exception {
String qualifierValue = "archiveRar";
// or String qualifierValue = "archiveZip";
Archive archive = beanFactory.getBean(qualifierValue, Archive.class);
System.out.println(archive.makeArchive());
}
}
New to Guice, so I'm looking into its expressive power. Suppose I have classes as follows:
public class Data {
#Inject
public Data(#Named("First") String first, #Named("Second") String second) { ... }
}
public class DataUser1 {
#Inject
public DataUser1(Data data) { ... }
}
public class DataUser2 {
#Inject
public DataUser2(Data data) { ... }
}
How do I create a module such that when I call injector.getInstance(DataUser1.class) I get something equivalent to new DataUser1(new Data("foo", "bar")) while having injector.getInstance(DataUser2.class) I get something equivalent to new DataUser2(new Data("foo2", "bar2"))?
Also related, how do I create a module for which I may need to get two instances of DataUser1, each of which using different Data values?
You use private modules for creating graphs of objects which are almost the same but differ in particular details.
public class DataUser1Module extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo");
bindConstant().annotatedWith(Names.named("Second")).to("bar");
bind(Data.class);
bind(DataUser1.class);
expose(DataUser1.class);
}
}
public class DataUser2Module extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo2");
bindConstant().annotatedWith(Names.named("Second")).to("bar2");
bind(Data.class);
bind(DataUser2.class);
expose(DataUser2.class);
}
}
Injector injector = Guice.createInjector(new DataUser1Module(), new DataUser2Module());
DataUser1 dataUser1 = injector.getInstance(DataUser1.class);
DataUser2 dataUser2 = injector.getInstance(DataUser2.class);
You do the same thing if you need two instances of DataUser1 with different Datas, but you use annotations to differentiate between them:
public class DataUser1Module1 extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo");
bindConstant().annotatedWith(Names.named("Second")).to("bar");
bind(Data.class);
bind(DataUser1.class).annotatedWith(Names.named("1")).to(DataUser1.class);
expose(DataUser1.class).annotatedWith(Names.named("1"));
}
}
public class DataUser1Module2 extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo2");
bindConstant().annotatedWith(Names.named("Second")).to("bar2");
bind(Data.class);
bind(DataUser1.class).annotatedWith(Names.named("2")).to(DataUser1.class);
expose(DataUser1.class).annotatedWith(Names.named("2"));
}
}
Injector injector = Guice.createInjector(new DataUser1Module1(), new DataUser1Module2());
DataUser1 dataUser11 = injector.getInstance(Key.get(DataUser1.class, Names.named("1"));
DataUser1 dataUser12 = injector.getInstance(Key.get(DataUser1.class, Names.named("2"));
This pattern is described in Guice FAQ.
See also these questions:
Binding a constructor argument based on the Annotation of the class
How do I bind Different Interfaces using Google Guice?