#Bean and optional #Autowire - spring

My #Configuration defines a couple of beans - A & B
#Configuration
public class MyConfiguration {
#Bean
public A supplyA() {
return new A(...);
}
#Bean
public B supplyB() {
return new B(...);
}
}
I was expecting that I should #Autowire A and B where they are needed, like so:
#Controller
public MyController {
#Autowire
public MyController(A a, B b) {
}
}
But it does work fine without the #Autowire on the constructor. What gives? (I'm on Spring 5 if that matters)

I think what you're trying is constructor injection, check https://docs.spring.io/spring/docs/4.3.26.RELEASE/spring-framework-reference/htmlsingle/#beans-constructor-injection
#Autowire, normally used for set injection.
Please understand the difference, before you make a change

Related

Switch bean by changing properties in Spring boot

I have one interface MyInterface, and 2 implementation beans: FirstImpl & SeconImpl. I want to switch between using these 2 implementations while program is running without restarting it, by only changing a property in application.properties file, e.g: interface.bean.default=FirstImpl change to interface.bean.default=SecondImpl.
Anyone knows how to do that with Spring boot?
You could try to use #ConditionalOnProperty:
#Configuration
public class MyInterfaceConfiguration {
#Bean
#ConditionalOnProperty(value = "my.interfacte.impl", havingValue="firstImpl")
public MyInterface firstImpl(){
return new FirstImpl();
}
#Bean
#ConditionalOnProperty(value = "my.interfacte.impl", havingValue="secondImpl")
public MyInterface secondImpl(){
return new SecondImpl();
}
}
and when you update your property in application.properties with actuator/refresh to:
my.interfacte.impl=firstImpl
you will have your FirstImpl instance. When you have:
my.interfacte.impl=secondImpl
you will have your SecondImpl.
#Hasan, your update only works if I customize it a little bit as below:
#Configuration
#RefreshScope
public class MyInterfaceConfiguration {
#Value("${my.interfacte.impl}")
String impl;
#Bean
#RefreshScope
public MyInterface getBean(){
if ("firstImpl".equals(impl)) {
return new FirstImpl();
} else if ("secondImpl".equals(impl)) {
return new SecondImpl();
}
return null;
}
}
I have to use 2 #RefreshScope at class level and bean creation method level!

Multiple Spring Configuration files (one per Profile)

I'm a Spring rookie and trying to benefit from the advantages of the easy 'profile' handling of Spring. I already worked through this tutorial: https://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile and now I'd like to adapt that concept to an easy example.
I've got two profiles: dev and prod. I imagine a #Configuration class for each profile where I can instantiate different beans (implementing a common interface respectively) depending on the set profile.
My currently used classes look like this:
StatusController.java
#RestController
#RequestMapping("/status")
public class StatusController {
private final EnvironmentAwareBean environmentBean;
#Autowired
public StatusController(EnvironmentAwareBean environmentBean) {
this.environmentBean = environmentBean;
}
#RequestMapping(method = RequestMethod.GET)
Status getStatus() {
Status status = new Status();
status.setExtra("environmentBean=" + environmentBean.getString());
return status;
}
}
EnvironmentAwareBean.java
public interface EnvironmentAwareBean {
String getString();
}
EnvironmentAwareBean.java
#Service
public class DevBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "development";
}
}
EnvironmentAwareBean.java
#Service
public class ProdBean implements EnvironmentAwareBean {
#Override
public String getString() {
return "production";
}
}
DevConfig.java
#Configuration
#Profile("dev")
public class DevConfig {
#Bean
public EnvironmentAwareBean getDevBean() {
return new DevBean();
}
}
ProdConfig.java
#Configuration
#Profile("prod")
public class ProdConfig {
#Bean
public EnvironmentAwareBean getProdBean() {
return new ProdBean();
}
}
Running the example throws this exception during startup (SPRING_PROFILES_DEFAULT is set to dev):
(...) UnsatisfiedDependencyException: (...) nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [EnvironmentAwareBean] is defined: expected single matching bean but found 3: prodBean,devBean,getDevBean
Is my approach far from a recommended configuration? In my opinion it would make more sense to annotate each Configuration with the #Profile annotation instead of doing it for each and every bean and possibly forgetting some variants when new classes are added later on.
Your implementations of EnvironmentAwareBean are all annotated with #Service.
This means they will all be picked up by component scanning and hence you get more than one matching bean. Do they need to be annotated with #Service?
Annotating each #Configuration with the #Profile annotation is fine. Another way as an educational exercise would be to not use #Profile and instead annotate the #Bean or Config classes with your own implementation of #Conditional.

Refer a spring bean from the inherited context in Java config

I am using the Java spring configuration using annotations.
If I have a configuration class A which is like
#Configuration
public class A{
#Bean(name="hello")
public hello(){
return new Hello();
}
#Bean(name="greeting")
public greeting(){
return new Greeting(hello());
}
So here the greeting bean gets hello as a dependency.
How would I do this if I were to declare the greeting bean in a different configuration class say B and them import the configuration from A
#Configuration
public class A{
#Bean(name="hello")
public hello(){
return new Hello();
}
}
#Configuration
#Import(A.class)
public class B{
#Bean(name="greeting")
public greeting(){
// what do i write here ?
// so that it is equivalent to
// return new Greeting(hello());
}
Below should be all you need.
Notice that I have injected your Hello bean as a method parameter. Spring DI shall take care of the rest.
#Configuration
#Import(A.class)
public class B{
#Bean(name="greeting")
public greeting(Hello hello){
return new Greeting(hello);
}
Answering my own question:
Should be able to make use of #Autowired annotation in B.
#Configuration
#Import(A.class)
public class B{
#Autowired
Hello hello;
#Bean(name="greeting")
public greeting(){
return new Greeting(hello);
}

JavaConfig "auto" injection of beans

I had being using the JSR 330 #Inject annotation to autowire my Spring beans. I started experimenting by removing the #Inject annotation - yet my application context still gets loaded correctly. Not sure if this is expected and cant find any spring documentation to verify this use case.
// This context is loaded correctly - and beans exist for B, C and Db
final ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
#Import({ B.class })
#Configuration
public class ApplicationConfig {
#Bean
public Db db() {
return new Database();
}
#Bean
// I thought this method would need an #Autowire or #Inject annotation to resolve b!?
public C c(final B b){
return new C(b);
}
}
#Configuration
public class BConfig {
#Bean
public B b() {
return new B();
}
}
#Autowired (or #Inject if you prefer) is implicit in #Bean methods (always has been as far as I know).

Passing parameters to #Configuration in Spring

I have a requirement of creating a prototype bean that's stateful, i.e. take parameters in constructor.
I tried to use #Configuration to create that bean, but found it doesn't work if I use a parameterized constructor...
Note that the parameters I want to pass are NOT spring beans...they are simple POJOs...so I can't autowire them.
So this is what I want to do -
#Configuration
public class MyClassFactory {
#Bean
public MyClass getMyClass(Pojo1 pojo1, Pojo2 pojo2) {
return new MyClass (pojo1, pojo2);
}
}
#Scope("PROTOTYPE")
public class MyClass {
public MyClass(Pojo1 pojo1, Pojo2 pojo2) {
...
}
#Autowired SomeService1 service1;
#Autowired SomeService1 service2;
...
}
Of course I can make MyClass applicationContextAware, and pick up services from it, rather than making it a prototype bean...but was wondering why above pattern is not allowed...

Resources