#Configuration
public class A {
#Autowired
private Integer connectTimeOut;
#Bean
public Integer connectTimeOut(){
return getTimeOutConfigured(HTTP_CONNECT_TIME_OUT, -1);
}
}
Springboot can't start successfully with below issue:
The dependencies of some of the beans in the application context form a cycle:
┌──->──┐
| A (field private java.lang.Integer XXX.XXX.XXX.XXX.XXXX.XXX.A.connectTimeOut)
└──<-──┘
It can work fine from springboot 2.5.6. But it can't start for 2.6.6.
Any one who has the experience? or any advice?
It sounds like you're using this connectTimeOut value somewhere, probably in some other bean:
public class MyBean {
private final Integer connectTimeOut;
public MyBean(Integer connectTimeOut) {
this.connectTimeOut = connectTimeOut;
}
}
And your configuration class looks something like this:
#Configuration
public class A {
#Autowired
private Integer connectTimeOut;
#Bean
public Integer connectTimeOut(){
return getTimeOutConfigured(HTTP_CONNECT_TIME_OUT, -1);
}
#Bean
public MyBean myBean() {
return new MyBean(connectTimeOut);
}
}
If so, consider using the built-in mechanism of spring boot to work with configuration properties:
#ConfigurationProperties
public class MyConfigProperties{
private Integer connectTimeOut = -1; // default
// setter, getter, no-args-constructor
}
In application.yml or properties file define:
application.yml
connectTimeOut: 30
Now you can rewrite the configuration and get rid of the autowiring altogether:
#Configuration
#EnableConfigurationProperties(MyConfigProperties.class)
public class A {
#Bean
public MyBean myBean(MyConfigProperties config) { // Injecting the configuration!!!
return new MyBean(config.getConnectTimeOut());
}
}
A cycle would explain why it worked in 2.5 but not in 2.6. The Spring Boot 2.6 Release Notes describe what's going on and how to resolve the issue:
Circular references between beans are now prohibited by default. If your application fails to start due to a BeanCurrentlyInCreationException you are strongly encouraged to update your configuration to break the dependency cycle. If you are unable to do so, circular references can be allowed again by setting spring.main.allow-circular-references to true, or using the new setter methods on SpringApplication and SpringApplicationBuilder This will restore 2.5’s behaviour and automatically attempt to break the dependency cycle.
Related
We are upgrading the spring boot version from 1.3.0.RELEASE to 2.3.12.RELEASE.
As per the old version, yml files were read using the following code snippet
#Configuration
#ConfigurationProperties(locations = "classpath:/config/myconf-source.yml")
public class MyConfigProperties {
private String configSource;
public String getConfigSource() {
return configSource;
}
public void setConfigSource(String configSource) {
this.configSource = configSource;
}
}
Config files in src/main/resources/config/
myconf-source.yml
news-source.yml
conf-mapping.yml
Content in myconf-source.yml
configSource: "TEST"
Corresponding Test Class
#ActiveProfiles("test")
#RunWith(SpringJUnit4ClassRunner.class)
#EnableAutoConfiguration
#SpringApplicationConfiguration(classes = SampleApplication.class)
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
#ConfigurationProperties(locations = "classpath:**/config/**")
public class MyConfigPropertiesTest {
#Autowired
private MyConfigProperties myConfigProperties;
#Test
public void testMyConfigProperties() {
String config = myConfigProperties.getConfigSource();
Assert.assertEquals(config, "TEST");
}
}
After changing to the new version, it throws an error Cannot resolve method 'locations'.
If I remove locations attribute how spring will know the class MyConfigProperties has to read myconf-source.yml
Also while running the test class, NullPointerException is thrown as myConfigProperties.getConfigSource(); becomes null.
I have tried various solutions posted but no luck,
Can anyone suggest how to make it work?
Thanks
#Configuration should be used if in that class you define beans with #Bean.
If not then remove it from there.
Also #Configuration does not make this class a bean to be autowired in the test that you require it to be.
If you want MyConfigProperties to be available for autowiring then you also need
#EnableConfigurationProperties(MyConfigProperties.class). This will make sure that this class is available as a spring bean in the application context.
So it would be
#PropertySource("classpath:/config/myconf-source.yml")
#ConfigurationProperties()
#EnableConfigurationProperties(MyConfigProperties.class)
public class MyConfigProperties {
private String configSource;
public String getConfigSource() {
return configSource;
}
public void setConfigSource(String configSource) {
this.configSource = configSource;
}
}
You can use #PropertySource annotation to read the yml file , you can read the below article :
https://www.baeldung.com/properties-with-spring
I have a config as follows:
#Configuration
public class REConfiguration {
private final DBRuleLoader dbRuleLoader;
public REConfiguration(
DBRuleLoader dbRuleLoader) {
this.dbRuleLoader = dbRuleLoader;
}
#RefreshScope
#Bean
public REMgr ruleEngine() {
return REmgrFactory.getREMgr(this.dbRuleLoader);
}
}
Actually, I have added #RefreshScope here, as I want on any update of underlying DBRuleLoader bean, REMgr gets refreshed. I am not sure if it actually works.
I have a spring-boot app that now needs to support multiple Object stores and selectively use the desired store based on the environment. Essentially what i have done is create an interface that each store repository then implements.
I have simplified the code for the examples.
I have created 2 beans for each store type based on the spring profile determining the env:
#Profile("env1")
#Bean
public store1Sdk buildClientStore1() {
return new store1sdk();
}
#Profile("env2")
#Bean
public store2Sdk buildClientStore2() {
return new store2sdk();
}
in the service layer I have autowired the interface and then in the repositories i have used #Profile to specify which instance of the interface to use.
public interface ObjectStore {
String download(String fileObjectKey);
...
}
#Service
public class ObjectHandlerService {
#Autowired
private ObjectStore objectStore;
public String getObject(String fileObjectKey) {
return objectStore.download(fileObjectKey);
}
...
}
#Repository
#Profile("env1")
public class Store1Repository implements ObjectStore {
#Autowired
private Store1Sdk store1client;
public String download(String fileObjectKey) {
return store1client.getObject(storeName, fileObjectKey);
}
}
When I start the application with the configured "env" this actually runs as expected. however when running the test I get the "no qualifying bean of type ObjectStore. expected at least 1 bean which qualifies as autowire candidate."
#ExtendWith({ SpringExtension.class })
#SpringBootTest(classes = Application.class)
#ActiveProfiles("env1,test")
public class ComposerServiceTest {
#Autowired
private ObjectHandlerService service;
#Test
void download_success() {
String response = service.getObject("testKey");
...
}
}
As noted in the #ActiveProfile on the test class there are some other environments e.g. dev,test,prod. I have tried playing around with Component scan, having impl and interface in the same package, etc, to no success. I feel like I am missing something obvious with the test setup. But could be something with my overall application config? my main aim with the solution is to avoid having something a long the lines of
if (store1Sdk != null) {
store1Sdk.download(fileObjectKey);
}
if (store2Sdk != null) {
store2Sdk.download(fileObjectKey);
}
Try #ActiveProfiles({"env1", "test"}).
Activate multiple profiles using #ActiveProfiles and specify profiles as an array.
this probrom because Store1Repository use #Profile("env1"), when you use #test,this class not invoke. try delete #Profile("env1") of Store1Repository.
if you use #test, both of store1Sdk/store2Sdk don't instanse, try add default instanse.eg:
#Bean
public store2Sdk buildClientStoreDefault() {
return new store2sdk();
}
I am learning concepts of Spring & I came across #Bean & #Component annotations. I want to know what will happen in below scenario:
#Configuration
class ConfigClass {
#Bean
public ComponentClass ComponentClass() {
return new ComponentClass(someDependency1, someDependency2, someDependency3);
}
}
#Component
class ComponentClass{
private SomeDependency1 sd1;
private SomeDependency2 sd2;
private SomeDependency3 sd3;
public ComponentClass(SomeDependency1 sd1, SomeDependency2 sd2, SomeDependency3 sd3) {
/* initialize here */
}
}
I have declared ComponentClass as #Component which means it is a spring bean now. But I have also defined a #Bean for it in config class separately.
Which of these beans will be actually used as by default Spring is singleton?
What happens when I remove #Component?
Spring will notice a mistake and throw NoUniqueBeanDefinitionException during application startup.
If you remove #Component annotation it will work as expected, #Bean will be used for initialization.
I'm using Spring Boot with a thrift server, and I have two #Configuration class with two bean generation method, and the code is as following:
#Configuration
public class EagleBeanCreator {
#Bean(destroyMethod = "destroy")
public EagleRestClient build() {
EagleRestClient client = new EagleRestClient();
// some set values code
return client;
}
}
And another one:
#Configuration
public class EagleServiceBuilder {
#Autowired
private EagleRestClient eagleProxy;
#Bean
public EagleService eagleService() {
EagleService service = new EagleService();
System.out.println(eagleProxy);
service.setEagleProxy(eagleProxy);
return service;
}
}
But when I run spring-boot:run, it print out null for "System.out.println(eagleProxy);"
Why?
=========================UPDATE=============================
I know setter injection or constructor injection works.
You may want to try this out.
#Configuration
public class EagleServiceBuilder {
#Bean
public EagleService eagleService(EagleRestClient eagleProxy) {
EagleService service = new EagleService();
System.out.println(eagleProxy);
service.setEagleProxy(eagleProxy);
return service;
}
}
My guess is that the way you currently implement doesn't indicate a dependency between the EagleService and EagleRestClient. So your current implementation leads to random initialization order between the two beans. The modified version tells Spring "Hey, my EagleService depends on EagleRestClient. Please initialize EagleRestClient before EagleService.
Because the order to load EagleBeanCreator and EagleServiceBuilder is not definite. You can use #Order or #ConditionalOnClass to make sure EagleBeanCreator initialize first.
Because the #Configuration bean are initialized in the same phase of bean lifecycle. I don't remember them clearly but something like:
Configurations -> Components -> Services
With the beans in the same phase, if they depend on each other, you should declare the load order by some #Conditional or #Order
Add #DependsOn("eagleRestClient") annotation on the definition of EagleService.
#DependsOn("eagleRestClient")
#Bean
public EagleService eagleService() {
EagleService service = new
EagleService();
System.out.println(eagleProxy);
service.setEagleProxy(eagleProxy);
return service;
}
Spring will then first create rest client then the eagle service.
First, you need to get the spring container through ApplicationContextAware try
ApplicationContext.getBean(EagleRestClient.class)