Search code examples
javaspringspring-bootdozer

Autowired annotation doesn't work inside custom dozer converter


I am using next dozer custom converter

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

My problem is when it goes through convertTo method inside the converter, I always got appConfig instance with null value which of course cause a null pointer exception

Note: my spring boot class have these annotations above:

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

Solution

  • I solved this by next trick:

    1- Using static with appConfig property.

    2- instantiate it by spring so when dozer use default empty constructor it will find appConfig have a value already (which assigned before to it by spring)

    And here are the code i used for this:

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

    UPDATE: Another solution

    Another trick is using Spring ApplicationContextAware to get a singleton object from getBean method:

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

    Then create a static method inside AppConfig class and return an instance of the single bean matching the required type:

    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
    }
    

    Then calling it direct inside the dozer converter by 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;
        }
    }