I'm starting to study modules, and I would like to try the "requires transitive". So I createad a module for the interface:
module interfacesModule {
exports com.my.interfaces;
}
with 1 interface:
package com.my.interfaces;
public interface MyInterface {
void go();
}
a module for implementation:
module javaModuleA {
requires interfacesModule;
exports com.my.moduleA;
}
class:
package com.my.moduleA;
import com.my.interfaces.MyInterface;
public class ClassA implements MyInterface {
public void go() {
System.out.println("in class A");
}
}
and the main module:
module mainModule {
requires transitive javaModuleA;
requires interfacesModule; // this line I want to comment, to use from requires transitive
}
class:
package com.my;
import com.my.interfaces.MyInterface;
import com.my.moduleA.ClassA;
public class Main {
public static void main(String[] args) {
System.out.println("main java module");
MyInterface a = new ClassA();
a.go();
}
}
This works fine!
Now if i comment the line "requires interfacesModule;" on my main module, should't it still work, as I have the "requires transitive" for javaModuleA?
When I comment it I get "The type com.my.interfaces.MyInterface is not accessible".
A requires transitive javaModuleA; declaration within mainModule implies that a hypothetical module having a requires mainModule declaration would automatically get a dependency as if having a declaration requires javaModuleA; too.
In other words, what you are trying to do, to get an implicit requires interfacesModule; for mainModule, needs a change in javaModuleA
module javaModuleA {
requires transitive interfacesModule;
exports com.my.moduleA;
}
Then, mainModule having a requires javaModuleA; or requires transitive javaModuleA; can use exported packages of interfacesModule without the need for a requires interfacesModule;.
The rationale is that you might have a javaModuleA containing both, the interface and the implementation, as a starting point and decide to refactor the module that has grown too big into javaModuleA and interfacesModule, without the need to adapt the dependent modules. Then, having requires transitive interfacesModule; in javaModuleA allows the modules depending on javaModuleA to continue to work, as if it still was the big module.
Related
I am trying to implement a simple modular application with a self written plugin loader. For that I created a gradle multiproject build. The main project is going to be the started application. In the subproject spi is a module containing the Plugin interface, while in the subproject simple-plugin I wanted to create a plugin to test the plugin loading.
The Plugin interface is very simple:
package io.github.zeroneca.orgascreen.spi;
public interface Plugin {
String getId();
}
module-info.java:
module io.github.zeroneca.orgascreen.spi {
exports io.github.zeroneca.orgascreen.spi;
}
SimplePlugin.java:
package io.github.zeroneca.orgascreen.plugins.simple;
import io.github.zeroneca.orgascreen.spi.Plugin;
public class SimplePlugin implements Plugin {
private final String id = "SimplePlugin";
#Override
public String getId() { return id; }
}
module-info.java
module io.github.zeroneca.orgascreen.plugins.simple {
requires io.github.zeroneca.orgascreen.spi;
provides io.github.zeroneca.orgascreen.spi.Plugin with io.github.zeroneca.orgascreen.plugins.simple.SimplePlugin;
}
Now in the class PluginLoader I'm loading all jars from a directory and want to add all Plugins to a List:
package io.github.zeroneca.orgascreen;
// several imports
public class PluginLoader {
private static final List<Plugin> PLUGINS = new ArrayList<>();
public static void load() {
URL[] pluginURLs = getPluginURLs(); // returns all URLs of jar files in a directory
URLClassLoader urlClassLoader = URLClassLoader.newInstance(pluginURLs);
ServiceLoader<Plugin> serviceLoader = ServiceLoader.load(Plugin.class, urlClassLoader);
for (Plugin plugin : serviceLoader) {
PLUGINS.add(plugin);
}
}
// some more methods hidden here
}
module-info.java
module io.github.zeroneca.orgascreen {
requires io.github.zeroneca.orgascreen.spi;
uses io.github.zeroneca.orgascreen.spi.Plugin;
}
Now after building with gradle and copying the resulting jar simple-plugin-0.1.jar into the directory, I can see, that the URL of the file is in pluginJars but the List PLUGINS remains empty. I'm not sure what I'm doing wrong, online searches didn't help, I cannot see a difference to working solution, but the use of gradle.
gradle 6.5.1 is in use and modularity.inferModulePath is set to true.
Help is greatly appreciated!
Note: when adding META-INF/services/io.github.zeroneca.orgascreen.spi.Plugin to the jar of SimplePlugin it works, so something must be wrong with the module declarations. But I just can't see what.
I'm trying to access a library that is implemented in a library that I implement. I own both these libraries. I.e.:
Lib1 has a certain class I want to access in the consumer app, say Car.java:
public class Car {
public Int wheelsCount;
}
Lib2 has an api which returns the class Car from lib1:
gradle.build:
...
api 'com.me.lib1:1.0' //From maven central (not currently live)
import com.lib1.Car
interface MyApi {
public Car getCar();
}
Consumer's gradle.build: implementation 'com.me.lib2:1.0'
MyApi api = getMyApi()
api.getCar() // Error
Cannot access class 'com.lib1.Car'. Check your module classpath for missing or conflicting dependencies
Question: Is there a way to do this using gradle?
I am not sure what you’re asking. Here is an example which I think should roughly do what you were talking about?
We have a gradle project with 3 subprojects, car, garage and app. car and garage provide libraries, whereas app is our application. Aside from these directories, the root directory contains only a settings.gradle with this content:
rootProject.name = 'example'
include 'car', 'garage', 'app'
Moving on, we have the car project which provides an interface. It’s build.gradle looks like this:
plugins {
id 'java-library'
}
Other than that, it only contains our interface at src/main/java/car/Car.java:
package car;
public interface Car {
void drive();
}
garage is a consumer for the car project, and provides a way to get new Cars. So we need to declare an api dependency on :car like this:
plugins {
id 'java-library'
}
dependencies {
api project(':car')
}
The Garage class itself looks like this (under src/main/java/garage/Garage.java:
package garage;
import car.Car;
public class Garage {
public Car getCar() {
return () -> { System.out.println("Hello World!"); };
}
}
Finally, we have our application which just needs an implementation dependency on garage to use it and a JavaExec task to run it:
plugins {
id 'java'
}
dependencies {
implementation project(':garage')
}
task run(type: JavaExec) {
main = 'app.Main'
classpath = sourceSets.main.runtimeClasspath
}
The actual implementation again goes to src/main/java/app/Main.java:
package app;
import garage.Garage;
public class Main {
public static void main(String[] args) {
Garage g = new Garage();
g.getCar().drive();
}
}
Now we can run the whole thing with gradle :app:run, which should give us output along these lines:
> Task :app:run
Hello World!
BUILD SUCCESSFUL in 506ms
6 actionable tasks: 1 executed, 5 up-to-date
<-------------> 0% WAITING
> IDLE
So as you can see, there’s nothing special you need to do beyond the things you’ve already stated. This is assuming you’re using classpaths. If you’re using the Java 9+ module system there are some additional steps, but this would be beyond the scope of this question I think.
I'm using Dagger across several Gradle modules in an android project. I have a lib and app module. Inside lib module I have two classes PrivateThing and ExposedThing. ExposedThing depends on PrivateThing.
I have a dagger #Module to provide these two things:
#Module
public class LibModule {
#Provides
ExposedThing provideExposedThing(PrivateThing privateThing) {
return new ExposedThing(privateThing);
}
#Provides
PrivateThing providePrivateThing() {
return new PrivateThing();
}
}
In the app module, I have a single class SomeUiElement which depends on ExposedThing and a separate module:
#Module
public class ApplicationModule {
#Provides
SomeUiElement provideSomeUiElement(ExposedThing exposedThing) {
return new SomeUiElement(exposedThing);
}
}
And a component to bring everything together:
#Singleton
#Component(modules = {
ApplicationModule.class,
LibModule.class
})
public interface ApplicationComponent {
void inject(SomeActivity activity);
}
Now I want to enforce that nothing in the app module can depend on PrivateThing. I think I'm asking something similar to this question. One solution was to use component dependency. However the documentation recommends subcomponents see "Subcomponents for encapsulation".
Which is the preferred way to do this? Won't either method mean that lib module supplies it's own Component? I thought this was not a best practice, that libraries should only supply a Module.
I'm having a project based on Dagger 2 which consists of two modules. The core module includes some interfaces and some classes that have member injections declared for these interfaces.
The actual implementations of these interfaces are included in the second module which is an Android project. So, naturally the provide methods for these are included in the Android project.
Dagger will complain during compilation about not knowing how to inject these in the core module.
Any thoughts on how to achieve this without using constructor injections?
In short, I just tried this, and it works. Be sure to check the exact error messages and make sure you are providing these interfaces and #Inject annotations are present.
There is probably just some wrong named interface or a missing annotation. Following up is a full sample using your described architecture that is compiling just fine. The issue you are currently experiencing is probably the one described in the last part of this post. If possible, you should go with the first solution though and just add those annotations.
The library
For reproducability this sample has minimalist models. First, the interface needed by my class in the library module:
public interface MyInterface {
}
Here is my class that needs that interface. Make sure to declare it in the constructor and provide the #Inject annotation!
#MyScope // be sure to add scopes in your class if you use constructor injection!
public class MyClassUsingMyInterface {
private MyInterface mMyInterface;
#Inject
public MyClassUsingMyInterface(MyInterface myInterface) {
mMyInterface = myInterface;
}
}
The idea is that the interface will be implemented by the app using MyClassUsingMyInterface and provided by dagger. The code is nicely decoupled, and my awesome library with not so many features is complete.
The application
Here need to supply the actual coupling. This means to get MyClassUsingMyInterface we have to make sure we can supply MyInterface. Let's start with the module supplying that:
#Module
public class MyModule {
#Provides
MyInterface providesMyInterface() {
return new MyInterface() {
// my super awesome implementation. MIT license applies.
};
}
}
And to actually use this, we provide a component that can inject into MyTestInjectedClass that is going to need MyClassUsingMyInterface.
#Component(modules = MyModule.class)
public interface MyComponent {
void inject(MyTestInjectedClass testClass);
}
Now we have a way to provide the requested interface. We declared that interface needed by the library class in a constructor marked with #Inject. Now I want a class that requires my awesome library class to use. And I want to inject it with dagger.
public class MyTestInjectedClass {
#Inject
MyClassUsingMyInterface mMyClassUsingMyInterface;
void onStart() {
DaggerMyComponent.create().inject(this);
}
}
Now we hit compile...and dagger will create all the factories needed.
Inject Libraries you can not modify
To just provide the full scale of dagger, this sample could also have been without actual access to the source code of the library. If there is no #Inject annotation dagger will have a hard time creating the object. Notice the missing annotation:
public class MyClassUsingMyInterface {
private MyInterface mMyInterface;
public MyClassUsingMyInterface(MyInterface myInterface) {
mMyInterface = myInterface;
}
}
In that case we have to manually provide the class. The module would be needed to be modified like the following:
#Module
public class MyModule {
#Provides
MyInterface providesMyInterface() {
return new MyInterface() {
};
}
#Provides
MyClassUsingMyInterface providesMyClass(MyInterface myInterface) {
return new MyClassUsingMyInterface(myInterface);
}
}
This introduces more code for us to write, but will make those classes available that you can not modify.
While exploring Guice, I had a question on the way the dependencies are injected.
Based on my understanding, one of the important aspects of DI is that, the dependency is known and is injected at runtime.
In Guice, to inject a dependency we either need to add the binding or implement a provider. Adding a dependency takes a class object which adds a compile time dependency on that class. One way to avoid that is to implement it as a provider and let the provider use reflection to dynamic load the class.
public class BillingModule extends AbstractModule {
#Override
protected void configure() {
bind(CreditCardProcessor.class).toProvider(
BofACreditCardProcessorProvider.class);
bind(CreditCardProcessor.class).annotatedWith(BofA.class).toProvider(
BofACreditCardProcessorProvider.class);
bind(CreditCardProcessor.class).annotatedWith(Amex.class).toProvider(
AmexCreditCardProcessorProvider.class);
}
#Provides
PaymentProcessor createPaymentProcessor() {
return new PayPalPaymentProcessor();
}
#Provides
PayPalPaymentProcessor createPayPalPaymentProcessor() {
return new PayPalPaymentProcessor();
}}
Is there a reason why Guice choose class object over class name? That could have removed the compile time dependency right?
If your interface and implementation are defined in the same dependency (that is, in the same JAR file) then you already have a hard build dependency on the implementation, whether you use Guice or not.
Basically, as soon as you have:
public final class MyClass {
public void doSomething(Foo foo);
}
Then to compile MyClass a definition of Foo needs to be on the compile-time classpath.
The way to resolve this is to separate out the interface from the implementation. For example, if Foo is an interface, and FooImpl is the implementation of it, you would put FooImpl in a different dependency (that is, a different JAR file) from Foo.
Now, let's say you have two sub-projects in Maven:
foo-api/
pom.xml
src/main/java/com/foo/Foo.java
foo-impl/
pom.xml
src/main/java/com/foo/FooImpl.java
Where should the Guice module that binds Foo live? It shouldn't live in the foo-api project, it should live in the foo-impl project, alongside FooImpl.
Now suppose you have a separate implementation of Foo (let's call it SuperFoo), and your project needs a Foo, but it could be either FooImpl or SuperFoo.
If we make SuperFoo its own project:
super-foo/
pom.xml
src/main/java/com/super/foo/SuperFoo.java
src/main/java/com/super/foo/SuperFooModule.java
Now all your application code can simply #Inject Foo and use the foo. In your main() method (or wherever you create your Injector) you need to decide whether to install FooModule (from foo-impl) or SuperFooModule (from super-foo).
That is the place where reflection may be warranted. For example, you could have a configuration flag foo_module which could be set to either "com.foo.FooModule" or "com.super.foo.SuperFooModule". You could decide which one to install using code like this:
public static void main(String[] args) {
Config config = parseConfig(args);
List<Module> modules = new ArrayList<>();
modules.add(...); // application modules
String fooModuleName = config.get("foo_module");
Class<? extends Module> moduleClass =
Class.forName(fooModuleName).asSubclass(Module.class);
modules.add(moduleClass.newInstance());
Injector injector = Guice.createInjector(modules);
injector.getInstance(MyApplication.class).run();
}
Of course, you could also use any other mechanism you like to select which module to install. In many cases, you don't even really want to do this reflectively, you can simply change the code at the same time you change the build dependency.