Автоматическая аннотация не работает внутри пользовательского преобразователя бульдозера.

Я использую пользовательский конвертер next dozer

public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {

    @Autowired
    private AppConfig appConfig;

    public MyCustomDozerConverter() {
        super(MyObject.class, String.class);
    }

    @Override
    public String convertTo(MyObject source, String destination) {      
        String myProperty = appConfig.getWhatever();
        // business logic
        return destination;
    }

    @Override
    public MyObject convertFrom(String source, MyObject destination) {
        // business logic
        return null;
    }
}

Моя проблема заключается в том, что когда он проходит через метод convertTo внутри преобразователя, я всегда получаю экземпляр appConfig с нулевым значением, что, конечно, вызывает исключение нулевого указателя.

Примечание. В моем весеннем классе загрузки есть эти аннотации выше:

@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan({"com.xxx"})
@EntityScan("com.xxx")
@EnableJpaRepositories("com.xxx")

person mibrahim.iti    schedule 25.11.2017    source источник
comment
Что создает экземпляр MyCustomDozerConverter? Если Spring этого не делает, то внедрение зависимостей не сработает.   -  person Andy Wilkinson    schedule 26.11.2017
comment
Да, экземпляр создается не весной, похоже, это делает бульдозер, и поэтому @Autowired не работает, но я прошу предложений или приемов для решения этой проблемы, понял?   -  person mibrahim.iti    schedule 26.11.2017
comment
Есть лучшая альтернатива, чем принятый ответ. См. Пользовательский преобразователь с внедрением зависимостей Spring   -  person candicetdh    schedule 26.09.2019


Ответы (3)


Я решил это с помощью следующего трюка:

1- Использование статики со свойством appConfig.

2- создайте экземпляр с помощью spring, поэтому, когда dozer использует пустой конструктор по умолчанию, он обнаружит, что appConfig уже имеет значение (которое ранее было назначено ему Spring)

И вот код, который я использовал для этого:

@Component //import org.springframework.stereotype.Component;
public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {

    private static AppConfig appConfig;

    // dozer needs this constructor to create an instance of converter (so it's a mandatory constructor)
    public MyCustomDozerConverter() {
        super(MyObject.class, String.class);
    }

    @Autowired // Spring will pass appConfig to constructor
    public MyCustomDozerConverter(AppConfig appConfig) {
        this();
        this.appConfig = appConfig;
    }

    @Override
    public String convertTo(MyObject source, String destination) {      
        String myProperty = appConfig.getWhatever();
        // business logic
        return destination;
    }

    @Override
    public MyObject convertFrom(String source, MyObject destination) {
        // business logic
        return null;
    }
}

ОБНОВЛЕНИЕ: еще одно решение

Еще одна хитрость заключается в использовании Spring ApplicationContextAware для получения одноэлементного объекта из метода getBean:

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static ApplicationContext getContext() {
        return context;
    }
}

Затем создайте статический метод внутри класса AppConfig и верните экземпляр одного компонента, соответствующего требуемому типу:

import org.springframework.context.annotation.Configuration;
import com.tripbru.ms.experiences.ApplicationContextHolder;

@Configuration
public class AppConfig {

    // Static method used to return an instatnce
    public static AppConfig getInstance() {
        return ApplicationContextHolder.getContext().getBean(AppConfig.class);
    }

    // Properties
}

Затем вызовите его непосредственно внутри преобразователя бульдозера с помощью AppConfig.getInstance();

public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {

    private static AppConfig appConfig;

    public MyCustomDozerConverter() {
        super(MyObject.class, String.class);
        appConfig = AppConfig.getInstance(); // Here are we intializing it by calling the static method we created.
    }

    @Override
    public String convertTo(MyObject source, String destination) {      
        String myProperty = appConfig.getWhatever();
        // business logic
        return destination;
    }

    @Override
    public MyObject convertFrom(String source, MyObject destination) {
        // business logic
        return null;
    }
}
person mibrahim.iti    schedule 25.11.2017

Попробуйте внедрение зависимостей конструктора

private AppConfig appConfig;
@Autowired
MyCustomerDozerConverter(AppConfig appConfig)
{
    this.appConfig = appConfig;
}
person Amr Alaa    schedule 25.11.2017
comment
К сожалению, это не работает, и это логично, потому что dozer создает экземпляр конвертера, поэтому хитрость заключается не только в использовании этого, но и в использовании статического ключевого слова с AppConfig, пожалуйста, проверьте мой ответ, который я добавил. Спасибо за Ваш ответ :) - person mibrahim.iti; 26.11.2017

Вы можете поместить следующую строку в CustomConverter, чтобы Spring автоматически подключил ее.

public class MyCustomDozerConverter extends DozerConverter<MyObject, String> {

@Autowired
private AppConfig appConfig;

public MyCustomDozerConverter() {
    super(MyObject.class, String.class);
    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
...
}
person Srinivas Lakshman    schedule 05.09.2018