2

My Spring Boot application has a converter registered to ConversionService which is dependent on a service, which in turn depends on a Dao. The Dao depends on @ConfigurationProperties annotated object injected as a constructor parameter named hardwareProperties which depends on ConversionService. How can this cycle be broken?

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  conversionService defined in class path resource [com/xxxx/services/AppConfiguration.class]
↑     ↓
|  activityMessageConverter (field private com.xxxx.services.cp.services.interfaces.SubscriptionService com.xxxx.services.cp.converters.ActivityMessageConverter.subscriptionService)
↑     ↓
|  subscriptionServiceImpl defined in file [D:\projectXXX\target\classes\com\xxxx\services\cp\services\SubscriptionServiceImpl.class]
↑     ↓
|  subscriptionDao defined in file [D:\projectXXX\target\classes\com\xxxx\services\cp\dao\SubscriptionDao.class]
↑     ↓
|  hardwareProperties
└─────┘

package com.xxxx.services;
import com.xxxx.services.cp.converters.ActivityMessageConverter;
public class AppConfiguration {

    @Bean
    public ConversionServiceFactoryBean conversionService(ActivityMessageConverter activityMessageConverter) {
        ConversionServiceFactoryBean factoryBean = new ConversionServiceFactoryBean();
        factoryBean.setConverters(Set.of(activityMessageConverter));
        factoryBean.afterPropertiesSet();
        return factoryBean;
    }
}

package com.xxxx.services.cp.converters;
import com.xxxx.services.cp.services.interfaces.SubscriptionService;
@Component
public class ActivityMessageConverter implements Converter<Message, Activity> { 

    @Autowired
    @Qualifier("subscriptionServiceImpl")
    private SubscriptionService subscriptionService;

    public SubscriptionService getSubscriptionService() {
        return subscriptionService;
    }

    public void setSubscriptionService(SubscriptionService subscriptionService) {
        this.subscriptionService = subscriptionService;
    }

    @Override
    public Activity convert(Message message) {
        // Implementation
    }
}


package com.xxxx.services.cp.services;
@Component
public class SubscriptionServiceImpl implements SubscriptionService {

    private SubscriptionService subscriptionDAO;

    public SubscriptionServiceImpl(@Qualifier(value = "subscriptionDao") SubscriptionService subscriptionDAO) {
        setSubscriptionDAO(subscriptionDAO);
    }

    public SubscriptionService getSubscriptionDAO() {
        return subscriptionDAO;
    }

    public void setSubscriptionDAO(SubscriptionService subscriptionDAO) {
        this.subscriptionDAO = subscriptionDAO;
    }
}

package com.xxxx.services.cp.dao;
import com.xxxx.services.cp.services.interfaces.SubscriptionService;
@Component
@Repository
public class SubscriptionDao implements SubscriptionService {

    private HardwareProperties hardwareProperties;

    public SubscriptionDao(HardwareProperties hardwareProperties) {
        this.hardwareProperties = hardwareProperties;       
    }
}

package com.xxxx.services.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:hardware.properties")
@ConfigurationProperties(prefix = "hardware.api")
public class HardwareProperties {
    private String host;
    private Integer port;
    private String username;
    private String password;

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}

// Usage of conversionService.
processMessages(context,
                filteredMessages.stream()
                        .map(message -> this.conversionService.convert(message, Activity.class))
                        .collect(Collectors.toList()));
user2176499
  • 363
  • 3
  • 16
  • Why are you passing like parameter ActivityMessageConverter in the bean Definition in the class AppConfiguration if you no use longer – tino89 Apr 23 '20 at 05:17
  • @tino89 The conversionService is used elsewhere. processMessages(context, filteredMessages.stream() .map(message -> this.conversionService.convert(message, Activity.class)) .collect(Collectors.toList())); – user2176499 Apr 23 '20 at 05:30
  • @tino89 Corrected my code. Apology for the mistake. – user2176499 Apr 23 '20 at 05:38

1 Answers1

0

You try modifying AppConfiguration's method by the snnipet:

@Bean
public ConversionServiceFactoryBean conversionService() {
    ConversionServiceFactoryBean factoryBean = new ConversionServiceFactoryBean();
    factoryBean.setConverters(Set.of(activityMessageConverter));
    factoryBean.afterPropertiesSet();
    return factoryBean;
}
tino89
  • 142
  • 5
  • I have modified my question to reflect your comment. Also, your snippet is missing a function argument. – user2176499 Apr 23 '20 at 05:57
  • Also, the crux of the question is the cyclic beans dependencies injection instead of the initialization of the conversionService bean. – user2176499 Apr 23 '20 at 05:58