Как автоматически подключить mongoTemplate в конвертер пользовательских типов?

Я пытаюсь создать конвертер, который будет извлекать объект из БД по его ObjectId. Но mongoTemplate всегда пуст в конвертере:

org.springframework.core.convert.ConversionFailedException:

Не удалось преобразовать тип org.bson.types.ObjectId в тип com.atlas.mymodule.datadomain.MyObject для значения '130000000000000000000013';

Вложенным исключением является java.lang.NullPointerException

Код:

@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
@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);
    }
}

Тестовая конфигурация:

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

Обновить:

Я обновил код в соответствии с предложением @mavarazy (добавлено определение компонента ObjectIdToMyObjectConverter), но получил исключение:

Ошибка создания bean-компонента с именем mongoTemplate: запрашиваемый bean-компонент находится в процессе создания: существует неразрешимая циклическая ссылка?

Полное исключение:

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?

Благодарю.

1 ответ

ObjectIdToMyObjectConverter не является пружинным компонентом. Если вы хотите, чтобы @Autowired работал, создайте ObjectIdToMyObjectConverter как бин Spring, например:

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

и @Autowire это в вашей конфигурации.

После обновления @Savash

Я не уделил достаточного внимания вашим настройкам.

То, что вы видите, происходит потому, что вы пытаетесь создать MongoTemplate, который зависит от CustomConversions, и в то же время CustomConversions зависят от MongoTemplate, Spring не может и не должна этого делать.

Как решение:

  1. Вы можете создавать свои CustomConversions с ApplicationContextAware и извлекать ссылку MongoTemplate лениво при первом вызове.
  2. Я думал, что вы используете CustomConversions как часть Spring-интеграции или что-то. Если это так, он не должен быть частью конвертеров для Монго. Если вам это нужно как MongoConverters, вы делаете что-то действительно странное.

Для чего нужен конкретный вариант использования?

Следующие комментарии:

Правильно ли я понимаю, что вы хотите, чтобы MongoTemplate считывал объект со ссылкой на пользователя в качестве объекта пользователя и записывал объект со значением пользователя в качестве ссылки на пользователя?

Я думаю.

  1. У вас плохая модель данных (вы пытаетесь эмулировать операцию JOIN в вашем MongoTemplate, что означает, что вы что-то упустили в своей модели данных, и это не то, как вы должны работать с mongo).
  2. Просто вызовите пользователя явно, когда вам это нужно, не перегружайте свою БД дополнительной работой, у вас будут проблемы с производительностью
  3. При необходимости вы можете использовать другой объект, который вы обогатите текущим пользователем.
  4. Может быть, SQL & ORM, такой как Hibernate, лучше для вас?
  5. Попробуйте Hibernate OGM для своих целей, он может обеспечить функциональность, которая вам нужна (но не уверен, что не работал с ней)
Другие вопросы по тегам