One dependency in multiple services classes with Dagger 2 - java

I want to inject a custom class as an dependency in different service classes, but I don't get it work. It always ends with a NPE. Here is my example (simple Java SE) ...
My Main class to get everything running
public class Main {
public static void main(String[] args) throws IOException, TimeoutException {
MyApplication MyApp = new MyApplication();
MyApp.execute();
}
}
MyApplication class
public class MyApplication {
private MyApplicationComponent appComponent;
#Inject FooService fooService;
#Inject BarService barService;
#Inject BazService bazService;
public MyApplication() {
component = DaggerMyApplicationComponent.builder().build();
component.inject(this);
}
public void execute() {
fooService.doStuff();
barService.doStuff();
// this will happen in the FooService construct, see below
// bazService.doStuff();
}
}
Component and Module classes as defined in Dagger, without using it the #Inject constructor way
#Singleton
#Component(modules = {MyApplicationModule.class})
public interface MyApplicationComponent {
void inject(MyApplication application);
}
#Module
public class MyApplicationModule {
#Singleton
#Provides
FooService provideFooService() {
return new FooService();
}
#Singleton
#Provides
BarService provideBarService() {
return new BarService();
}
#Provides
BazService provideBazService() {
return new BarService();
}
}
Using the MyApplicationModule and MyApplicationComponent to provide needed dependencies works within the Main.class. I also want to use the BazService within the FooService class. Therefore I use the #Inject way to define it as a dependency with FooService.class.
Using #Inject of BazService within the FooService.class
public class FooService {
#Inject BazService bazService;
public FooService(){}
public doStuff(){
bazService.doStuff();
}
}
Running the Main.class always ends within a NPE, due to undefined bazService in the FooSerivce class. I don't think, that I missed to add an annotation anywhere. I think Dagger will not work this way ... any ideas?

FooService expects bazService to be injected though field injection but you are calling bazService before this happens. If you want to call bazService.doStuff() in FooService's constructor you'll have to use constructor injection.

Related

Spring Boot: how to inject dependencies into a class called by a library?

I'm using Kinesis Client Library (KCL) and Spring boot. To use KCL, I have to implement a class (I named it RecordProcessor) for interface IRecordProcessor. And KCL will call this class and process records from kinesis. But when I tried to use dependency injection, I found it was not succeeded.
Here's the snippet for RecordProcessor:
#Component
public class RecordProcessor implements IRecordProcessor {
#Autowired
private SingleRecordProcessor singleRecordProcessor;
#Override
public void initialize(String shardId) {
...
}
#Override
public void processRecords(List<Record> records, IRecordProcessorCheckpointer checkpointer) {
...
}
}
I use Class SingleRecordProcessor to process single each record from kinesis. And this is my SingleRecordProcessor class snippet:
#Component
public class SingleRecordProcessor {
private Parser parser;
private Map<String, Table> tables;
public SingleRecordProcessor() {
}
#Autowired
private void setParser(Parser parser) {
this.parser = parser;
}
#Autowired
private void setTables(Map<String, Table> tables) {
this.tables = tables;
}
public void process(String record) {
...
}
}
I want to let spring framework automatically inject the SingleRecordProcessor instance into the class and use it. But I found that the field singleRecordProcessor is null.
Any idea why the dependency injection is failed? Or is it impossible to inject dependencies into a class which is called by other framework (in this case it's KCL)? Any suggestions will be appreciated! Really need some help please!!
[UPDATE]:
Sorry for not expressing the error clearly. The error was NullPointerException. I tried to inject singleRecordProcessor and call method process() on it. I think the injection was not successful so the instance singleRecordProcessor is null and there comes the NullPointerException.
More information is as follows:
I have a major class called Application
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./app.pid"));
ConfigurableApplicationContext ctx = application.run(args);
}
}
And I have the MainProcessor class which will call KCL.
#Service
public final class MainProcessor {
#EventListener(ApplicationReadyEvent.class)
public static void startConsumer() throws Exception {
init();
IRecordProcessorFactory recordProcessorFactory = new RecordProcessorFactory();
Worker worker = new Worker(recordProcessorFactory, kinesisClientLibConfiguration);
...
worker.run(); // this line will call KCL library and eventually call ProcessorRecord class.
}
}
[UPDATE2]
RecordProcessorFactory only has one method like this
#Component
public class RecordProcessorFactory implements IRecordProcessorFactory {
#Autowired
RecordProcessor recordProcessor;
#Override
public IRecordProcessor createProcessor() {
return recordProcessor;
}
}
It creates a new RecordProcessor instance for KCL to use it.
You should autowire an instance of this into your MainProcessor:
#Component
public class RecordProcessorFactory {
#Lookup IRecordProcessor createProcessor() { return null; }
}
Spring will instantiate a RecordProcessorFactory for you, and replace the implementation of createProcessor() in it with one that will return a new IRecordProcessor each time it's called. Both the factory and the processors will be Spring beans - which is what you want.

Dagger 2 single instance of MyClass

I'm trying to refactor my code so I was thinking about Dagger2 to solve my issues. I've created AppComponent to store all of my Singletons:
#AppScope
#Component(
modules = {
AppModule.class,
// more here...
}
)
public interface AppComponent {
Context exposeContext();
CmdComponent newCmdComponent(CmdModule module);
// ... few injections here
}
My AppModule:
#Module
public class AppModule {
private Context context;
public AppModule(Context context) {
this.context = context;
}
// ... provide appContext etc.
#AppScope #Provides
MyClass provideMyClass() {
Log.i("DAGGER", "provideMyClass: ");
return new MyClass();
}
}
I inject this in my Application class:
public class App extends Application {
private static AppComponent component;
#Override
public void onCreate() {
super.onCreate();
component = DaggerAppComponent.builder()
.appModule(new AppModule(app))
.build();
}
public static AppComponent getAppComponent() {
return component;
}
}
Then I have my subcomponent CmdComponent with different #Scope
#CmdScope
#Subcomponent(
modules = {
CmdModule.class
}
)
public interface CmdComponent {
void inject(Cmd cmd);
}
Now I'm injecting dependancies into my Cmd instance like:
#Inject MyClass myClass;
public Cmd() {
App.getAppComponent()
.newCmdComponent(new CmdModule())
.inject(this);
}
Unfortunetly log: Log.i("DAGGER", "provideMyClass: "); and log inside MyClass constructor are shown multiple times... so I get new instance of MyClass every time. How to tell Dagger to give me the same instance (created once) every time?
Ok. I solve my issue. The solution is simple. My AppScope was created wrong. For some reason, I thought that annotation works like inheritance.
My custom annotation was like:
#Singleton
#Retention(RetentionPolicy.RUNTIME)
public #interface AppScope {
}
and Dagger thought that my component is unscoped.. It should be like:
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface AppScope {
}
How about the .newCmdComponent(new CmdModule()). Why do you do that, creating a new module every time? The component already has a module attached, you've set that on creation of the component.

Dagger 2 LocalDataSource cannot be provided without #Provides annotated method

I'm trying to follow the same example as in here by creating a Repository pattern with DI.
The problem is that I get the following error:
"Error:(16, 20) error: #mvp.model.di.scope.Local
mvp.model.repository.local.GameLocalDataSource cannot be provided
without an #Provides-annotated method. #mvp.model.di.scope.Local
mvp.model.repository.local.GameLocalDataSource is injected at
mvp.model.repository.GameRepository.(gameLocalDataSource, …)
mvp.model.repository.GameRepository is provided at
mvp.model.di.component.RepositoryComponent.getGameRepository()"
Here's the code related to the app:
public class GameApplication extends Application {
private RepositoryComponent repositoryComponent;
#Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
repositoryComponent = DaggerRepositoryComponent.builder()
.applicationModule(new ApplicationModule((getApplicationContext())))
.build();
}
public RepositoryComponent getRepositoryComponent() {
return repositoryComponent;
}
}
This is my RepositoryComponent:
#Singleton
#Component(modules = {RepositoryModule.class, ApplicationModule.class})
public interface RepositoryComponent {
GameRepository getGameRepository();
}
Here's the RepositoryModule:
#Module
public class RepositoryModule {
#Singleton
#Provides
#Local
GameDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
#Singleton
#Provides
#Remote
GameDataSource provideRemoteDataSource() {
return new GameRemoteDataSource();
}
}
And finally, the ApplicationModule:
#Module
public final class ApplicationModule {
private Context context;
public ApplicationModule(Context context) {
this.context = context;
}
#Provides
Context providesContext() {
return context;
}
}
Here's most of my GameRepository class:
#Singleton
public class GameRepository implements GameDataSource {
private GameDataSource remoteDataSource;
private GameDataSource localDataSource;
#Inject
public GameRepository(#Local GameLocalDataSource gameLocalDataSource, #Remote GameRemoteDataSource gameRemoteDataSource) {
remoteDataSource = gameRemoteDataSource;
localDataSource = gameLocalDataSource;
}
Also, as in the mentioned example, I created a couple of scopes, #Local and #Remote since my two data sources have the same type and Dagger needs to differentiate them.
#Qualifier
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface Local {
}
The rest of the code I have related to dagger, is just the #Inject in the constructors where I want to inject my dependencies.
Also, the DaggerRepositoryComponent is never generated in the GameApplication class.
Thanks a lot for the help in advance!
GameLocalDataSource cannot be provided without an #Provides-annotated method
Somewhere in the code you are trying to #Inject GameLocalDataSource, but you have specified in your module how to provide GameDataSource, not GameLocalDataSource.
...
GameDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
...
Either ask Dagger to inject GameDataSource, or describe Dagger how to provide GameLocalDataSource.
...
GameLocalDataSource provideLocalDataSource(Context context) {
return new GameLocalDataSource(context);
}
...

How can I inject member annotations into my current instantiated object using Guice?

I am trying to inject a custom annotation using the Guice bindInterceptor into my currently instantiated Service.java class. Unfortunately when I call myMethod() the OnAnnotationEvent::invoke method is not called. How can I use Guice to call OnAnnotationEvent::invoke when the #OnAnnotation annotation tag is used on a method in the current class?
My code looks like this:
Service.java
//Instantiated by another service
public class Service extends AbstractVerticle {
private DataAccess dataAccess;
#Inject
public void setDataAccess(DataAccess dataAccess){
this.dataAccess = dataAccess;
}
#Override
public void start() throws Exception {
Guice.createInjector(new DataAccessModule()).injectMembers(this);
myMethod();
}
#MyAnnotation
public void myMethod() {
dataAccess.doStuff();
}
}
DataAccessModule.java
public class DataAccessModule extends AbstractModule {
#Override
protected void configure() {
OnAnnotationEvent onAnnotationEvent = new OnAnnotationEvent();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(MyAnnotation.class), onAnnotationEvent);
bind(DataAcess.class).to(DataAccessImpl.class);
}
}
OnAnnotationEvent
public class OnAnnotationEvent implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Annotation called on: " + invocation.getMethod().getName();
return invocation.proceed();
}
}
MyAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface MyAnnotation {}
I think that your problem is that you creating new injector that does not knows anything about your class. If you just need injector in your class - use #Inject private Injector injector;. If you need to load some aditional modules locally you just need to create child injector :
#Inject private baseInjector;
...
injector = baseInjector.createChildInjector(new Module1(),new Moddule2());
This doesn't work because your Service instance isn't managed by Guice. To make it work you must either create Service with Guice or annotate method doStuff in DataAccessImpl with MyAnnotation.

javax inject on a Resteasy service not working on Jetty

I have this Restful service:
#Service
public class MyRESTServiceImpl implements MyRESTService {
#Inject
private Helper helper;
public MyRESTServiceImpl() {
}
#Override
public List<Something> getThings() {
return helper.getThings(); // NPE here!
}
}
The problem here is that when getThings is accessed through /api/getThings, although the method is invoked the helper is not injected. Whereas on the other parts of the app it injected properly. I am missing some annotation for this MyRESTServiceImpl?

Categories