Skip to content

@PropertySource fails to autowire required class (Spring Boot 2.3.8.RELEASE)

I have the following class I plan on using to autowire an instance of a Spring class named AADAppRoleStatelessAuthenticationFilter:

import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@PropertySource("classpath:default-integration.yml")
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class AADFilterManager {​​​​​​​​
    @Autowired
    private AADAppRoleStatelessAuthenticationFilter aadAppRoleStatelessAuthenticationFilter;
}​​​​​​​​​​​​​​​

Although the default-integration.yml file is well placed under /resources (no ‘FileNotFoundException’ is thrown), it seems like during the application start-up, Spring, for whatever reason, is not able to read it’s content (or disregards it while trying to initialize aadAppRoleStatelessAuthenticationFilter).

enter image description here

It throws errors related to missing properties, which do exist (if I move the properties as they are into the application.yml file – the autowiring completes successfully).

Field aadAppRoleStatelessAuthenticationFilter in com.security.AADFilterManager required a bean of type 'com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter' that could not be found.
 The injection point has the following annotations:
    - @org.springframework.beans.factory.annotation.Autowired(required=true)
The following candidates were found but could not be injected:
    - Bean method 'azureADStatelessAuthFilter' in 'AADAuthenticationFilterAutoConfiguration' not loaded because @ConditionalOnProperty (azure.activedirectory.client-id) did not find property 'client-id' 

My default-integration file:

spring:
  security:
    oauth2:
      client:
        registration:
          azure:
            client-id: B
            client-secret: C
azure:
  activedirectory:
    tenant-id: A
    client-id: B
    client-secret: C
    session-stateless: true
    app-id-uri: D
    user-group:
      allowed-groups: Users

Answer

Yes I ran into exactly this issue, this is how I resolved it.

First, you need to define a YamlPropertySourceFactory class:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import org.springframework.lang.Nullable;

public class YamlPropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
        Properties propertiesFromYaml = loadYamlIntoProperties(resource);
        String sourceName = name != null ? name : resource.getResource().getFilename();
        return new PropertiesPropertySource(sourceName, propertiesFromYaml);
    }

    private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
        try {
            YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
            factory.setResources(resource.getResource());
            factory.afterPropertiesSet();
            return factory.getObject();
        } catch (IllegalStateException e) {
            // for ignoreResourceNotFound
            Throwable cause = e.getCause();
            if (cause instanceof FileNotFoundException)
                throw (FileNotFoundException) e.getCause();
            throw e;
        }
    }
}

Now you can use this on the beans where you want to use a yaml file as your PropertySource. For example:

@Configuration
@PropertySource(factory = YamlPropertySourceFactory.class, value = "classpath:datasource.yml")
@ConfigurationProperties(prefix = "datasource")
public class DatasourceConfiguration {
   ...
}