I'm trying to imitate the behavior of the Spring XML alias in #Configuration class.
I have an alias in XML that looks like that:
<alias name="${com.some.bean}" alias="myBean"/>
I have a #Configuration class that looks like that:
#Configuration
public class MyConfig {
#Lazy
#Bean(name = "bean1")
public MyBean bean1() { return new MyBean(); }
#Lazy
#Bean(name = "bean2")
public MyBean bean2() { return new MyBean(); }
}
I want to load dynamically either bean1 or bean2 an give the one that's loaded the alias myBean. according to the resolution of com.some.bean property. It's easy to do with XMLs, yet I can't find the #Configuation equivalent.
Note: I do not want to use profiles because that's not how my system currently works and changing all the property resolution to use profiles instead of property files is not an option at the moment.
An ugly workaround is to define a #Bean which receives the property value as a #Value and performs the resolution itself
#Bean("myBean")
public MyBean myBean(#Value("${com.some.bean}") String value) {
if (value.equals("bean1"))
return bean1();
else if (value.equals("bean2"))
return bean2();
else {
throw new RuntimeException("nope, something went wrong");
}
}
Related
this is a springBoot1.5.22 project.I have 3 java config Bean ,use #Configuration and #Bean annotation.
when i try to run project with debug mode。why myBean method of ConfigurationC execute ? ,the myBean method of ConfigurationA and ConfigurationB not execute。what is mechanism ?
packages of the classes
start class
#Configuration
public class ConfigurationA {
#Bean
public MyBean myBean(){
System.out.println("ConfigurationA myBean init");
return new MyBean();
}
}
#Configuration
public class ConfigurationB {
#Bean
public MyBean myBean(){
System.out.println("ConfigurationB myBean init");
return new MyBean();
}
}
#Configuration
public class ConfigurationC {
#Bean
public MyBean myBean(){
System.out.println("ConfigurationC myBean init");
return new MyBean();
}
}
Like #jackycflau said in the comment above, all your beans have the same name.
These three beans, all with the same name and type, are being loaded (but not yet initialized) sequentially into the application context (bean container). When a bean named "myBean" of type MyBean is returned from the application context, you get the one from ConfigurationC because it was the last one written into the container, which overwrote the previous two beans of the same name/type. It's apparently not being initialized until it's actually pulled from the container by client code, which is why it's the only one whose code actually runs.
please provide code snippets to analyse it more.
Bean id should be unique. I don't think you would be allowed to create beans with same beanid.
please try below code
#Configuration
public class ConfigurationA {
#Bean
public MyBean myBean(){
System.out.println("ConfigurationA myBean init");
return new MyBean();
}
}
#Configuration
public class ConfigurationB {
#Bean
public MyBean myBean1(){
System.out.println("ConfigurationB myBean init");
return new MyBean();
}
}
#Configuration
public class ConfigurationC {
#Bean
public MyBean myBean2(){
System.out.println("ConfigurationC myBean init");
return new MyBean();
}
}
You can try specifying names:
#Bean(name="bean1")
and then select the injected bean:
#Autowired
#Qualifier("bean1")
In Spring Boot 1.5.x the bean overriding is enabled by default.
This means, the bean definition tree is built first and then the last overriding bean is used (executed), all others are ignored (as they were overrided). In your case the last definition comes from ConfigurationC.
This mechanism prevents from ambitious bean definitions, where more than one definition is found, and Spring can't know which one to use - an error occurs (BeanDefinitionOverrideException).
Please note, this must be explicit enabled in Spring Boot 2.x.
In Spring Framework is it possible to eliminate the entire Spring.xml and use a configuration class with #Configuration and #Bean annotation for creating bean, and for all other purpose use a spring.xml?
Yes, you can have pure java configuration in Spring. You have to create a class and annotate it with #Configuration. We annotate methods with #Bean and instantiate the Spring bean and return it from that method.
#Configuration
public class SomeClass {
#Bean
public SomeBean someBean() {
return new SomeBean();
}
}
If you want to enable component scanning, then you can give #ComponentScan(basePackages="specify_your_package") under the #Configuration. Also the method name as someBean serves as bean id. Also if you have to inject a dependency, you can use constructor injection and do as following:
#Configuration
public class SomeClass {
#Bean
public SomeDependency someDependency() {
return new SomeDependency();
}
#Bean
public SomeBean someBean() {
return new SomeBean(someDependency());
}
}
Yes,most of (maybe all of)official guides uses absolutely no xml configuration file,just annotations.
I'm not sure if I understand it correctly, but from what I got, is that I can use #Value annotations to read values from my application.properties.
As I figured out this works only for Beans.
I defined such a bean like this
#Service
public class DBConfigBean {
#Value("${spring.datasource.username}")
private String userName;
#Bean
public String getName() {
return this.userName;
}
}
When the application starts I'm able to retrieve the username, however - how can I access this value at runtime?
Whenever I do
DBConfigBean conf = new DBConfigBean()
conf.getName();
* EDIT *
Due to the comments I'm able to use this config DBConfigBean - but my initial problem still remains, when I want to use it in another class
#Configurable
public SomeOtherClass {
#Autowired
private DBConfigBean dbConfig; // IS NULL
public void DoStuff() {
// read the config value from dbConfig
}
}
How can I read the DBConfig in a some helper class which I can define as a bean
Thanks
As Eirini already mentioned you must inject your beans.
The #Value annotation only works on Spring beans.
There is another way of accessing configuration with #ConfigurationProperties.
There you define a class that holds the configuration.
The main advantage is, that this is typesafe and the configuration is in one place.
Read more about this:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-vs-value
You shouldn't instantiate your service with the new operator. You should inject it, for example
#Autowired
private DBConfigBean dbConfig;
and then dbConfig.getName();
Also you don't need any #Bean decorator in your getName() method
You just need to tell spring where to search for your annotated beans. So in your configuration you could add the following:
#ComponentScan(basePackages = {"a.package.containing.the.service",
"another.package.containing.the.service"})
EDIT
The #Value, #Autowired etc annotations can only work with beans, that spring is aware of.
Declare your SomeOtherClass as a bean and add the package config in your #Configuration class
#Bean
private SomeOtherClass someOtherClass;
and then
#Configuration
#ComponentScan(basePackages = {"a.package.containing.the.service"
"some.other.class.package"})
public class AppConfiguration {
//By the way you can also define beans like:
#Bean
public AwesomeService service() {
return new AwesomeService();
}
}
Wrap your DBConfig with #Component annotation and inject it using #Autowired :
#Autowired
private DBConfig dbConfig;
Just add below annotation to your DBConfigBean class:
#PropertySource(value = {"classpath:application.properties"})
I am turning old xml/java configuration into pure java config. In xml I used injection of parameters into configuration file like this:
<bean class="com.project.SpringRestConfiguration">
<property name="parameters" ref="parameters" />
</bean>
#Configuration
public class SpringRestConfiguration {
private Parameters parameters;
public void setParameters(Parameters parameters) {
this.parameters = parameters;
}
// #Bean definitions
...
}
Is it possible to inject Parameters in javaconfig? (Without the need of using autowiring!)
#Configuration
#Import(SpringRestConfiguration.class)
EDIT:
With #Import I can't see any chance to inject Parameters into SpringRestConfiguration
Basically you would need to use #Autowired but you can still use a name and not type interpretation like this:
#Configuration
public class SpringRestConfiguration {
#Autowired
#Qualifier("parameters") // Somewhere in your context you should have a bean named 'parameters'. It doesn't matter if it was defined with XML, configuration class or with auto scanning. As long as such bean with the right type and name exists, you should be good.
private Parameters parameters;
// #Bean definitions
...
}
This solves the confusion problem you mentioned when using #Autowired - there's no question here which bean is injected, the bean that is named parameters.
You can even do a little test, leave the parameters bean defined in the XML as before, use #Autowired, see that it works. Only then migrate parameters to #Configuration class.
In my answer here you can find a complete explanation of how you should migrate XML to #Configuration step by step.
You can also skip the private member altogether and do something like this:
#Configuration
public class SpringRestConfiguration {
#Bean
public BeanThatNeedsParamters beanThatNeedsParamters (#Qualifier("parameters") Parameters parameters) {
return new BeanThatNeedsParamters(parameters)
}
}
If I have understood your question properly, this is what you are trying to do :
#Component
public class SomeConfiguration {
#Bean(name="parameters")
public Parameters getParameters(){
Parameters parameters = new Parameters();
// add your stuff
return parameters;
}
#Bean(name="springRestConfiguration")
public SpringRestConfiguration springRestConfiguration(){
SpringRestConfiguration springRestConfiguration = new SpringRestConfiguration();
springRestConfiguration.setParametere(getParameters());
return springRestConfiguration;
}
}
and use it like :
ApplicationContext appContext = new AnnotationConfigApplicationContext(SomeConfiguration.class);
SpringRestConfiguration springRestConfiguration = (SpringRestConfiguration) appContext.getBean("springRestConfiguration");
Is it possible to configure spring to instantiate a bean or not, depending on a boolean placeholder property? Or at least to exclude a package from annotation scanning based on such a property?
I think you should be using Spring profiles to configure different behaviours. However if you are using annotations you could create an #Configuration object and and a factory method to create a bean based on the property value
e.g.
#Configuration
class ExampleConfig {
private final String prop;
public ExampleConfig(#Value("${your.prop.name}" prop} {
this.prop = prop;
}
#Bean
public YourBeanClass create() {
if (prop.equals("someValue") {
return new YourBeanClass();
}
return new OtherClass(); // must be subclass/implementation of YBC
}
}
You can use ConditionalOnProperty:
#Bean
#ConditionalOnProperty(value = "property", havingValue = "value", matchIfMissing = true)
public MyBean myBean() ...
Also, check this answer.
This may not fit your needs, and I'm assuming that you have control over the class in question (i.e. not vendor code), but have you considered marking the the bean to be lazy loaded? At least, that way it won't get instantiated until it actually gets used, which may happen conditionally depending on the rest of your configuration.
You can also use #Conditional
Step 1 : Create a class that implements Condition
public class ProdDataSourceCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String dbname = context.getEnvironment().getProperty("database.name");
return dbname.equalsIgnoreCase("prod");
}}
Step 2 : Use the above class with #Conditional
#Configuration
public class EmployeeDataSourceConfig {
....
#Bean(name="dataSource")
#Conditional(ProdDataSourceCondition.class)
public DataSource getProdDataSource() {
return new ProductionDatabaseUtil();
}
}
http://javapapers.com/spring/spring-conditional-annotation/
We can use ConditionalOnProperty . Just define a property deployment.environemnt in application.properties file . And based on this property you can control the creation of objects.
#Bean(name = "prodDatasource")
#ConditionalOnProperty(prefix = "deployment" name = "environment"havingValue = "production")
public DataSource getProdDataSource() {
return new ProductionDatasource();
}
#Bean(name = "devDatasource")
#ConditionalOnProperty(prefix = "deployment" name = "environment"havingValue = "dev")
public DataSource getDevDataSource() {
return new devDatasource();
}