How to autowire mongoTemplate into custom type con

2019-05-14 23:12发布

I'm trying to create a converter that will fetch object from DB by it's ObjectId. But the mongoTemplate is always empty in converter:

org.springframework.core.convert.ConversionFailedException:

Failed to convert from type org.bson.types.ObjectId to type com.atlas.mymodule.datadomain.MyObject for value '130000000000000000000013';

nested exception is java.lang.NullPointerException

Code:

@Component
public class ObjectIdToMyObjectConverter implements Converter<ObjectId, MyObject> {

    @Autowired
    private MongoTemplate mongoTemplate; // null ???

    public MyObject convert(ObjectId objectId) {
        return mongoTemplate.findById(objectId, MyObject.class); // <- NullPointerException
    }
}

Configuration:

@Configuration
@ComponentScan
@EnableMongoRepositories
public abstract class MyModuleConfiguration extends AbstractMongoConfiguration {

    @Override
    public MongoClient mongo() throws Exception {
        List<MongoCredential> mongoCredential = getMongoCredentials();
        return mongoCredential == null ?
            new MongoClient(getMongoServerAddresses()) :
            new MongoClient(getMongoServerAddresses(), mongoCredential, getMongoClientOptions());
    }

    protected abstract List<MongoCredential> getMongoCredentials();

    protected abstract MongoClientOptions getMongoClientOptions();

    protected abstract List<ServerAddress> getMongoServerAddresses() throws UnknownHostException;

    @Bean
    public ObjectIdToMyObjectConverter objectIdToMyObjectConverter() {
        return new ObjectIdToMyObjectConverter());
    }

    @Override
    public CustomConversions customConversions() {
        List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
        converters.add(objectIdToMyObjectConverter());

        return new CustomConversions(converters);
    }
}

Test Configuration:

public class MyModuleTestConfiguration extends MyModuleConfiguration {
  // overrides abstract methods, defines connection details...
}

update:

I've updated the code according to @mavarazy suggestion (added ObjectIdToMyObjectConverter bean definition) but got an exception:

Error creating bean with name 'mongoTemplate': Requested bean is currently in creation: Is there an unresolvable circular reference?

Full exception:

Error creating bean with name 'mongoTemplate' defined in com.atlas.MyModule.MyModuleTestConfiguration: 
    Bean instantiation via factory method failed;

nested exception is org.springframework.beans.BeanInstantiationException: 
    Failed to instantiate [org.springframework.data.mongodb.core.MongoTemplate]: Factory method 'mongoTemplate' threw exception; 

nested exception is org.springframework.beans.factory.BeanCreationException: 
    Error creating bean with name 'mappingMongoConverter' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed; 

nested exception is org.springframework.beans.BeanInstantiationException: 
    Failed to instantiate [org.springframework.data.mongodb.core.convert.MappingMongoConverter]: Factory method 'mappingMongoConverter' threw exception; 

nested exception is org.springframework.beans.factory.BeanCreationException: 
    Error creating bean with name 'mongoMappingContext' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed;

nested exception is org.springframework.beans.BeanInstantiationException: 
    Failed to instantiate [org.springframework.data.mongodb.core.mapping.MongoMappingContext]: Factory method 'mongoMappingContext' threw exception; 

nested exception is org.springframework.beans.factory.BeanCreationException: 
    Error creating bean with name 'customConversions' defined in com.atlas.MyModule.MyModuleTestConfiguration: Bean instantiation via factory method failed; 

nested exception is org.springframework.beans.BeanInstantiationException: 
    Failed to instantiate [org.springframework.data.mongodb.core.convert.CustomConversions]: Factory method 'customConversions' threw exception; 

nested exception is org.springframework.beans.factory.BeanCreationException: 
    Error creating bean with name 'objectIdToMyObjectConverter': Injection of autowired dependencies failed; 

nested exception is org.springframework.beans.factory.BeanCreationException: 
    Could not autowire field: private org.springframework.data.mongodb.core.MongoTemplate com.atlas.MyModule.ObjectIdToMyObjectConverter.mongoTemplate; 

nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
    Error creating bean with name 'mongoTemplate': Requested bean is currently in creation: Is there an unresolvable circular reference?

Thanks.

1条回答
Lonely孤独者°
2楼-- · 2019-05-15 00:00

ObjectIdToMyObjectConverter is not a spring bean. If you want @Autowired to work, create ObjectIdToMyObjectConverter as Spring bean, like this:

@Bean
public ObjectIdToMyObjectConverter objectIdToMyObjectConverter() {
    return new ObjectIdToMyObjectConverter());
}

and @Autowire it in your configuration.

Following @Savash update

I have not paid enough attention to your configurations.

What you see is happening because you are trying to create MongoTemplate, which depends on CustomConversions, and at the same time CustomConversions depend on MongoTemplate, spring can't and should not do that.

As a solution:

  1. You can create your CustomConversions with ApplicationContextAware, and extract MongoTemplate reference lazily on a first call.
  2. I thought you are using CustomConversions as part of spring-integration or something. If so it does not need to be part of converters for Mongo. If you need it as MongoConverters, you are doing something really strange.

What is exact use case, you need this for?

Following comments:

Do I understand right, that you want MongoTemplate to read object with user reference as User object, and write object with User value as user reference?

I think.

  1. You have a bad data model (you are trying to emulate JOIN operation in your MongoTemplate, which means you are missing something in your data model, and this is not how you should work with mongo).
  2. Just call User explicitly when you need it, don't overload your DB with additional work, you'll have problems with performance
  3. You can use another object, which you'll enrich with current user, as needed
  4. Maybe SQL & ORM like Hibernate is a better approach for you ?
  5. Try Hibernate OGM for your purpose, it might provide functionality, you need (Not sure, though, have not worked with it)
查看更多
登录 后发表回答