I want to make a binding using a method annotated with #Provides into an eager singleton. I've found bug 216, which suggests this isn't possible, but doesn't mention the #Provides annotation explicitly.
I currently have a class that requests the eager singletons in time by itself being a singleton, but it's not a very nice solution.
public class LogicModule extends AbstractModule {
#Override public void configure() {
bind(SomeDep.class);
bind(MyWorkaround.class).asEagerSingleton();
}
// cannot add eager requirement here
#Provides #Singleton Logic createLogic(SomeDep dep) {
return LogicCreator.create(dep);
}
private static class MyWorkaround {
#Inject Logic logic;
}
}
Can I change something near the comment that would make the workaround class obsolete?
Why not to use
bind(Logic.class).toInstance(LogicCreator.create(dep));
//ohh we missing dep
then we can do this
class LogicProvider implements Provider<Logic> {
private final SomeDep dep;
#Inject
public LogicProvider(SomeDep dep) {
this.dep = dep;
}
#Override
public Logic get() {
return LogicCreator.create(dep);
}
}
and then
bind(Logic.class).toProvider(LogicProvider.class).asEagerSingleton();
You can even pass SomeDep dep to your provider as Provider<SomeDep> and then call providerDep.get() in LogicCreator.create() that would be a bit more robust.
Related
Heres my current setup
Class file
public class ToyAdapter {
private final ToyClient toyClient;
private final Retryer retryer;
#Inject
public APIAdapter(final ToyClient toyClient,
#Named("toyRetryer") final Retryer retryer) {
this.toyClient = toyClient;
this.retryer = retryer;
}
Guice file
I have several guice modules, but this one pertains to the above class
public class ToyModule extends AbstractModule {
#Override
protected void configure() {
bind(ToyAdapter.class).in(Singleton.class);
bind(Retryer.class).annotatedWith(Names.named("toyRetryer")).toInstance(getToyRetryer());
}
#Provides
#Singleton
public ToyClient getToyClient(...){
...
}
private Retryer getToyRetryer() {#Takes no arguments
return RetryerBuilder...build();
}
}
So far this works great! However, now my retryer requires a LogPublisher object provided in another module.
I'm trying
public class ToyModule extends AbstractModule {
LogPublisher logPublisher;
#Override
protected void configure() {
requestInjection(logPublisher);
bind(ToyAdapter.class).in(Singleton.class);
bind(Retryer.class).annotatedWith(Names.named("toyRetryer")).toInstance(getToyRetryer());
}
private Retryer getToyRetryer() {
return RetryerBuilder.withLogPublisher(logPublisher).build();
}
}
LogPublisher is provided in another guice module which has alot of other objects that depend on LogPublisher so I'd rather not just merge everything into one giant guice module.
#Provides
#Singleton
public LogPublisher getLogPublisher() {...}
Is this the proper way to do this? I'm getting Java findBugs errors saying unwritten field so I'm thinking I'm doing it wrong.
Declare your Retryer with help of #Provides/#Named annotations.
#Provides
#Singleton
#Named("toyRetryer")
public Retryer getToyRetryer(LogPublisher logPublisher) {
return RetryerBuilder.withLogPublisher(logPublisher).build();
}
I have a question about SOLID design principles in the context of Spring dependency injection with generic type usage. I have the next generic interface:
public interface EmailChecker<T> {
boolean check(T message);
}
Then I have two implementations: one for production and one for the staging environment
#Profile("!production")
#Component
public class EmailRequestCheckerStaging implements EmailChecker<EmailRequest> {
#Override
public boolean check(EmailRequest message) {
//TODO: some code here;
return result;
}
}
#Profile("production")
#Component
public class EmailRequestCheckerProduction implements EmailChecker<EmailRequest> {
#Override
public boolean check(EmailRequest message) {
//TODO: some code here;
return result;
}
}
And finally over here is a dependency injection of 'emailChecker' field:
#Service
public class Receiver {
#Autowired
private EmailChecker<EmailRequest> emailChecker;
public void receiveMessage(EmailRequest dto) {
if(emailChecker.check(dto)) {
//TODO: some logic here
}
}
}
Question: does such dependency injection follows all SOLID principles?
Note: I have multiple implementations of 'EmailChecker' interface with different types (for simplicity I described 'EmailRequest' DTO implementations)
While opinion based, the injected dependency should be explicit via constructor injection.
#Service
public class Receiver {
private EmailChecker<EmailRequest> emailChecker;
#Autowired //could actually be omitted since class has only one constructor
public Receiver(EmailChecker<EmailRequest> emailChecker) {
this.emailChecker = emailChecker;
}
public void receiveMessage(EmailRequest dto) {
if(emailChecker.check(dto)) {
//TODO: some logic here
}
}
}
So that consumers of the Receiver class are aware of what it needs in order for it to function correctly
Classes with explicit dependencies are more honest about what they need. They state very clearly what they require in order to perform their particular function.
I have class A which is taking a set as guice dependency. The set is singleton. Below is the code example:
class A
{
private Set<InetAddress> set;
private String pingUriPath;
#Inject
public A(Set<InetAddress> set, #Named("pingUri") String pingUriPath)
{
this.set = set;
this.pingUriPath = pingUriPath; // this is used somewhere
}
public void storeValue(String str)
{
if(str.equals("abc"))
{
set.add(str);
}
}
}
Here is the guice module that injects dependency:
private class GuiceModule extends AbstractModule {
#Override
public void configure() {
bindConstant().annotatedWith(Names.named("pingUri")).to("/ping");
}
#Provides
#Singleton
Set<InetAddress> healthyTargets(){
return Sets.newConcurrentHashSet();
}
}
I want to mock the method storeValue and for that i have to mock the set. I am not able to mock the set using guice.
If i mock like below, it gives assertion error(no interactions with this mock)
#Mock
Set<InetAddress> mockHealthyTargets;
private class MockClassesModule extends AbstractModule {
#Override
public void configure() {
bindConstant().annotatedWith(Names.named("pingUri")).to("/ping");
}
#Provides
#Singleton
Set<InetAddress> healthyTargets(){
return Sets.newConcurrentHashSet();
}
}
public test_storeValue()
{
Injector injector = Guice.createInjector(new MockClassesModule());
A a = injector.getInstance(A.class);
a.storeValue("abc");
verify(mockHealthyTargets).add("abc")
}
If you have the need to use guice in your unit tests, something is most likely going the wrong direction. One of the biggest benefits of dependency injection is that testing becomes easy, because you can pass dependencies that are controlled by you.
I assume you want to test the class A and specifically the method storeValue. For this you don't even need mocking
#Test
public void test() {
// prepare dependencies
Set<InetAddress> set = Sets.newConcurrentHashSet();
String pingUri = "example.com";
// prepare input
String input = "someValue";
// prepare class under test
A classUnderTest = new A(set, pingUri);
// call class under test
classUnderTest.storeValue(input);
// check that what you expected happened
// in this case you expect that the dependency set now contains the input
assertThat(set, contains(input));
}
I have found what the mistake was, I should return mock when providing to my unit test. It should look like this:
#Mock
Set<InetAddress> mockHealthyTargets;
private class MockClassesModule extends AbstractModule {
#Override
public void configure() {
bindConstant().annotatedWith(Names.named("pingUri")).to("/ping");
}
#Provides
#Singleton
Set<InetAddress> healthyTargets(){
return mockHealthyTargets;
}
}
For a dagger2 module
#Module
public class MyModule {
#Provides #Singleton public RestService provideRestService() {
return new RestService();
}
#Provides #Singleton public MyPrinter provideMyPrinter() {
return new MyPrinter();
}
}
We could have the test module as Test
public class TestModule extends MyModule {
#Override public MyPrinter provideMyPrinter() {
return Mockito.mock(MyPrinter.class);
}
#Override public RestService provideRestService() {
return Mockito.mock(RestService.class);
}
}
However if for a class as below that is not declared in the dagger module...
public class MainService {
#Inject MyPrinter myPrinter;
#Inject public MainService(RestService restService) {
this.restService = restService;
}
}
How do I create a mock of MainService as above.
Note, I'm not planning to perform test for MainService as per share in https://medium.com/#fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-rule-c8487ed01b56#.9aky15kke, but instead, my MainService is used in another normal class that I wanted to test. e.g.
public class MyClassDoingSomething() {
#Inject MainService mainService;
public MyClassDoingSomething() {
//...
}
// ...
public void myPublicFunction() {
// This function uses mainService
}
}
This is definitely not answering your question, but in my honest opinion it is related, it's helpful and too big for a comment.
I'm often facing this question and I end always doing "Constructor dependency injection". What this means is that I no longer do field injection by annotating the field with #Inject but pass the dependencies in the constructor like so:
public class MyClassDoingSomething implements DoSomethig {
private final Service mainService;
#Inject
public MyClassDoingSomething(Service mainService) {
this.mainService = mainService;
}
}
Notice how the constructor now receives the parameter and sets the field to it and is also annotated with #Inject? I also like to make these classes implement an interface (also for MyService) - Amongst several other benefits I find it makes the dagger module easier to write:
#Module
public class DoSomethingModule {
#Provides #Singleton public RestService provideRestService() {
return new RestService();
}
#Provides #Singleton public MyPrinter provideMyPrinter() {
return new MyPrinter();
}
#Provides #Singleton public Service provideMyPrinter(MyService service) {
return service;
}
#Provides #Singleton public DoSomethig provideMyPrinter(MyClassDoingSomething something) {
return something;
}
}
(This assumes that MyService implements or extends Service)
By now it seems you already know that dagger is able to figure out the dependency graph by itself and build all the objects for you. So what about unit testing the class MyClassDoingSomething? I don't even use dagger here. I simply provide the dependencies manually:
public class MyClassDoingSomethingTest {
#Mock
Service service;
private MyClassDoingSomething something;
#Before
public void setUp() throws Exception {
MockitoAnnotations.init(this);
something = new MyClassDoingSomething(service);
}
// ...
}
As you see, the dependency is passed through the constructor manually.
Obviously this doesn't work if you're coding something that doesn't have a constructor that can be invoked by you. Classical examples are android activities, fragments or views. There are ways to achieve that, but personally I still think you can somehow overcome this without dagger. If you are unit testing a view that has a field #Inject MyPresenter myPresenter, usually this field will have package access that works fine in the tests:
public class MyViewTest {
#Mock MyPresenter presenter;
private MyView view;
#Before
public void setUp() throws Exception {
MockitoAnnotations.init(this);
view.myPresenter = presenter;
}
}
Note that this only works if both MyViewTest and MyView are in the same package (which often is the case in android projects).
At the end of the day if you still want to use dagger for the tests, you can always create "test" modules and components that can inject by declaring methods in the component like:
#Inject
public interface MyTestComponent {
void inject(MyClassDoingSomething something);
}
I find this approach ok-ish, but throughout my development years I prefer the first approach. This also has reported issues with Robolectric that some setup in the build.gradle file is required to actually make the dagger-compiler run for the tests so the classes are actually generated.
I have 2 modules: AndroidModule for providing Context-related objects and PhotosModule providing PhotoManager.
I want to use PhotoManager in PhotosFragment. PhotoManager depends on DbManager, which is provided by AndroidModule.
How do I connect all the dots?
The code compiles, but a NPE is thrown on this line:
mDbManager.readDatabase();
meaning that the injection of DbManager into PhotoManager did not occur, even though I set the
injects = PhotoManager.class
in AndroidModule.
Also, mDbManager is not a private field, so I don't think I have to use injection on PhotoManager's constructor; field injection should be sufficient.
What's the reason and how do I satisfy the missing dependency? I assume there may be more than one problem with my code. Note: I just started with Dagger, please keep that in mind.
Application:
public class TestApp extends Application {
private ObjectGraph mObjectGraph;
#Override
public void onCreate() {
super.onCreate();
mObjectGraph = ObjectGraph.create(getModules().toArray());
}
protected List<Object> getModules() {
return Arrays.asList(new AndroidModule(this), new PhotosModule());
}
public void inject(Object object) {
mObjectGraph.inject(object);
}
}
AndroidModule:
#Module(library = true, injects = PhotoManager.class)
public class AndroidModule {
private final TestApp mApplication;
public AndroidModule(TestApp application) {
mApplication = application;
}
#Provides
#Singleton
#ForApplication
Context provideApplicationContext() {
return mApplication;
}
#Provides
#Singleton
DbManager provideDbManager() {
return new DbManager(mApplication);
}
}
PhotosModule:
#Module(injects = PhotosFragment.class)
public class PhotosModule {
#Provides
#Singleton
PhotoManager providePhotoManager() {
return new PhotoManager();
}
}
PhotoManager:
#Singleton
public class PhotoManager {
#Inject
DbManager mDbManager;
public void doSomething() {
mDbManager.readDatabase();
}
}
PhotosFragment:
public class PhotosFragment extends Fragment {
#Inject
PhotoManager mPhotoManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((TestApp) getActivity().getApplication()).inject(this);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
///...
mPhotoManager.doSomething();
}
}
DbManager:
#Singleton
public class DbManager {
public DbManager(Context context) {
//...
}
public void readDatabase() {
//...
}
}
Here's the core of the problem: in PhotosModule, you have the provider method:
PhotoManager providePhotoManager() {
return new PhotoManager();
}
You are newing up the PhotoManager yourself, so Dagger doesn't have an opportunity to inject its dependency (the DbManager).
I think it's because you're misunderstanding the meaning of the injects property on the #Module(injects=...) annotation of the AndroidModule. You have:
#Module(library = true, injects = PhotoManager.class)
But that injects isn't needed, because you're never calling objectGraph.inject on the PhotoManager. Instead, you're only injecting the PhotoFragment, which is correct...
So one way to fix the NPE problem:
remove the injects=PhotoManager.class from AndroidModule.java:
#Module(library = true)
change PhotosModule to include AndroidModule:
#Module(injects = PhotosFragment.class, includes = AndroidModule.class)
I avoid field injection using the #Inject annotation (only use it for the top-level objects, i.e. where you do the objectgraph.inject, such as activity and fragment level), and use constructor injection instead for all the other dependencies. So I'd change PhotoManager to inject the DbManager like this:
private final DbManager mDbManager;
public PhotoManager(DbManager dbManager) {
mDbManager = dbManager;
}
I've put up the code here on GitHub.