Issue with injecting a dependency into a bean itself used by a @Configuration class loaded on startup

StackOverflow https://stackoverflow.com/questions/23302261

Question

I want for a @Configuration class to manipulate a Map of Spring beans (here the beans will be the values for the Map).

Moreover, some Spring beans will have dependencies injected into them - such as Spring Data Jpa repositories.

I have the following configuration class (MethodSecurityConfiguration):

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {

    @Override
    protected AuthenticationManager authenticationManager() {
        AuthenticationManager authenticationManager = new ProviderManager();
        return authenticationManager;
    }

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(permissionEvaluator());
        return expressionHandler;
    }

    @Bean
    public ApplicationPermissionEvaluator permissionEvaluator() {
        return new ApplicationPermissionEvaluator(permissionMap());
    }

    private Map<String, Permission> permissionMap() {
        Map<String, Permission> map = new HashMap<>();
        map.put("updateAdvertisementIsAllowed", advertisementOwnerPermission());
        map.put("advertisementByIdOwnerPermission", advertisementByIdOwnerPermission());
        return map;
    }

    @Bean
    public AdvertisementOwnerPermission advertisementOwnerPermission() {
        return new AdvertisementOwnerPermission();
    }

    @Bean
    public AdvertisementByIdOwnerPermission advertisementByIdOwnerPermission() {
        return new AdvertisementByIdOwnerPermission();
    }

}

This will be the value for the above Map (AdvertisementByIdOwnerPermission):

@Component
public class AdvertisementByIdOwnerPermission implements Permission {

    @Autowired
    private AdvertisementRepository advertisementRepository;

    @Override
    public boolean isAllowed(Authentication authentication, Object targetDomainObject) {
        boolean hasPermission = false;

        if (isId(targetDomainObject)) {
            Advertisement advertisement = advertisementRepository.findOne((Long) targetDomainObject);
            if (advertisement.getMember().getId().equals(((Member) authentication).getId())) {
                hasPermission = true;
            }
        }
        return hasPermission;
    }

    private boolean isId(Object targetDomainObject) {
        return targetDomainObject instanceof Long && (Long) targetDomainObject > 0;
    }

}

Here is the exception I get on application context startup:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.setPermissionEvaluator(java.util.List); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'permissionEvaluator' defined in class path resource [com/bignibou/configuration/security/MethodSecurityConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public com.bignibou.configuration.security.permission.ApplicationPermissionEvaluator com.bignibou.configuration.security.MethodSecurityConfiguration.permissionEvaluator()] threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'advertisementByIdOwnerPermission': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bignibou.repository.advertisement.AdvertisementRepository com.bignibou.configuration.security.permission.AdvertisementByIdOwnerPermission.advertisementRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bignibou.repository.advertisement.AdvertisementRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
    ... 45 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'permissionEvaluator' defined in class path resource [com/bignibou/configuration/security/MethodSecurityConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public com.bignibou.configuration.security.permission.ApplicationPermissionEvaluator com.bignibou.configuration.security.MethodSecurityConfiguration.permissionEvaluator()] threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'advertisementByIdOwnerPermission': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bignibou.repository.advertisement.AdvertisementRepository com.bignibou.configuration.security.permission.AdvertisementByIdOwnerPermission.advertisementRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bignibou.repository.advertisement.AdvertisementRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:597)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1017)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:912)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:553)
    ... 47 more
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public com.bignibou.configuration.security.permission.ApplicationPermissionEvaluator com.bignibou.configuration.security.MethodSecurityConfiguration.permissionEvaluator()] threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'advertisementByIdOwnerPermission': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bignibou.repository.advertisement.AdvertisementRepository com.bignibou.configuration.security.permission.AdvertisementByIdOwnerPermission.advertisementRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bignibou.repository.advertisement.AdvertisementRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:188)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:586)
    ... 59 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'advertisementByIdOwnerPermission': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bignibou.repository.advertisement.AdvertisementRepository com.bignibou.configuration.security.permission.AdvertisementByIdOwnerPermission.advertisementRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bignibou.repository.advertisement.AdvertisementRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:324)
    at com.bignibou.configuration.security.MethodSecurityConfiguration$$EnhancerBySpringCGLIB$$6fb58a27.advertisementByIdOwnerPermission(<generated>)
    at com.bignibou.configuration.security.MethodSecurityConfiguration.permissionMap(MethodSecurityConfiguration.java:47)
    at com.bignibou.configuration.security.MethodSecurityConfiguration.permissionEvaluator(MethodSecurityConfiguration.java:41)
    at com.bignibou.configuration.security.MethodSecurityConfiguration$$EnhancerBySpringCGLIB$$6fb58a27.CGLIB$permissionEvaluator$2(<generated>)
    at com.bignibou.configuration.security.MethodSecurityConfiguration$$EnhancerBySpringCGLIB$$6fb58a27$$FastClassBySpringCGLIB$$71f98c77.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312)
    at com.bignibou.configuration.security.MethodSecurityConfiguration$$EnhancerBySpringCGLIB$$6fb58a27.permissionEvaluator(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:166)
    ... 60 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.bignibou.repository.advertisement.AdvertisementRepository com.bignibou.configuration.security.permission.AdvertisementByIdOwnerPermission.advertisementRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bignibou.repository.advertisement.AdvertisementRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
    ... 81 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.bignibou.repository.advertisement.AdvertisementRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1103)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:963)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:480)
    ... 83 more

Can anyone please help me configure my app so that the AdvertisementByIdOwnerPermission gets its dependency properly injected?

edit 1: AdvertisementRepository is a Spring data Jpa repository declared as follows:

RooJpaRepository:

@RooJpaRepository(domainType = Advertisement.class)
public interface AdvertisementRepository {

}

Together with its ITD:

privileged aspect AdvertisementRepository_Roo_Jpa_Repository {

    declare parents: AdvertisementRepository extends JpaRepository<Advertisement, Long>;

    declare parents: AdvertisementRepository extends JpaSpecificationExecutor<Advertisement>;

    declare @type: AdvertisementRepository: @Repository;

}

edit 2: JPA configuration:

@Configuration
@EnableJpaRepositories(basePackages = "com.bignibou.repository")
public class JpaConfiguration {

}

public abstract class DataConfiguration {

    @Value("${database.driverClassName}")
    private String driverClassName;

    @Value("${database.url}")
    private String url;

    @Value("${database.username}")
    private String username;

    @Value("${database.password}")
    private String password;

    @Value("${database.validationQuery}")
    private String validationQuery;

    @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setTestOnBorrow(Boolean.TRUE);
        dataSource.setTestOnReturn(Boolean.TRUE);
        dataSource.setTestWhileIdle(Boolean.TRUE);
        dataSource.setTimeBetweenEvictionRunsMillis(1800000);
        dataSource.setNumTestsPerEvictionRun(3);
        dataSource.setMinEvictableIdleTimeMillis(1800000);
        dataSource.setValidationQuery(validationQuery);
        dataSource.setMaxActive(5);
        dataSource.setLogAbandoned(Boolean.TRUE);
        dataSource.setRemoveAbandoned(Boolean.TRUE);
        dataSource.setRemoveAbandonedTimeout(10);
        return dataSource;
    }

    @Bean
    public JpaTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return transactionManager;
    }

    @Bean
    public EntityManager entityManager() {
        return entityManagerFactory().getObject().createEntityManager();
    }

    @Bean
    public HibernateJpaVendorAdapter jpaVendorAdapter() {
        return new HibernateJpaVendorAdapter();
    }

    @Bean
    public HibernatePersistence persistenceProvider() {
        return new HibernatePersistence();
    }

    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setPackagesToScan("com.bignibou.domain");
        entityManagerFactoryBean.setDataSource(dataSource());
        entityManagerFactoryBean.setPersistenceProvider(persistenceProvider());
        entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
        entityManagerFactoryBean.setJpaPropertyMap(propertiesMap());
        entityManagerFactoryBean.afterPropertiesSet();
        return entityManagerFactoryBean;
    }

    public abstract Map<String, String> propertiesMap();

    @Bean
    public HibernateExceptionTranslator hibernateExceptionTranslator() {
        return new HibernateExceptionTranslator();
    }

}

@Profile({ Profiles.DEFAULT, Profiles.CLOUD, Profiles.DEV })
@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
class DefaultDataConfiguration extends DataConfiguration {

    @Override
    public Map<String, String> propertiesMap() {
        Map<String, String> propertiesMap = new HashMap<>();
        propertiesMap.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
        propertiesMap.put("hibernate.hbm2ddl.auto", "update");
        propertiesMap.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
        propertiesMap.put("hibernate.connection.charSet", "UTF-8");
        propertiesMap.put("hibernate.show_sql", "true");
        propertiesMap.put("hibernate.format_sql", "true");
        propertiesMap.put("hibernate.use_sql_comments", "true");
        return propertiesMap;
    }

}

edit 3: I changed my MethodSecurityConfiguration as follows:

@Autowired//FAILS!!
private AdvertisementRepository advertisementRepository;

@Bean
public AdvertisementByIdOwnerPermission advertisementByIdOwnerPermission() {
    return new AdvertisementByIdOwnerPermission(advertisementRepository);
}

Now the AdvertisementByIdOwnerPermission takes its dependency through the constructor. The only issue to be resolved is that the AdvertisementRepository is not constructed yet by the time the MethodSecurityConfiguration class attempts to autowire it. Can someone please help me sort out this issue?

edit 4: I tried placing @Order(2) to the MethodSecurityConfiguration class and @Order(1) to the JpaConfiguration class. I still have the same issue...

edit 5: @Configuration class using the @ComponentScan annotation:

@Configuration
@EnableSpringConfigured
@ComponentScan(basePackages = { "com.bignibou" }, excludeFilters = { @Filter(type = FilterType.CUSTOM, value = RooRegexFilter.class),
        @Filter(type = FilterType.ANNOTATION, value = Controller.class) })
public class BaseConfiguration {
...
Was it helpful?

Solution

This is very likely to be caused by using an older version of Spring Data JPA. When using 1.4.2 you should pull in Spring Data Commons 1.7.2 and thus benefit from a few improvements we made in the area of type prediction.

Bottom line is: this should work in the latest version.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top