Bean injection failing in Library - java

I created one library, in which i created some beans. Below is the file where i am creating some beans:
#Configuration
public class StorageBindings {
#Value("${storageAccountName}")
private String storageAccountName;
#Value("${storageAccountKey}")
private String storageAccountKey;
#Bean(name = "cloudBlobClient")
public CloudBlobClient getCloudBlobClientUsingCredentials() throws URISyntaxException {
return new CloudBlobClient();
}
#Bean(name = "storageCredentialsToken")
public StorageCredentialsToken getStorageCredentialsToken() throws IOException {
return new StorageCredentialsToken();
}
#Bean(name = "msiTokenGenerator")
public MSITokenGenerator getMSITokenGenerator() {
return new MSITokenGenerator();
}
}
Then i created the class, which i use as entry point to do further operations
public class StorageClient {
#Autowired
private CloudBlobClient cloudBlobClient;
#Autowired
private MSITokenGenerator msiTokenGenerator;
#Value("${storageAccountName}")
private String storageAccountName;
#Value("${storageAccountKey}")
private String storageAccountKey;
}
I created the jar with above files and include it in our main project, where i created the bean of StorageClient as below:
#Bean(name = {"storageClient"})
public StorageClient getStorageClient() {
LOG.debug("I am inside storage class");
StorageClient ac = null;
try {
ac = new StorageClient();
return ac;
}
But after execution i found that no injection in StorageClient instance ac for below variables and not even environment property getting reflected and all of them are null:
//beans NOT Injecting
ac.cloudBlobClient=null;
ac.msiTokenGenerator=null;
//env variables
ac.storageAccountName=null;
ac.storageAccountKey=null;
Am i missing something, as i am getting null. Sequence of instantiation of beans are ok. I checked. So first beans of StorageBindings are getting created.

When you do this:
ac = new StorageClient();
you lose the context of spring, because you are creating a new instance out of that context. The beans inside CloudBlobClient,MSITokenGenerator and variables storageAccountName,storageAccountKey, they don't get injected.
You can annotate StorageClient with #Component.
So since you pack it as jar, in your main project you have to make sure that the #ComponentScan includes the path where StorageClient is.
Then you can do:
#Autowired
private StorageClient storageClient;
in your main project.

If you are creating object inside #Bean annotated method autowiring doesn't inject beans there - you simply are creating it by yourself.
So you have to #Autowire it ie on fields in your Configuration class and set with setter/constructor.
Ie:
#Autowired
private CloudBlobClient cloudBlobClient;
#Autowired
private MSITokenGenerator msiTokenGenerator;
#Bean(name = {"storageClient"})
public StorageClient getStorageClient() {
LOG.debug("I am inside storage class");
StorageClient ac = null;
try {
ac = new StorageClient();
ac.setCloudBlobClient(cloudBlobClient);
ac.setMsiTokenGenerator(msiTokenGenerator);
return ac;
}
}

Related

Use #ConfigurationProperties over non-managed #Bean

I would like to benefit from #ConfigurationProperties fantastic facilities without needing to expose the bean into my context. It is not a problem of #Primaries and the like, I simply cannot expose another Datasource into the context. How can I achieve the following?
#ConfigurationProperties("com.non.exposed.datasource.hikari")
public DataSource privateHikariDatasource() {
if (Objects.isNull(this.nonExposedDatasource)) {
this.nonExposedDatasource = this.nonExposedDatasourceProperties.initializeDataSourceBuilder().build();
}
return this.nonExposedDatasource;
}
Thanks to the answer by #LppEdd, the final perfect solution is:
#Autowired
private Environment environment;
public DataSource privateHikariDatasource() {
if (Objects.isNull(this.nonExposedDatasource)) {
this.nonExposedDatasource = bindHikariProperties(this.nonExposedDatasourceProperties.initializeDataSourceBuilder().build());
}
return this.nonExposedDatasource;
}
//This does exactly the same as #ConfigurationProperties("com.non.exposed.hikari") but without requiring the exposure of the Datasource in the ctx as #Bean
private <T extends DataSource> T bindHikariProperties(final T instance) {
return Binder.get(this.environment).bind("com.non.exposed.datasource.hikari", Bindable.ofInstance(instance)).get();
}
Then you can call your bean internally with this.privateHikariDatasource() to be used by your other beans.
Great thanks to #LppEdd!
Being that this DataSource is private to a class, and that containing class can be/is inside the Spring context, you can have a #ConfigurationProperties class
#ConfigurationProperties("com.foo.bar.datasource.hikari")
public class HikariConfiguration { ... }
Which, by registering it via #EnableConfigurationProperties, is available for autowiring
#EnableConfigurationProperties(HikariConfiguration.class)
#SpringBootApplication
public class Application { ... }
And thus can be autowired in the containing class
#Component
class MyClass {
private final HikariConfiguration hikariConfiguration;
private DataSource springDatasource;
MyClass(final HikariConfiguration hikariConfiguration) {
this.hikariConfiguration = hikariConfiguration;
}
...
private DataSource privateSingletonDataSource() {
if (Objects.isNull(this.springDatasource)) {
this.springDatasource = buildDataSource(this.hikariConfiguration);
}
return this.springDatasource;
}
}
buildDataSource will manually construct the DataSource instance.
Remember that you need to take care of synchronization when building the DataSource.
Final response is that you cannot re-use DataSourceProperties. You can't even extend it to change the properties' prefix. Only a single instance of it can exist inside the context.
The best you can do is mimic what Spring does.
Having
com.non.exposed.datasource.hikari.url=testUrl
com.non.exposed.datasource.hikari.username=testUsername
com.non.exposed.datasource.hikari.password=testPassword
...
You can define a new #ConfigurationProperties class
#ConfigurationProperties("com.non.exposed.datasource")
public class NonExposedProperties {
private final Map<String, String> hikari = new HashMap<>(8);
public Map<String, String> getHikari() {
return hikari;
}
}
Then, autowire this properties class in your #Configuration/#Component class.
Follow in-code comments.
#Configuration
public class CustomConfiguration {
private final NonExposedProperties nonExposedProperties;
private DataSource dataSource;
CustomConfiguration(final NonExposedProperties nonExposedProperties) {
this.nonExposedProperties= nonExposedProperties;
}
public DataSource dataSource() {
if (Objects.isNull(dataSource)) {
// Create a standalone instance of DataSourceProperties
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
// Use the NonExposedProperties "hikari" Map as properties' source. It will be
// {
// url -> testUrl
// username -> testUsername
// password -> testPassword
// ... other properties
// }
final ConfigurationPropertySource source = new MapConfigurationPropertySource(nonExposedProperties.getHikari());
// Bind those properties to the DataSourceProperties instance
final BindResult<DataSourceProperties> binded =
new Binder(source).bind(
ConfigurationPropertyName.EMPTY,
Bindable.ofInstance(dataSourceProperties)
);
// Retrieve the binded instance (it's not a new one, it's the same as before)
dataSource = binded.get().initializeDataSourceBuilder().build();
}
// Return the constructed HikariDataSource
return dataSource;
}
}

JavaConfig Spring make beans available to all application

I have a jar with a main method. I created a java config with the #Configuration annotation.
#Configuration
#ComponentScan(basePackages = { "com.test.commons" })
public class ProxyConfig {
}
In this com.test.commons I have put a service
package com.test.commons;
#Service
public class RestService {
//do rest calls
public String restGetCall(URL url){
...
}
}
I am NOT asking for this
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ProxyConfig.class);
context.getBean("myBean");
The main
#SpringBootApplication(scanBasePackages={"com.test.commons", "com.test.soapproxy" })
public class MainAppProxy
{
private final static Logger logger = LoggerFactory.getLogger(MainAppProxy.class);
public static void main( String[] args )
{
SpringApplication.run(MainAppProxy.class, args);
// Choose environment from the arguments
String env = args[0];
// publish an endpoint here
Configuration config = null;
config = configs.properties
(new File("config_"+env+".properties"));
Endpoint.publish(endpointAddress, new SomeProxyImpl(config));
The class in which I am trying to inject the bean (is the #Component needed here really?)
#Component
public class SomeProxyImpl implements SomeServiceSoap {
#Autowired RestService restService;
I would like to be able to inject this RestService bean through #Autowired in all my application, not only in SomeProxyImpl(which is not working anyway). How can I do that?
Spring don't autowire field created by a new, unless you ask for it, like this : ApplicationContextHolder.getContext().getAutowireCapableBeanFactory().autowireBean(object);
If your SomeProxyImpl class is in the "com.test.soapproxy" package, and your class is annotated with #Component, then Spring must have created an instance with the bean autowired.
You should then get this bean from your context and use it instead of creating a new one.

Injecting a mock in Spring application context creates a copy

I have a test class that looks like this :
#SpringApplicationConfiguration(classes = RecipesApplicationTest.class) // This test class will configure its own context
#ComponentScan("com.mysmartfridge.domain.recipes") // Scan recipes domain package so that domain services are available
#Import(RecipesApplication.class) // Load the bean that we want to test.
public class RecipesApplicationTest {
#ClassRule
public static final SpringClassRule SCR = new SpringClassRule();
#Rule
public final SpringMethodRule SMR = new SpringMethodRule();
#Autowired
private RecipesRepository recipesRepository;
private final RecipesRepository recipesRepositoryMock = Mockito.mock(RecipesRepository.class); // final mocks because they are given to spring context.
// Get the service to test from the context
#Autowired
RecipesApplication recipesApplication;
#Test
public void addingARecipeShouldMakeItAvailableInRandomRecipes() {
//given
RecipeDto dto = new RecipeDto();
dto.title="test";
dto.ingredients = new ArrayList<>();
dto.steps = new ArrayList<>();
final List<Recipe> recipesInMock = new ArrayList<>();
Mockito.when(recipesRepository.save(Mockito.any(Recipe.class))).thenAnswer(new Answer<Recipe>() {
#Override
public Recipe answer(InvocationOnMock aInvocation) throws Throwable {
Recipe arg = aInvocation.getArgumentAt(0, Recipe.class);
recipesInMock.add(arg);
return arg;
}
});
Mockito.when(recipesRepository.findAll()).thenAnswer(new Answer<List<Recipe>>() {
#Override
public List<Recipe> answer(InvocationOnMock aInvocation) throws Throwable {
return recipesInMock;
}
});
//when
dto = recipesApplication.createRecipe(dto);
RecipeDto randomDto = recipesApplication.findRandomRecipe();
//then
Assertions.assertThat(randomDto).isEqualTo(dto);
}
// Inject the recipeRepository mock in the context
#Bean
RecipesRepository recipesRepository() {
return recipesRepositoryMock;
}
}
My problem is that the two fields recipesRepositoryMock and recipesRepository are not the same object. Thus, if i try to replace recipesRepository with recipesRepositoryMock when setting up my answers (i.e. if I do when(recipesRepositoryMock.save()).thenAnswer(...)), it doesn't work, then custom answer is never called.
I would like to be able to get rid of the #Autowired recipesRepository which is kind of a duplicate with recipesRepositoryMock...
Is it because of a proxy added by spring around my bean ?
You could better remove
private final RecipesRepository recipesRepositoryMock = Mockito.mock(RecipesRepository.class);
If you want to mock a Spring bean then you have to create it for Spring as use #Autowired.
So you test could look like:
#SpringApplicationConfiguration(classes = RecipesApplicationTest.class) // This test class will configure its own context
#ComponentScan("com.mysmartfridge.domain.recipes") // Scan recipes domain package so that domain services are available
#Import(RecipesApplication.class) // Load the bean that we want to test.
public class RecipesApplicationTest {
#ClassRule
public static final SpringClassRule SCR = new SpringClassRule();
#Rule
public final SpringMethodRule SMR = new SpringMethodRule();
#Autowired
private RecipesRepository recipesRepository;
// Get the service to test from the context
#Autowired
RecipesApplication recipesApplication;
#Test
public void addingARecipeShouldMakeItAvailableInRandomRecipes() {
// your test
}
// Inject the recipeRepository mock in the context
#Bean
RecipesRepository recipesRepository() {
return Mockito.mock(RecipesRepository.class);
}
}
It sounds like you are trying to rely on auto-wiring so that Spring correctly injects the RecipesRepository instance as a dependency into RecipesApplication. This question seems to be similar to yours. That being said, I'm hesitant to declare a duplicate because you may not have make use of something like [ReflectionTestUtils][2]. Rather, try just deleting the recipesRepository field in RecipesApplicationTest, keeping recipesRepositoryMock field. But, before the dto = recipesApplication.createRecipe(dto) line, try to see if you can manually inject the recipesRepositoryMock into recipesApplication via a call like recipesApplication.setRecipesRepository(recipesRepositoryMock) or something similar.

How to set property of one bean to another bean if it is in another configuration without autowire?

With the help of #Autowire, it is possible to set properties from beans in another configuration.
Is it possible to do equivalent assignment in Java-based config code?
The sample is below.
It outputs:
instance1.instance2 = com.inthemoon.snippets.springframework.InterconfigSettingTry$Bean2#574b560f
instance1.instance3 = null
which proves, that property is autowired between configs.
The sample contains comments, explaining, where I would like to assign property instead of #Autowired:
public class InterconfigSettingTry {
// first bean class with two properties, one of which is set with autowire, while another does not
public static class Bean1 {
#Autowired
public Bean2 instance2;
// #Autowired // not using autowire feature
public Bean2 instance3;
}
// second bean class, which will be instantiated in two beans
public static class Bean2 {
}
// java based configuration #1, which contains a bean, wishing to be aware of two beans in another config
#Configuration
public static class _Config1 {
#Bean
public Bean1 instance1() {
Bean1 ans = new Bean1();
// how to set ans.instance3 here without autowire?
//ans.instance3 = ???
return ans;
}
}
// java based configuration #2
#Configuration
public static class _Config2 {
#Bean
public Bean2 instance2() {
return new Bean2();
}
#Bean
public Bean2 instance3() {
return new Bean2();
}
}
// main method
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(_Config1.class, _Config2.class);
Bean1 instance1 = (Bean1) context.getBean("instance1");
System.out.println("instance1.instance2 = " + instance1.instance2);
System.out.println("instance1.instance3 = " + instance1.instance3);
}
}

Overriding beans in Java-based spring configuration hierarchy

Let's assume we have an application that can be customized for some customers. The application is using Java-based spring configuration (a.k.a. Java config) for dependency injection. The application consists of modules and their submodules. Each module and submodule has its own #Configuration class which is imported by parent configuration using #Import. This creates the following hierarchy:
MainConfig
----------+---------------- ....
| |
ModuleAConfig ModuleBConfig
|--------------------|
| |
SubModuleA1Config SubModuleA2Config
For example ModuleAConfig looks like this:
#Configuration
#Import({SubModuleA1Config.class, SubModuleA2Config.class})
public class ModuleAConfig {
// some module level beans
}
Let's say that SubModuleA1Config defines bean someBean of type SomeBean:
#Configuration
public class SubModuleA1Config {
#Bean
public SomeBean someBean() { return new SomeBean(); }
}
Now I want to customize the application for Customer1 (C1) - I want to use C1SomeBean (extending SomeBean) instead of SomeBean as someBean.
How can I achieve this with minimum duplication?
One of my ideas was to prepare alternative hierarchy with C1Config inheriting from MainConfig, C1ModuleAConfig from ModuleAConfig and C1SubModuleA1Config from SubModuleA1Config. C1SubModuleA1Config would override someBean() method returning C1SomeBean. Unfortunately with Spring 4.0.6 I get something like:
Overriding bean definition for bean 'someBean': replacing [someBean defined in class C1SubmoduleA1Config] with [someBean defined in class SubModuleA1Config]
and indeed SomeBean class is returned from context instead of C1SomeBean. This is clearly not what I want.
Note that you cannot override #Import extending configuration classes.
If you want to select which imports to use at runtime, you could use a #ImportSelector instead.
However, #Configuration classes are not more that spring (scoped) managed factories so as you already have a factory method for someBean you don't need to go even further:
#Configuration
public class SubModuleA1Config {
#Autowired
private Environment env;
#Bean
public SomeBean someBean() {
String customerProperty = env.getProperty("customer");
if ("C1".equals(customerProperty))
return new C1SomeBean();
return new SomeBean();
}
}
Update
Using a ImportSelector:
class CustomerImportSelector implements ImportSelector, EnvironmentAware {
private static final String PACKAGE = "org.example.config";
private static final String CONFIG_CLASS = "SubModuleConfig";
private Environment env;
#Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String customer = env.getProperty("customer");
return new String[] { PACKAGE + "." + customer + "." + CONFIG_CLASS };
}
#Override
public void setEnvironment(Environment environment) {
this.env = environment;
}
}
#Configuration
#Import(CustomerImportSelector.class)
public class ModuleAConfig {
// some module level beans
}
However, as every customer has a a separate package, consider also using #ComponentScan. This will pick the configuration class present and don't need a extra configuration property.
#Configuration
#ComponentScan(basePackages="org.example.customer")
public class SubModuleA1Config {
#Autowired
private CustomerFactory customerFactory;
#Bean
public SomeBean someBean() {
return customerFactory.someBean();
}
}
public interface CustomerFactory {
SomeBean someBean();
}
#Component
public class C1CustomerFactory implements CustomerFactory {
#Override
public SomeBean someBean() {
return new C1SomeBean();
}
}

Categories