I'm trying to inject instances with generics and i'm getting the following error:
HasOne<ModelClass> cannot be used as a key; It is not fully specified.
I've read elsewhere that safest way to do this is to explicitly name the class to be used in the generic when using the injector to get an instance but i'd like to be a little cleaner. I'm trying to create Relationship objects between Models.
Here is my simplified Model class
public class Model {
#Inject
Injector injector;
public <ModelClass extends Model> HasOne<ModelClass> hasOne(Class<ModelClass> clazz) {
HasOne<ModelClass> hasOne = injector.getInstance(Key.get(new TypeLiteral<HasOne<ModelClass>>() {
}));
hasOne.init(clazz);
return hasOne;
}
}
My HasOne relationship
public class HasOne<T extends Model> {
Class clazz;
public void init(Class<T> clazz){
this.clazz = clazz;
}
#Inject
Injector injector;
public T get(){
return (T) injector.getInstance(clazz);
}
}
Test Model #1
public class TestModel extends Model {
public HasOne<ExampleModel> exampleModel(){
return hasOne(ExampleModel.class);
}
}
Test Model #2
public class ExampleModel extends Model {
}
I get the error when doing this
TestModel testModel = RoboGuice.getInjector(context).getInstance(TestModel.class);
HasOne<ExampleModel> relationship = testModel.exampleModel();
I'm trying to hide away the ugly relationship creation and keep it in the Model class
You cannot use new TypeLiteral<T>() { } if T is a type parameter, it has to be a fully-specified type. Luckily, since you have an instance of Class<ModelClass>, you can do this:
(Key<HasOne<ModelClass>>) Key.get(TypeLiteral.get(Types.newParameterizedType(HasOne.class, clazz)))
You'll get a warning on the cast but it is safe to suppress it.
Related
I have created REST controller with base request mapping on class.
#RestController
#RequestMapping(".../{type}/{typeId}/param..")
public class FooController{
#Autowired
BarServiceProxy proxy;
public List<Foo> getFoo(){
return proxy.get(getType());
}
/*
public Type getType(???){
return type;
}
*/
}
Next I have enum Type which determines what service will be used by proxy service (ie. proxy has injected list of serivces and gets one that supports type). I am wondering if there is any way how to make part of request mapping {type} and get it in getter method below so I don't have to repeat it in every request mapping in this class.
I only figured one alternative solution - make this class abstract and then extend it and return constant. This would however leave me with lot of classes without any added value. For example:
#RequestMapping(".../{typeId}/param..")
public abstract class FooController{
#Autowired
BarServiceProxy proxy;
public List<Foo> getFoo(){
return proxy.get(getType());
}
protected abstract Type getType();
}
#RestController
#RequestMapping("/typeAbc)
public class TypeAbcFooController extends FooController{
public Type getType{
return Type.Abc;
}
}
So is it possible to bind #PathVariable from URL specified on class #RequestMapping in some shared method? Thanks
I hope i've understood your problem, but one way of improving your design could be to implement a strategy per type, to inject them, and to use them corresponding to your type received in your controller.
Exemple:
public enum MyType {
TYPE1,
TYPE2
}
public interface IService {
MyType getHandledType();
List<Foo> getFoo();
}
#Service
public class Type1Service implements IService {
#Override
public MyType getHandledType() {
return MyType.TYPE1;
}
#Override
public List<Foo> getFoo() {
// IMPLEMENTATION FOR TYPE1;
}
}
public class FooController{
#Autowired
List<IService> services;
public List<Foo> getFoo(MyType requestType){
IService service = services.stream().filter(iService -> iService.getHandledType() == requestType).findFirst().get();
return service.getFoo();
}
}
This way your controller is agnostic of the underlying service implementation, which is a big responsability.
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
I write JUnit tests for some Spring MVC Controllers. The initialization of the JUnit test is common for all my Controllers tests, so I wanted to create an abstract class that does this initialization.
Thus, I created the following code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:spring/applicationContext-test.xml", "classpath*:spring/spring-mvc-test.xml" })
#Transactional
public abstract class AbstractSpringMVCControllerTest<T> {
#Autowired
protected ApplicationContext applicationContext;
protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
protected HandlerAdapter handlerAdapter;
protected T controller;
#SuppressWarnings("unchecked")
#Before
public void initContext() throws SecurityException, NoSuchFieldException {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handlerAdapter = applicationContext.getBean(AnnotationMethodHandlerAdapter.class);
// Does not work, the problem is here...
controller = applicationContext.getBean(T);
}
}
The idea is to create, for each controller I want to test a JUnit test class that extends my AbstractSpringMVCControllerTest. The type given in the extends declaration is the class of the Controller.
For example, if I want to test my AccountController, I will create the AccountControllerTest class like that:
public class AccountControllerTest extends AbstractSpringMVCControllerTest<AccountController> {
#Test
public void list_accounts() throws Exception {
request.setRequestURI("/account/list.html");
ModelAndView mav = handlerAdapter.handle(request, response, controller);
...
}
}
My problem is located in the last line of the initContext() method of the abstract class. This abstract class declares the controller object as a T object, but how can say to the Spring Application Context to return the bean of type T?
I've tried something like that:
Class<?> controllerClass = this.getClass().getSuperclass().getDeclaredField("controller").getType();
controller = (T) applicationContext.getBean(controllerClass);
but controllerClass returns the java.lang.Object.class class, not AccountController.class.
Of course, I can create a public abstract Class<?> getControllerClass(); method, which will be overriden by each JUnit Controller test class, but I prefer to avoid this solution.
So, any idea?
This is possible if your subclasses of AbstractSpringMVCControllerTest bind T at compile time. That is, you have something like
public class DerpControllerTest extends AbstractSpringMVCControllerTest<DerpController> { }
rather than
public class AnyControllerTest<T> extends AbstractSpringMVCControllerTest<T> { }
I'm guessing you probably have the former. In this case, the type of T is erased from the Class object for AbstractSpringMVCControllerTest at runtime, but the Class object for DerpControllerTest does provide a way to know what T is, since it bound T at compile time.
The following classes demonstrate how to access the type of T:
Super.java
import java.lang.reflect.ParameterizedType;
public abstract class Super<T> {
protected T object;
#SuppressWarnings("unchecked")
public Class<T> getObjectType() {
// This only works if the subclass directly subclasses this class
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
Sub.java
public class Sub extends Super<Double> {
}
Test.java
public class Test {
public static void main(String...args) {
Sub s = new Sub();
System.out.println(s.getObjectType()); // prints "Class java.lang.Double"
}
}
This is different from the type erasure we normally see. With type erasure, we don't know the parameter of the current class (the one you get with getClass()), but you can get those in super class / super interface (those you get with getGenericSuperxxxxx()) because this is part of the type declaration.
This won't give your the type of controller field, but I hope this is enough for your purpose.
Code:
public class A<P> {
}
import java.lang.reflect.ParameterizedType;
public class B extends A<String> {
public static void main(String[] arg) {
System.out.println(
((ParameterizedType)B.class.getGenericSuperclass()).getActualTypeArguments()[0]);
}
}
Output:
class java.lang.String
In your case, it would be
Class controllerClass = (Class)( ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
Something to notes:
If the class B is also parameterized like this:
public class B<X> extends A<X> {}
This won't work. Or if you have another class extends B, it will have problem too. I won't go into all those cases, but you should get the idea.
You can't because at runtime, due to ERASURE, the JVM cannot know the class of your "controller" attribute. It is considered as Object...
When I try to mock a Dao using Jukito I get the following exception:
java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
at org.jukito.JukitoModule.addKeyDependency(JukitoModule.java:338)
at org.jukito.JukitoModule.addInjectionPointDependencies(JukitoModule.java:330)
at org.jukito.JukitoModule.addDependencies(JukitoModule.java:313)
The object I try to mock is a ConcreteDao.
public class ConcreteDao extends AbstractDao<MyDomain> {
}
public abstract class AbstractDao<T extends DatastoreObject> {
}
I read several posts on SO about this binding generics but I can't figure out a way to use TypeLiteral for my binding.
This is what I tried:
bind(new TypeLiteral<AbstractDao<MyDomain>>(){}).to(ConcreteDao.class);
You need to bind like this:
bind(new TypeLiteral<AbstractDao<MyDomain>>(){}).to(new TypeLiteral<ConcreteDao<MyDomain>>(){});
This is how you can retrieve the generic class:
class AbstractDao {
protected final Class<T> clazz;
#Inject
public AbstractDao(TypeLiteral<T> type) {
clazz = (Class<T>) type.getRawType();
}
}
Subclasses of AbstractDao will need to pass entity specific TypeLiterals to the parent class (AbstractDao):
class ConcreteDao extends AbstractDao<MyDomain> {
#Inject
public ConcreteDao(TypeLiteral<MyDomain> type) {
super(type);
}
}
Note that you can make your AbstractDao class non-abstract and implement basic CRUD operations, so that you can use it without the need to extend AbstractDao for each entity. You will just need a binding for each entity like this:
bind(new TypeLiteral<GenericDao<User>>(){}).in(Scopes.SINGLETON);
See my blog-post for more information.
If I have the following class:
public class ObjectDAOMongoDBImpl<T> extends GenericDAOMongoDBImpl<T, ObjectId> implements ObjectDAO<T> {
public ObjectDAOMongoDBImpl(Class<T> entityClass, Mongo mongo, Morphia morphia, String dbName) {
super(entityClass, mongo, morphia, dbName);
}
}
Where, entityClass is provided at run-time - how can I use guice to bind the said type to an interface?
public class RunnerModule extends AbstractModule {
#Override
protected void configure() {
bind(GenericDAO.class).to(ObjectDAOMongoDBImpl.class);
}
}
public class Runner<T, V> {
GenericDAO<T, V> dao;
#Inject
public Runner(GenericDAO<T, V> dao) {
this.dao = dao;
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new RunnerModule());
injector.getInstance(Runner.class);
}
}
It's fine to define mongo, morphia, and dbName as literals to RunnerModule (is there a cleaner way?), but I have no way of knowing what entityClass is until runtime.
This isn't doable with Guice idiomatically, and it isn't its primary focus either.
jfpoilpret have said everything that can be said, but I would like to approach the problem from another direction, where you have the option to (possibly) solve your problem by losing type-safety.
So, in your code, you ask Guice to get an instance of your Runner<T, V> class like this
injector.getInstance(Runner.class);
but this can't be resolved by Guice, because Runner<T, V> has a dependency on GenericDAO<T, V>, but you didn't bind an exact implementation for it. So as jfpoilpret has said, you have to bind some concrete implementations for it in your module.
I'm guessing that you want to determine the exact GenericDAO<T, V> implementation that you pass to your Runner<T, V> based on some input data, which data's type isn't known at compile time. Now, let's assume you have two implementations.
bind(new TypeLiteral<GenericDAO<String, ObjectID>>(){}).to(StringDAO.class);
bind(new TypeLiteral<GenericDAO<Double, ObjectID>>(){}).to(IntegerDAO.class);
Based on different type of inputs you can do this
Injector injector = Guice.createInjector(new RunnerModule());
// possible input which you get from *somewhere* dynamically
Object object = 1.0;
TypeLiteral<?> matchedTypeLiteral = null;
for (Key<?> key : injector.getAllBindings().keySet()) {
TypeLiteral<?> typeLiteral = key.getTypeLiteral();
Type type = typeLiteral.getType();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
if (parameterizedType.getRawType() == GenericDAO.class) {
List<Type> actualTypeArguments = Arrays.asList(parameterizedType.getActualTypeArguments());
if (actualTypeArguments.get(0) == object.getClass())
matchedTypeLiteral = typeLiteral;
}
}
};
Runner<?, ?> runner = new Runner<>((GenericDAO<?, ?>) injector.getInstance(Key.get(matchedTypeLiteral)));
System.out.println(runner.dao.getClass()); // IntegerDAO.class
If Object object = "string";, then the other implementation will be found. This is of course rather ugly and can be improved with checking for sub-classes and stuff, but I think you get the idea. The bottom-line is that you can't get around this.
If you manage to do it (getting around it), please drop me an e-mail because I would like to know about it! I had faced the same problem as you're facing not too long ago. I've written a simple BSON codec where I wanted to load specific implementations of a generic interface based on the type of some arbitrary input. This worked well with Java-to-BSON mappings, but I couldn't do it the other way around in any sensible way, so I've opted for a simpler solution.
The way you wrote it, entityClass can only be Object.class (== Class<Object>), and nothing else.
Hence, first of all, your ObjectDAOMongoDBImpl should be generic:
public class ObjectDAOMongoDBImpl<T>
extends GenericDAOMongoDBImpl<T, ObjectId> ...
That part of the problem is related to java, not Guice.
Now for Guice part, you need to define a binding including the generic types, ie by using Guice TypeLiteral:
bind(new TypeLiteral<GenericDAO<T, V>>(){}).to(...);
where T and V must be known in the code above (can't just be generic parameters there).
Looking at this question may also give you further details related to your situation.
This question is a bit old, but I've recently ran into a similar problem, and managed to solve it quite elegantly by adding a tiny extra layer, a factory.
Consider the following repository:
public interface Repository<T extends Model<T>> {
void save(T t);
T load(long key);
}
class SomeDbRepositoryImpl<T extends Model<T>> implements Repository<T> {
private final SomeDbConnection db;
private final Class<T> type;
RepositoryImpl(final Class<T> type, final SomeDbConnection db) {
this.db = db;
this.type = type;
}
...
}
Then, suppose I have a service that needs an instance of Repository<User>. My first try was to try to make Guice pass an instance of Repository<User> in the constructor, and then I'd bind it somehow. The problem is that I really don't want to have to add repository bindings and providers for each model. If I were to do that, the code would look like this:
// Won't work.
class MyService {
private final Repository<User> userRepository;
#Inject MyService(final Repository<User> userRepository) {
this.userRepository = userRepository;
}
...
}
What I ended up doing is creating a RepositoryFactory class, which is not generic by itself, but it contains a generic method.
public interface RepositoryFactory {
<T extends Model<T>> Repository<T> getRepository(Class<T> type);
}
class SomeDbRepositoryFactoryImpl implements RepositoryFactory {
private final SomeDbConnection db;
#Inject SomeDbRepositoryFactoryImpl(final SomeDbConnection db) {
this.db = db;
#Override <T extends Model<T>> Repository<T> getRepository(Class<T> type) {
return new SomeDbRepositoryImpl(type, db);
}
}
So, this is completely type-safe, and I don't have to add a binding for each module. The service that uses a Repository would then look like:
class MyService {
private final Repository<User> userRepository;
#Inject MyService(final RepositoryFactory f) {
this.userRepository = f.getRepository(User.class);
}
...
}
You could also keep the instance of RepositoryFactory instead of already getting the Repository instance.
I hope this can be useful to someone.
Beyond what Kohányi has said, you could load the DAO or entity classes reflectively by name, and then bind only the specific types asked for in the command-line arguments:
package com.example;
public class App
{
public static void main(final String[] args)
{
final Injector appleInjector = Guice.createInjector(new DynamicDaoModule(getClass("com.example.AppleDao")));
appleInjector.getInstance(Runner.class);
final Injector orangeInjector = Guice.createInjector(new DynamicDaoModule( getClass("com.example.OrangeDao")));
orangeInjector.getInstance(Runner.class);
// final Injector commandLineInjector = Guice.createInjector(new DynamicDaoModule(getClass(args[0])));
// commandLineInjector.getInstance(Runner.class);
}
private static Class getClass(final String className)
{
try
{
return Class.forName(className);
}
catch (final ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
}
class DynamicDaoModule extends AbstractModule
{
private final Class<? extends GenericDao<? extends Entity>> daoClass;
public DynamicDaoModule(final Class<? extends GenericDao<? extends Entity>> daoClass)
{
this.daoClass = daoClass;
}
#Override
protected void configure()
{
// bind GenericDao<? extends Entity> to daoClass
final TypeLiteral<GenericDao<? extends Entity>> daoOfEntity = (TypeLiteral) TypeLiteral.get(Types.newParameterizedType(GenericDao.class, Types.subtypeOf(Entity.class)));
bind(daoOfEntity).to(daoClass);
}
}
interface Entity
{
}
class Apple implements Entity
{
}
class Orange implements Entity
{
}
class Runner
{
#Inject
public Runner(final GenericDao<? extends Entity> dao)
{
System.out.println("This runner has an " + dao);
}
}
class GenericDao<T extends Entity>
{
private final Class<? extends Entity> entityClass;
protected GenericDao(final Class<? extends Entity> entityClass)
{
this.entityClass = entityClass;
}
#Override
public String toString()
{
return String.format("%s constructed with entityClass %s", getClass().getSimpleName(), entityClass.getSimpleName());
}
}
class AppleDao extends GenericDao<Apple>
{
#Inject
public AppleDao()
{
super(Apple.class);
}
}
class OrangeDao extends GenericDao<Orange>
{
#Inject
public OrangeDao()
{
super(Orange.class);
}
}
And the output would be
This runner has an AppleDao constructed with entityClass Apple
This runner has an OrangeDao constructed with entityClass Orange
I have changed the example to have entity classes implement an interface, in case they share some functionality that would be useful to Runner or GenericDao. If in fact you don't have such an interface, the technique also works with entity classes like String and Double if you remove the extends Entity upper bounds (e.g. GenericDao<T>).
I also removed the <T> parameter on Runner, as that was providing no benefit due to type erasure. If you were to subclass Runner<T>, then you might have Guice provide AppleRunner extends Runner<Apple> or OrangeRunner extends Runner<Orange>. But the type parameter provides nothing if Runner itself is the only concrete class Guice will be providing.
EDIT Oops, I left the class injections in. They are removed now. Of course, if you have a concrete GenericDao subclass for each entity, then perhaps you don't need to inject the entity classes themselves.
I suppose it's not clear to me whether you can provide concrete GenericDao subclasses for all the entity types ahead of time or not. If not, and you were only using the GenericDao class itself for each different kind of entity class, then you would want to inject concrete entity classes and not concrete DAO classes.