Using named injection in Guice - java

I'm using Guice for dependency injection and I'm a bit confused. There are two Named annotations in different packages:
com.google.inject.name.Named and javax.inject.Named (JSR 330?).
I'm eager to depend on javax.inject.*. Code sample:
import javax.inject.Inject;
import javax.inject.Named;
public class MyClass
{
#Inject
#Named("APrefix_CustomerTypeProvider")
private CustomerTypeProvider customerTypeProvider;
}
In my naming module I may have the following line:
bind(CustomerTypeProvider.class).annotatedWith(...).toProvider(CustomerTypeProviderProvider.class);
The question: I'm curious what should I put where the dots are? I would expect something like com.google.inject.name.Names.named("APrefix_CustomerTypeProvider") but this one returns com.google.inject.name.Named while I need the one in javax.inject.
CustomerTypeProviderProvider.class.getAnnotation(javax.inject.Named.class) also does not fit well because the CustomerTypeProviderProvider (ignore the stupid name, legacy issue) is not annotated.

As mentioned on the Guice wiki, both work the same. You shouldn't worry about that. It is even recommended to use javax.inject.* when available, just as you prefer too (bottom of the same page).
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.name.Names;
import javax.inject.Inject;
public class Main {
static class Holder {
#Inject #javax.inject.Named("foo")
String javaNamed;
#Inject #com.google.inject.name.Named("foo")
String guiceNamed;
}
public static void main(String[] args) {
Holder holder = Guice.createInjector(new AbstractModule(){
#Override
protected void configure() {
// Only one injection, using c.g.i.Names.named("").
bind(String.class).annotatedWith(Names.named("foo")).toInstance("foo");
}
}).getInstance(Holder.class);
System.out.printf("javax.inject: %s%n", holder.javaNamed);
System.out.printf("guice: %s%n", holder.guiceNamed);
}
}
Prints:
java.inject: foo
guice: foo

Related

Inject a dependency into an annotation

I have a class that uses a custom json serializer via #JsonAdapter annotation:
import com.google.gson.annotations.JsonAdapter;
#JsonAdapter(IFooAdapter.class)
public interface IFoo {
//...
}
The IFooAdapter class has a dependency on IMyFactory that should be injected.
Injection is configured via Guice.CreateInjector() and bind(IMyFactory.class).to(MyFactoryImpl.class);.
But how can I get guice to inject this into my IFooAdapter class (shown below), which is only used in the #JsonAdapter annotation (shown above)?
import com.google.gson.JsonElement;
import com.google.gson.TypeAdapter;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.inject.Inject;
public class IFooAdapter implements TypeAdapter<IFoo> {
#Inject IMyFactory myFactory; // <-- THIS INJECTION DOES NOT WORK
#Override public IFoo read(JsonReader in) {
return myFactory.create(/*...*/);
}
#Override public void write(JsonWriter out, IFoo value) {
//...
}
}
After injection via guice.InjectMembers(myMainClass), when I try to parse json via GsonBuilder().create().fromJson(), and debug IFooAdapter.read(), myFactory is Null.
(Which is really not that surprising, after all the IFooAdapter is not a member of myMainClass)
But how do I inject this properly?
You could try registering InstanceCreator for your adapter via something like
gsonBuilder.registerTypeAdapter(IFooAdapter.class, type -> injector.getInstance(type))
You would need to create Gson instance in a place, where you have access to injector (I would do it as #Provides method providing Gson instance with Injector parameter, then inject Gson wherever needed).

Testing custom JsonDeserializer in Jackson / SpringBoot

I am trying to write a unit test to a custom deserializer that is instantiated using a constructor with an #Autowired parameter and my entity marked with #JsonDeserialize. It works fine in my integration tests where a MockMvc brings up spring serverside.
However with tests where objectMapper.readValue(...) is being called, a new instance of deserializer using default constructor with no parameters is instantiated. Even though
#Bean
public MyDeserializer deserializer(ExternalObject externalObject)
instantiates wired version of deserializer, real call is still passed to empty constructor and context is not being filled up.
I tried manually instantiating of a deserializer instance and registering it in ObjectMapper, but it only works if I remove #JsonDeserialize from my entity class (and it breaks my integration tests even if I do the same in my #Configuration class.) - looks related to this: https://github.com/FasterXML/jackson-databind/issues/1300
I can still test the deserializer behavior calling deserializer.deserialize(...) directly, but this approach doesn't work for me in tests that are not Deserializer's unit tests...
UPD: working code below
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.github.tomakehurst.wiremock.common.Json;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
import java.io.IOException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
#JsonTest
#RunWith(SpringRunner.class)
public class JacksonInjectExample {
private static final String JSON = "{\"field1\":\"value1\", \"field2\":123}";
public static class ExternalObject {
#Override
public String toString() {
return "MyExternalObject";
}
}
#JsonDeserialize(using = MyDeserializer.class)
public static class MyEntity {
public String field1;
public String field2;
public String name;
public MyEntity(ExternalObject eo) {
name = eo.toString();
}
#Override
public String toString() {
return name;
}
}
#Component
public static class MyDeserializer extends JsonDeserializer<MyEntity> {
#Autowired
private ExternalObject external;
public MyDeserializer() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
public MyDeserializer(#JacksonInject final ExternalObject external) {
this.external = external;
}
#Override
public MyEntity deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
return new MyEntity(external);
}
}
#Configuration
public static class TestConfiguration {
#Bean
public ExternalObject externalObject() {
return new ExternalObject();
}
#Bean
public MyDeserializer deserializer(ExternalObject externalObject) {
return new MyDeserializer(externalObject);
}
}
#Test
public void main() throws IOException {
HandlerInstantiator hi = mock(HandlerInstantiator.class);
MyDeserializer deserializer = new MyDeserializer();
deserializer.external = new ExternalObject();
doReturn(deserializer).when(hi).deserializerInstance(any(), any(), eq(MyDeserializer.class));
final ObjectMapper mapper = Json.getObjectMapper();
mapper.setHandlerInstantiator(hi);
final MyEntity entity = mapper.readValue(JSON, MyEntity.class);
Assert.assertEquals("MyExternalObject", entity.name);
}
}
I don't know how to set this particularly using Jackson injection, but you can test it using spring Json tests. I think this method is closer to the real scenario and much more simplier. Spring will load only related to serialization/deserialization beans, thus you have to provide only custom beans or mocks instead them.
#JsonTest
public class JacksonInjectExample {
private static final String JSON = "{\"field1\":\"value1\", \"field2\":123}";
#Autowired
private JacksonTester<MyEntity> jacksonTester;
#Configuration
public static class TestConfiguration {
#Bean
public ExternalObject externalObject() {
return new ExternalObject();
}
}
#Test
public void test() throws IOException {
MyEntity result = jacksonTester.parseObject(JSON);
assertThat(result.getName()).isEqualTo("MyExternalObject");
}
If you would like to use mocks use following snippet:
#MockBean
private ExternalObject externalObject;
#Test
public void test() throws IOException {
when(externalObject.toString()).thenReturn("Any string");
MyEntity result = jacksonTester.parseObject(JSON);
assertThat(result.getName()).isEqualTo("Any string");
}
Very interesting question, it made me wonder how autowiring into jackson deserializers actually works in a spring application. The jackson facility that is used seems to be the HandlerInstantiator interface, which is configured by spring to the SpringHandlerInstantiator implementation, which just looks up the class in the application context.
So in theory you could setup an ObjectMapper in your unit test with your own (mocked) HandlerInstantiator, returning a prepared instance from deserializerInstance(). It seems to be fine to return null for other methods or when the class parameter does not match, this will cause jackson to create the instance on its own.
However, I do not think this is a good way to unit test deserialization logic, as the ObjectMapper setup is necessarily different from what is used during actual application execution. Using the JsonTest annotation as suggested in Anton's answer would be a much better approach, as you are getting the same json configuration that would be used during runtime.
Unit tests should not depend upon or invoke other major classes or frameworks. This is especially true if there are also integration or acceptance tests covering the functioning of the application with a particular set of dependencies as you describe. So it would be best to write the unit test so that it has a single class as its subject i.e. calling deserializer.deserialize(...) directly.
In this case a unit test should consist of instanciating a MyDeserializer with a mocked or stubbed ExternalObject, then testing that its deserialize() method returns a MyEntity correctly for different states of the JsonParser and DeserializationContext arguments. Mockito is really good for setting up mock dependencies!
By using an ObjectMapper in the unit test, quite a lot of code from the Jackson framework is also being invoked in each run - so the test is not verifying the contract of MyDeserializer, it is verifying the behaviour of the combination of MyDeserializer and a particular release of Jackson. If there is a failure of the test it won't be immediatly clear which of all the components involved is at fault. And because setting up the environment of the two frameworks together is more difficult the test will prove brittle over time and fail more often due to issues with the setup in the test class.
The Jackson framework is responsible for writing unit tests of ObjectMapper.readValue and constructors using #JacksonInject. For the 'other unit tests that are not Deserializer's unit tests' - it would be best to mock/stub the MyDeserializer (or other dependencies) for that test. That way the other class's logic is being isolated from the logic in MyDeserializer - and the other class's contracts can be verified without being qualified by the behaviour of code outside of the unit under test.

No qualifying bean of type 'concert.PerformanceImp' available

I am still a beginner in Spring Framework so I tried to code a program about "introduction" in Spring AOP but I am facing an error while compiling. Please find below the classes in the package concert:
PerformanceImp.java
package concert;
import org.springframework.stereotype.Component;
#Component
public class PerformanceImp implements Performance {
public void perform() {
System.out.println("This is the performance function");
}
}
Performance.java
package concert;
public interface Performance {
public void perform();
}
Encoreable.java
package concert;
public interface Encoreable {
void performEncore();
}
DefaultEncoreable.java
package concert;
import org.springframework.stereotype.Component;
#Component
public class DefaultEncoreable implements Encoreable {
public void performEncore() {
System.out.println("This is the performEncore function");
}
}
EncoreableIntroducer.java
package concert;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class EncoreableIntroducer {
#DeclareParents(value="concert.Performance+",
defaultImpl=DefaultEncoreable.class)
public static Encoreable encoreable;
}
ConcertConfig.java
package concert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan("concert")
public class ConcertConfig {
}
And the main class:
Main.java
package concert;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ConcertConfig.class);
PerformanceImp pi = (PerformanceImp) context.getBean(PerformanceImp.class);
((Encoreable) pi).performEncore();
pi.perform();
}
}
I am getting the error:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'concert.PerformanceImp' available
Any help please ?
You cannot access the implementation (PerformanceImp) by default, because you enabled AOP, which sets to target interfaces instead of implementation. If you would remove EnableAspectJAutoProxy, you would see the code would work fine.
To understand a bit more about how AOP targeting works, take a look at this Spring Documentation
Spring AOP can also use CGLIB proxies. This is necessary to proxy
classes rather than interfaces. CGLIB is used by default if a business
object does not implement an interface. As it is good practice to
program to interfaces rather than classes; business classes normally
will implement one or more business interfaces. It is possible to
force the use of CGLIB, in those (hopefully rare) cases where you need
to advise a method that is not declared on an interface, or where you
need to pass a proxied object to a method as a concrete type.
So you have two options:
Take the interface when trying to get the bean from the ApplicationContext.
Enable AOP to target concrete classes instead.
To do this point #2, modify your annotation as follows:
#EnableAspectJAutoProxy(proxyTargetClass = true)
Try:
Performance pi = context.getBean("performanceImp", Performance.class);
instead of:
PerformanceImp pi = (PerformanceImp) context.getBean(PerformanceImp.class);

auto scan for guice

I have never used guice before, and I wanted to try it out on an example project with jersey based JAX-RS API backed by a service-bean. I followed this guide: http://randomizedsort.blogspot.de/2011/05/using-guice-ified-jersey-in-embedded.html and was able to bring it to work. My setup is very simple, a JAX-RS resource is invoked via Guice and has a field that is annotated #Inject and injected by Guice:
#Path("configuration")
#Produces(MediaType.APPLICATION_JSON)
#Singleton
public class ConfigurationResource {
#Inject
private ConfigurationService configurationService;
So far so good, everything works like it should, besides following: I am using GuiceServletContextListener for setting things up and have to name each component explicitly:
#WebListener
public class GuiceInitializer extends GuiceServletContextListener{
#Override
protected Injector getInjector() {
return Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets() {
//resources
bind(ConfigurationResource.class);
//services
bind(ConfigurationService.class).to(ConfigurationServiceImpl.class);
// Route all requests through GuiceContainer
serve("/management/*").with(GuiceContainer.class);
}
});
}
}
I find it pretty inconvenient to explicitly name all dependencies. I have worked with standalone jersey before and it's perfectly capable of auto-scanning for resources in defined packages. Also Spring and CDI are capable of mapping implementation to interfaces without need to explicitly name them.
Now the question part:
is there any autoscan extension/setting for guice? I found some on the internet, but it's hard to tell which of them are still useable and uptodate.
is there any other possibility to make configuration of implementations and resources more convenient?
thanks in advance.
Leon
I do not think Guice has built in support for someting like the component-scan of Spring framework. However, it is not difficult to simulate this feature in Guice.
You simply need to write a helper module like the following
import com.google.inject.AbstractModule;
import org.reflections.Reflections;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* To use this helper module, call install(new ComponentScanModule("com.foo", Named.class); in the configure method of
* another module class.
*/
public final class ComponentScanModule extends AbstractModule {
private final String packageName;
private final Set<Class<? extends Annotation>> bindingAnnotations;
#SafeVarargs
public ComponentScanModule(String packageName, final Class<? extends Annotation>... bindingAnnotations) {
this.packageName = packageName;
this.bindingAnnotations = new HashSet<>(Arrays.asList(bindingAnnotations));
}
#Override
public void configure() {
Reflections packageReflections = new Reflections(packageName);
bindingAnnotations.stream()
.map(packageReflections::getTypesAnnotatedWith)
.flatMap(Set::stream)
.forEach(this::bind);
}
}
To component scan a package like com.foo and sub packages for classes carrying #Singleton, use it in this way:
public class AppModule extends AbstractModule {
public void configure() {
install(new ComponentScanModule("com.foo", Singleton.class));
}
}

Guice: Using providers to get multiple instances:

I am trying to learn Guice for dependency Injection using Providers to create multiple instances of an object(Example from getting started guide on Guice website). how should I test this? Please advise.
The following is the module:
package testing;
import com.google.inject.AbstractModule;
public class BillingModule extends AbstractModule {
#Override
protected void configure() {
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
bind(BillingService.class).to(RealBillingService.class);
bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
}
}
The following is the class under test:
package testing;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class RealBillingService implements BillingService {
private Provider<CreditCardProcessor> processorProvider;
private Provider<TransactionLog> transactionLogProvider;
#Inject
public RealBillingService(Provider<CreditCardProcessor> processorProvider,
Provider<TransactionLog> transactionLogProvider) {
this.processorProvider = processorProvider;
this.transactionLogProvider = transactionLogProvider;
}
public void chargeOrder() {
CreditCardProcessor processor = processorProvider.get();
TransactionLog transactionLog = transactionLogProvider.get();
/* use the processor and transaction log here */
processor.toString();
transactionLog.toString();
}
}
The following is the test class with main():
public class test {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new BillingModule());
BillingService billingService = injector.getInstance(BillingService.class);
billingService.chargeOrder();
}
}
Upon running this, I am expecting the output from the following toString methods to show up but am seeing nothing:
processor.toString();
transactionLog.toString();
What am i missing here?
Please advise,
thanks!
This happens because you just call toString without putting the resulting string anywhere (eg the call to System.out.println)
However providers are not intended to be used like that. You should not call Provider.get yourself: instead require the result of the provider, register your provider and let Guice do its job (you can also annotate methods in your modules with #Provides instead of defining provider classes)
By default providers are called each time a new instance of a certain class is required. Instances are not recycled unless you explicitly request it via using scopes (like the builtin Singleton)

Categories