I have this interface and simple implementation:
public interface Data {
}
import java.nio.file.Path;
import javax.annotation.Nullable;
import javax.inject.Inject;
import com.google.inject.assistedinject.Assisted;
public class SimpleData implements Data {
#Inject
public SimpleData(#Assisted #Nullable Path path) {
}
}
I want to generate a Factory with different methods using guice.
import java.nio.file.Path;
import javax.annotation.Nullable;
public interface Factory {
Data create();
Data load(#Nullable Path path);
}
But the following module configuration:
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class Main {
public static void main(String[] args) {
Injector injector = Guice.createInjector(
binder -> binder.install(
new FactoryModuleBuilder().implement(Data.class, SimpleData.class)
.build(Factory.class)));
Data data = injector.getInstance(Factory.class).create();
}
}
fails:
Exception in thread "main" com.google.inject.CreationException: Guice creation errors:
1) No implementation for java.nio.file.Path annotated with #com.google.inject.assistedinject.Assisted(value=) was bound.
while locating java.nio.file.Path annotated with #com.google.inject.assistedinject.Assisted(value=)
for parameter 0 at SimpleData.<init>(SimpleData.java:10)
at Factory.create(Factory.java:1)
at com.google.inject.assistedinject.FactoryProvider2.initialize(FactoryProvider2.java:539)
at com.google.inject.assistedinject.FactoryModuleBuilder$1.configure(FactoryModuleBuilder.java:335)
1 error
at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:435)
at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:175)
at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:109)
at com.google.inject.Guice.createInjector(Guice.java:95)
at com.google.inject.Guice.createInjector(Guice.java:72)
at com.google.inject.Guice.createInjector(Guice.java:62)
at Main.main(Main.java:9)
I solved my problem using the annotation #AssistedInject. Quote from the javadoc:
When used in tandem with FactoryModuleBuilder, constructors annotated with #AssistedInject indicate that multiple constructors can be injected, each with different parameters.
So i add the annotation and a constructor to the SimpleData class:
public class SimpleData implements Data {
#AssistedInject
public SimpleData(#Assisted Path path) {
}
#AssistedInject
public SimpleData() {
}
}
i removed the #Nullable annotation from the factory:
import java.nio.file.Path;
public interface Factory {
Data create();
Data load(Path path);
}
#Nullable does not mean that if you don't have a binding, then null will be injected. It only allows writing bindings to null. If you don't have a binding and there is no applicable JIT-binding, then injection will fail.
Your factory's create() method requires Guice to find an #Assisted Path binding, but it obviously can't find it since you've never created one, so it fails.
Honestly, I'm not sure if there is a clean way to implement such defaulting. Ideally you should mark Path with some binding annotation and add a default binding to null for it, but #Assisted already is a binding annotation, and it is not possible to have multiple binding annotations on a single injection point. You can try creating a binding for #Assisted Path:
binder.bind(Path.class).annotatedWith(Assisted.class).toInstance(null);
However, I'm not sure if it would work because Assisted can be special to Guice. And even if it will work, it is not very clean - there may be conflicts with other assisted factories accepting Paths.
I would have Guice implement some kind of internal factory interface, then expose something else. Like this:
interface InternalFactory {
Data load(#Nullable Path path);
}
public interface Factory {
Data load();
Data load(#Nullable Path path);
}
class FactoryImpl implements Factory {
#Inject InternalFactory internalFactory;
#Override
public Data load() {
return load(null); // Pass your defaults here
}
#Override
public Data load(#Nullable Path path) {
// Sadly you'll have to explicitly forward arguments here, but it's not
// too bad IMO
return internalFactory.load(path);
}
}
public class MyModule extends AbstractModule {
#Override
protected void configure() {
install(new FactoryModuleBuilder()
.implement(Data.class, SimpleData.class)
.build(InternalFactory.class));
bind(Factory).to(FactoryImpl.class);
}
}
Related
I am trying to setup a custom #ConfigurationProperties class loaded from a HOCON syntax .conf file.
I have a Class annotated with #PropertySource(factory=TypesafePropertySourceFactory.class, value = "classpath:app.conf")
#Configuration
#ConfigurationProperties(value = "app.server")
#PropertySource(factory = TypesafePropertySourceFactory.class, value = "classpath:app.conf")
public class ServerProperties {
public int port;
}
and a simple test class:
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeTest {
#Test
public void someCoolTest() {/* ... */}
// ...
}
When i run my junit test runner, i get the following error:
Caused by: com.typesafe.config.ConfigException$BadPath: path parameter: Invalid path 'spring.info.build.location:classpath:META-INF/build-info.properties': Token not allowed in path expression: ':' (you can double-quote this token if you really want it here)
at com.typesafe.config.impl.PathParser.parsePathExpression(PathParser.java:155) ~[config-1.4.0.jar:1.4.0]
at com.typesafe.config.impl.PathParser.parsePathExpression(PathParser.java:74) ~[config-1.4.0.jar:1.4.0]
at com.typesafe.config.impl.PathParser.parsePath(PathParser.java:61) ~[config-1.4.0.jar:1.4.0]
...
If i uncomment the #PropertySource line on the ServerProperties class, the tests proceed normally. It seems strange to me that my custom PropertySourceFactory gets in the way of the default .properties file resolution process.
PropertySource and Factory classes
// TypesafeConfigPropertySource.java
import com.typesafe.config.Config;
import org.springframework.core.env.PropertySource;
public class TypesafeConfigPropertySource extends PropertySource<Config> {
public TypesafeConfigPropertySource(String name, Config source) {
super(name, source);
}
#Override
public Object getProperty(String path) {
if (source.hasPath(path)) {
return source.getAnyRef(path);
}
return null;
}
}
// TypesafePropertySourceFactory.java
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;
import java.util.Objects;
public class TypesafePropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
Config config = ConfigFactory.load(Objects.requireNonNull(resource.getResource().getFilename())).resolve();
String safeName = name == null ? "typeSafe" : name;
return new TypesafeConfigPropertySource(safeName, config);
}
}
Am I missing something fundamental about configuring custom property resource factories, or is this a bug?
Versions
Spring boot 2.3.4
Junit Jupiter 5.6.2
Maybe you can also solve it with the use of a ContextInitializer as suggested in the answer here:
Spring Environment backed by Typesafe Config
TL;DR
Return null if you cannot process the path in your custom impl
public class TypesafeConfigPropertySource extends PropertySource<Config> {
// ...
#Override
public Object getProperty(String path) {
try {
if (source.hasPath(path)) {
return source.getAnyRef(path);
}
} catch(ConfigException.BadPath ignore) {
}
return null;
}
// ...
}
Explanation
I am making educated guesses, but functionally this appears supported by the way the code behaves
the most likely scenario here is the resolution order will consider our custom implementation before any default implementation. The method used in our implementation will error out with any path containing a ":" and "[" as the error occurs in the check for the path's existence.
I'm simply wrapping the BadPath exception in order to catch any problem and then returning null to signify no match.
I am very familiar with using spring to inject using #Bean & #Autowired. I have switched over to looking at guice and I am wondering what is the minimum involved to have it functioning. The following basic example throws a NPE:
import javax.inject.Inject;
public class ClassA {
String a = "test";
#Inject
public ClassA() {
System.out.println(a);
}
public void go() {
System.out.println("two");
}
}
The following class attempting to instantiate a new instance of ClassA:
import javax.inject.Inject;
public class ClassB {
#Inject
ClassA guice;
public ClassB() {
guice.go();
}
public static void main(String[] args) {
ClassB b = new ClassB();
}
}
I have tried all sorts of combinations of the following with no success:
public class SupportModule extends AbstractModule {
#Override
protected void configure() {
bind(ClassA.class);
//bind(ClassA.class).to(ClassB.class);
//others too
}
}
I must be missing a key thing somewhere here, I'm not quite sure where? do I need some manual instantiation of guice/configuration itself? I assume I possibly do.
guice.go(); <= NullPointerException occurs here, obviously the obj is null because my guice setup isn't right
In Spring i can do the following, I assume Guice can aid me in doing this too:
#Bean
public FrameworkProperties properties() {
return new FrameworkProperties();
}
and then just:
#Autowired
FrameworkProperties props;
do I need some manual instantiation of guice/configuration itself? I assume I possibly do.
Yes, you assumed right. You'll have to bootstrap the Injector modules that you have defined using the Guice.createInjector() method. Also, one other thing to note is when using a custom defined constructor like in ClassB, you'll have to use constructor injection. So in order to get this working, ClassB should look like:
public class ClassB {
private ClassA guice;
#Inject //This will inject the dependencies used in the constructor arguments
public ClassB(final ClassA guice) {
this.guice = guice;
guice.go();
}
public static void main(String[] args) {
/**
* If only type to type binding is required, you can skip creating a Module class &
* bootstrap the injector with empty argument createInjector like used below. But, when
* there are other kind of bindings like Type to Implementations defined in modules, you can use:
* final Injector injector1 = Guice.createInjector(new GuiceModule1(), new GuiceModule2());
*/
final Injector injector = Guice.createInjector();
final ClassB b = injector.getInstance(ClassB.class); //This will create dependency graph for you and inject all dependencies used by ClassB and downwards
}
}
Also, you can remove the #Inject annotation used in ClassA's constructor as you are not injecting any external dependencies in that constructor. You can look up the Guice's getting started wiki for more documentation.
I have a Spring bean defined like this:
package org.behrang.sample;
import foo.AbstractThirdPartyClass;
#Component
public class SampleBean extends AbstractThirdPartyClass<Input, Output> {
#Override
public Optional<Output> process(Input input) {
}
}
The AbstractThirdPartyClass class is defined in a third-party library named foo.
I want to implement an advice that applies to all methods in the
org.behrang.sample package, so I have implemented something like this:
#Aspect
#Component
public class SampleAspect {
#Before("execution(public * org.behrang.sample..*.*(..))")
public void sampleBefore(JoinPoint joinPoint) {
}
}
However this is not advising SampleBean::process(Input). If I remove the extends AbstractThirdPartyClass<Input, Output>
part, then the process method is advised.
Is there an elegant way to workaround this problem? For example, I can define an interface in org.behrang.sample with one method like this:
public interface Sampler<I, O> {
public Optional<O> process(I input);
}
And make the SampleBean implement it too. But this is way too ugly and anti-DRY.
Also I have enabled AOP using #EnableAspectJAutoProxy(proxyTargetClass = true) as manu beans defined in this project are not implementing any interfaces.
Novice here trying to use a dummy Java Facebook app that uses Guice to inject a database dependency into the Facebook factory but continue to have Guice error out telling me:
### No implementation for com.example.storage.Db annotated with #com.example.storage.annotations.SystemDb() was bound while locating com.example.storage.Db annotated with #com.example.storage.annotations.SystemDb() for parameter 0 at com.example.facebook.client.exceptions.FacebookExceptionHandlerDb at com.example.facebook.client.guice.FacebookClientModule.configure
### Could not find a suitable constructor in com.example.facebook.statsd.StatsdClient. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private. at com.example.facebook.statsd.StatsdClient.class while locating com.example.facebook.statsd.StatsdClient for parameter 1 at com.example.facebook.client.exceptions.FacebookExceptionHandlerDb. com.example.facebook.client.guice.FacebookClientModule.configure
Code for app:
app.java
package com.example.facebook;
import com.google.inject.Guice;
import com.restfb.Connection;
import com.restfb.types.Post;
import com.example.facebook.client.FacebookClientFactory;
import com.example.facebook.client.RobustFacebookClient;
import com.example.facebook.client.guice.FacebookClientModule;
import com.example.facebook.statsd.StatsdClient;
public class App {
public static void main ( String[] args ) {
final FacebookClientFactory facebookClientFactory =
Guice.createInjector(new FacebookClientModule()).getInstance(FacebookClientFactory.class);
//error from line above
final RobustFacebookClient robustFacebookClient =
facebookClientFactory.create("accessToken");
//more ...
}
The resulting error points me to the FacebookClientModule binding:
FacebookClientModule.java
public class FacebookClientModule extends AbstractModule {
bind(FacebookExceptionHandler.class).to(FacebookExceptionHandlerDb.class);
//error resulting from the failed binding on the FacebookExceptionHandlerDB class
install(new FactoryModuleBuilder()
.implement(FacebookClient.class, RobustFacebookClient.class)
.build(FacebookClientFactory.class));
}
}
Where inside the FacebookExceptionHandleDB class the constructor has the injection:
FacebookExceptionHandlerDB.java
public class FacebookExceptionHandlerDb implements FacebookExceptionHandler {
// list of class String variables ...
private final FacebookErrorParser parser;
private final Db db;
private StatsdClient statsd;
#Inject
public FacebookExceptionHandlerDb(#SystemDb Db db, StatsdClient statsd, FacebookErrorParser parser) {
this.db = db;
this.statsd = statsd;
this.parser = parser;
}
}
From what I can gleam, the dependency injection for parameters zero and one, db and statsD respectively, is failing. Could someone point out where or what in the app code is missing?
At first glance it seems like your missing the bindings for the Db annotated dependency and the StatsdClient.
You'll need to provide the missing bindings to your module like so
bind(Db.class).annotatedWith(SystemDb.class).to(DbImplOfSomeSort.class);
bind(StatsdClient.class).to(StatsdClientImplOfSomeSort.class);
Guice is able to automatically inject Concrete Class with either a public no argument constructor or a constructor with #Inject without any specific defined binding in your module but when it comes to Interfaces you have to define the necessary bindings.
Here Db.class and StatsdClient.class are interfaces which you need to bind to specific implementation.
Not the source of the issue in this particular case, but I ran across this issue when I had my implementation and interface classes backwards:
public class MyModule extends AbstractModule {
#Override
public void configure() {
bind(MyClassImpl.class).to(MyInterface.class);
}
}
Should have been:
bind(MyInterface.class).to(MyClassImpl.class);
I have a class with a constructor, e.g.:
#Inject
public ClassTest(ITestInterface testInterface, Class<?> clazz){
...
}
The problem is how do I bind a class to an implementation which can be injected in this constructor and will the ClassTest binding pick the right class?
I want to inject different classes at different point of time. When I attempted to solve it Guice gives an error that it cannot find any suitable constructor on java.lang.Class.
I think you have to use assisted inject extension of Guice.
Basically, you define your ClassTest as it is, but mark 'varying' dependencies as #Assisted:
#Inject
public ClassTest(ITestInterface testInterface, #Assisted Class<?> clazz){
...
}
Then you create a factory interface for ClassTest objects which will accept Class argument and return ClassTests:
public interface ClassTestFactory {
ClassTest create(Class<?> clazz);
}
Then you install special kind of module which will create factories for you:
// Inside your module
install(new FactoryModuleBuilder().build(ClassTestFactory.class));
Then wherever you need ClassTest instances you should inject ClassTestFactory interface instead:
#Inject
YourLogicClass(ClassTestFactory ctFactory) {
this.ctFactory = ctFactory;
}
And finally you use it to create ClassTests for every class object you want:
ClassTest ct1 = ctFactory.create(SomeClass.class);
ClassTest ct2 = ctFactory.create(AnotherClass.class);
But if I were you, I would really reconsider the whole class architecture to avoid the need in such things.
This can be solved even without assisted inject, simply by using a TypeLiteral when creating the binding:
import javax.inject.Inject;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
public class ClassTest
{
static interface ITestInterface {}
#Inject
public ClassTest(ITestInterface testInterface, Class<?> clazz)
{
System.err.println("testInterface=" + testInterface);
System.err.println("clazz=" + clazz);
}
public static void main(String... argument)
{
ITestInterface testObject = new ITestInterface() {};
Module module = new AbstractModule()
{
#Override
protected void configure()
{
binder().bind(ITestInterface.class).toInstance(testObject);
binder().bind(new TypeLiteral<Class<?>>() {}).toInstance(testObject.getClass());
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
};
Injector injector = Guice.createInjector(module);
injector.getInstance(ClassTest.class);
}
}
The output when running this code is something like:
testInterface=ClassTest$1#3d921e20
clazz=class ClassTest$1
I have to agree with #VladimirMatveev though, that this is a somewhat unusual use case, and that the need for injection of java.lang.Class objects might be indicative of a design flaw. The only seemingly valid case of this type of injection that I've come across is for type checking, where an injected class needs the Class object to check the type of some other object (via Class.isInstance(...)) but it is not desirable to inject an instance (!) of that class (e.g., because it is not a singleton and might spawn all sorts of other undesired object creations). Still, even that scenario is somewhat hokey and might be solvable in a better way.
At the very least, I would use a more specific type argument, like Class<? extends ITestInterface> (which, I suspect, is what's intended by the OP).
To change injected value over time you could use the Provider bindings. And then it could look like this:
The module configuration:
public class SomeModule extends AbstractModule{
#Override
protected void configure() {
bind(Class.class).toProvider(SomeProvider.class);
}
}
The provider(not very elegant but may be for a start...):
public class SomeProvider implements Provider<Class<?>>{
private static Class<?> myClazz;
public static void setClass(Class<?> clazz){
myClazz = clazz;
}
#Override
public Class<?> get() {
return myClazz;
}
}
Some different classes:
public class SomeClass{
public int itWorks;
}
public class SomeOtherClass{
public int itWorksGreat;
}
Example client code:
public static void main(String[] args){
SomeProvider.setClass(SomeClass.class);
Injector injector = Guice.createInjector(new SomeModule());
printFields(injector.getInstance(Class.class));
SomeProvider.setClass(SomeOtherClass.class);
printFields(injector.getInstance(Class.class));
}
private static void printFields(Class clazz) {
Field[] declaredFields = clazz.getDeclaredFields();
for(Field field : declaredFields){
System.out.println(field.getName());
}
}
And finally the result:
itWorks
itWorksGreat