3

I have a simple spring boot application in which I have a jpa repository object which i want to autowire inside an @Configuration class like below.

@Configuration
public class Appconfig {

    @Bean
    @Autowired
    public  PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(OctopusPropertiesRepository repo) {
        PropertySourcesPlaceholderConfigurer property = new PropertySourcesPlaceholderConfigurer();
        Map<String,Object> props = new ConcurrentHashMap<>();
        List<OctopusProperties> loadedSettings = repo.findAll();
        loadedSettings.forEach(entry -> props.put(entry.getKey(), entry.getValue()));
        MutablePropertySources mutablePropertySources = new MutablePropertySources();
        mutablePropertySources.addFirst(new MapPropertySource("custom", props));
        property.setPropertySources(mutablePropertySources);
        return property;
    }
}

And here is the @Repository class

@Repository
public interface OctopusPropertiesRepository extends JpaRepository<OctopusProperties, Long> {
}

And I get following exception.

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.example.app.OctopusPropertiesRepository]

If i don't make that configuration and boot app successfully i see from the actuator that bean is available. What is the problem here? why I can't wire @Repository inside @Configuration.

P.S. These two java files are under same folder: com.example.app

P.P.S. Eclipse view of my project:

enter image description here

Vladimir Vagaytsev
  • 2,871
  • 9
  • 33
  • 36
cacert
  • 2,677
  • 10
  • 33
  • 56
  • 1
    include class which is scanning for components. Please also include packages into listings. Probably main class? – luboskrnac Aug 03 '16 at 12:13
  • Main class is in root package, and all other classes in the same package. I think its somewhat related with the timing, because spring is generating this bean if i dont use this configuration. – cacert Aug 03 '16 at 12:16
  • If you add a simple member, like below, in the configuration class does Spring find it? @Autowired OctopusPropertiesRepository octopusPropertiesRepository – Alexandru Marina Aug 03 '16 at 12:18
  • no, i tried it but no change :( – cacert Aug 03 '16 at 12:19
  • @luboskrnac I have added the screen shot – cacert Aug 03 '16 at 12:31

3 Answers3

1

According to the @Bean javadoc:

BeanFactoryPostProcessor-returning @Bean methods
Special consideration must be taken for @Bean methods that return Spring 
BeanFactoryPostProcessor (BFPP) types. Because BFPP objects must be    
instantiated very early in the container lifecycle, they can interfere with
processing of annotations such as @Autowired, @Value, and @PostConstruct
within @Configuration classes. To avoid these lifecycle issues, mark BFPP
returning @Bean methods as static. For example: 

 @Bean
 public static PropertyPlaceholderConfigurer ppc() {
     // instantiate, configure and return ppc ...
 }


By marking this method as static, it can be invoked without causing 
instantiation of its declaring @Configuration class, thus avoiding the above-
mentioned lifecycle conflicts. Note however that static @Bean methods will not   
be enhanced for scoping and AOP semantics as mentioned above. This works out
in BFPP cases, as they are not typically referenced by other @Bean methods. As
a reminder, a WARN-level log message will be issued for any non-static @Bean
methods having a return type assignable to BeanFactoryPostProcessor.

So according to this, your propertySourcesPlaceholderConfigurer() should be static.

Edit: Sorry for the late edit. I don't think you can use Hibernate repository here, because BeanFactoryPostProcessor gets instantiated before any dependency needed for hibernate and jpa. Perhaps you could use JdbcTemplate here, at least that worked for me:

@Configuration
public class Appconfig {

    @Bean
    public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer property = new PropertySourcesPlaceholderConfigurer();
        Map<String, Object> props = new ConcurrentHashMap<>();
        List<OctopusProperties> loadedSettings = getAll();
        loadedSettings.forEach(entry -> props.put(entry.getKey(), entry.getValue()));
        MutablePropertySources mutablePropertySources = new MutablePropertySources();
        mutablePropertySources.addFirst(new MapPropertySource("custom", props));
        property.setPropertySources(mutablePropertySources);
        return property;
    }

    @Bean
    public static DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        return dataSource;
    }

    private List<OctopusProperties> getAll() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
        List<OctopusProperties> all = jdbcTemplate.query("select id,key_column,value_column from octopus_properties",
                new RowMapper<OctopusProperties>() {
                    @Override
                    public OctopusProperties mapRow(final ResultSet rs, final int rowNum) throws SQLException {
                        OctopusProperties op = new OctopusProperties();
                        op.setId(rs.getLong("id"));
                        op.setKey(rs.getString("key_column"));
                        op.setValue(rs.getString("value_column"));
                        return op;
                    }
                });
        return all;
    }

}

Please note, that I've used MySQL here, so the datasource is configured accordingly.

gybandi
  • 1,880
  • 1
  • 11
  • 14
1

Use @PostConstruct.

@Autowired PropertySourcesPlaceholderConfigurer property;


@Autowired OctopusPropertiesRepository repo;


   @PostConstruct
    public  void onInit() {

        Map<String,Object> props = new ConcurrentHashMap<>();
        List<OctopusProperties> loadedSettings = repo.findAll();
        loadedSettings.forEach(entry -> props.put(entry.getKey(), entry.getValue()));
        MutablePropertySources mutablePropertySources = new MutablePropertySources();
        mutablePropertySources.addFirst(new MapPropertySource("custom", props));
        property.setPropertySources(mutablePropertySources);

    }
Beñat Bermejo
  • 361
  • 1
  • 9
0

A similar problem is addressed in this question

Is it permissible to change your AppConfig class like this?

@Configuration
public class Appconfig {

    @Autowired
    private OctopusPropertiesRepository repo;

    @Bean
    public  PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer property = new PropertySourcesPlaceholderConfigurer();
        Map<String,Object> props = new ConcurrentHashMap<>();
        List<OctopusProperties> loadedSettings = repo.findAll();
        loadedSettings.forEach(entry -> props.put(entry.getKey(), entry.getValue()));
        MutablePropertySources mutablePropertySources = new MutablePropertySources();
        mutablePropertySources.addFirst(new MapPropertySource("custom", props));
        property.setPropertySources(mutablePropertySources);
        return property;
    }
}

Do revert if this solution works or not.

Community
  • 1
  • 1
Raman Sahasi
  • 30,180
  • 9
  • 58
  • 71