Java class xml vs java bean autowiring - spring

In xml defined beans you can define two classes like this
<bean id="classA" class="ex.ClassA"/>
<bean id="classB" class="ex.classB"/>
Then in your java implementation you can autowire the constructor of one of the classes in example
public class ClassA {
#autowired
public(ClassB classB){
this.classB = classB;
}
Now how does one do that with java config beans since in example
#Bean
public ClassA classA(){
return new ClassB();
}
#Bean
public ClassB classB(){
return new ClassB()
}
the compiler would warn that Class a does not have any such constructor, how does one do that in java, with autowiring?
Thanks all

See http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-java-injecting-dependencies
Note that the ClassB bean is implicitly a singleton. The use of the annotation #Configuration on the Config class ensures that Spring returns the singleton instance of the ClassB bean in the classB() call.
#Configuration
public class Config {
#Bean
public ClassA classA(){
return new ClassA( classB() );
}
#Bean
public ClassB classB(){
return new ClassB();
}
}
Or you may prefer this approach (Spring 4.2.1+ required)
#Configuration
#Import(ClassA.class)
public class Config {
#Bean
public ClassB classB(){
return new ClassB();
}
}
#Component
public class ClassA {
#Autowired
public ClassA(ClassB classB) {
...
}
}

Pass the beans you want as parameters to the #Bean method, or use component scanning to create the dependent bean implicitly.

Related

Spring bean with same method name but different qualifier fail to load

I have two Spring Configuration classes defined as follows
#Configuration
public class ClsA {
#Bean
#Qualifier("ClasA")
public String getSomething(){
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean
#Qualifier("ClsB")
public String getSomething(){
return "somethingB";
}
}
Both have the same method name. Even though qualifiers are different, the application doesn't load as it only injects one and wherever the other one is injected, if fails with noBeanDefinition exception let's say for ClsB bean qualifier.
When I keep the method name different and everything loads hunky dory.
Is this behavior normal ? Why doesn't spring load these beans just fine as they have different qualifiers ?
The #Qualifier annotation is supposed to be used at injection points to resolve ambiguity as to which bean to inject. But in the example, you use it at bean declaration site. At declaration site, you can give a name to each bean by specifying it in #Bean annotation. So if you leave your methods with the same name, then a valid example can be as follows:
#Configuration
public class ClsA {
#Bean("ClasA")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("ClasB")
public String getSomething() {
return "somethingB";
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ClsB.class, ClsA.class})
public class ClsTest {
#Autowired
#Qualifier("ClasA") //this is the place where #Qualifier can be used
String smthA;
#Autowired
#Qualifier("ClasB")
String smthB;
#Test
public void test() {
System.out.println(smthA);
System.out.println(smthB);
}
}
Output:
somethingA
somethingB
1. Customize Bean Naming
Each bean name must be unique.
1.3.1. Naming Beans
1.12.3. Using the #Bean Annotation > Customizing Bean Naming
#Configuration
public class ClsA {
#Bean("clasAText")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("clsBText")
public String getSomething() {
return "somethingB";
}
}
2. (Optional) Add qualifier metadata
1.10.5. Defining Bean Metadata within Components
#Configuration
public class ClsA {
#Bean("clasAText")
#Qualifier("clsA")
public String getSomething() {
return "somethingA";
}
}
#Configuration
public class ClsB {
#Bean("clsBText")
#Qualifier("clsB")
public String getSomething() {
return "somethingB";
}
}
3-1. Inject by name
1.9.7. Injection with #Resource
#Component
public class MyComponent {
#Resource(name = "clasAText")
private String text;
// ...
}
Note:
if you intend to express annotation-driven injection by name, do not primarily use #Autowired (snip). Instead, use the JSR-250 #Resource annotation
3-2. Inject by qualifier
1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers
If qualifiers have been added, these are available.
#Component
public class MyComponent {
#Autowired
#Qualifier("clsA")
private String text;
// ...
}
This answer is a fallback behavior. Name and qualifier are different one.
1.9.4. Fine-tuning Annotation-based Autowiring with Qualifiers
For a fallback match, the bean name is considered a default qualifier value.

Why spring boot does not load beans configuration in order?

When i try to run a spring boot project, it tolde me that it can not autowire some beans whitch are instanciated in a configuration classes.
I think that spring can not load those configuration classes in order.
The stack trace : no bean found the be autowired Ignite<Long,MyEntity> myEntityCache in MyDao
Here is the source :
The main class
#SpringBootApplication
// The beans in the IgniteConfig have to be loaded before dao, service, and Controller
#ComponentScan(basePackageClasses={IgniteConfig.class,AppConfig.class})
public class DemoIgnite {
public static void main(String[] args) {
SpringApplication.run(DemoIgnite .class, args);
}
}
Config Class 1
#Configuration
public class IgniteConfig {
#Bean
public SpringContext springContext() {
return new SpringContext();
}
#Bean
public Ignite igniteInstance(#Autowired SpringContext springContext) {
IgniteConfiguration cfg = new IgniteConfiguration();
cfg.setIgniteInstanceName("instance");
List<CacheConfiguration> ccDas = new ArrayList<>();
CacheConfiguration cch = new CacheConfiguration<>("myEntitycache");
cch.setCacheMode(CacheMode.REPLICATED);
cch.setIndexedTypes(Long.class, myEntity.class);
ccDas.add(cch);
cfg.setCacheConfiguration( ccDas.toArray(new CacheConfiguration[0]));
SpringCacheManager springCacheManager = new SpringCacheManager();
springCacheManager.setConfiguration(cfg);
return Ignition.start(cfg);
}
#Bean
public IgniteCache<Long, MyEntity> myEntityCache(#Autowired Ignite igniteInstance) {
return igniteInstance.cache("myEntitycache");
}
Config class 2
#Configuration
#ComponentScan({
"com.demo.repository",
"com.demo.service",
"com.demo.controller"
})
public class AppConfig {
}
Dao class
#Repository
public class MyDao{
#Autowired
private Ignite<Long,MyEntity> myEntityCache;
...
Service class:
#Service
public class MyService{
#Autowird
private MyDao dao;
...
Controller class:
#RestController
#RequestMapping
public class MyController{
#Autowired
private MyService service;
....
This means that you don't have a bean of Ignite<Long,MyEntity> type in your context. Moreover springContext bean seems redundant, it's not used by igniteInstance bean. As pointed out by moilejter it probably should be:
IgniteConfig
#Bean
public Ignite ignite() {
...
}
#Bean
public IgniteCache<Long, MyEntity> myEntityCache() {
return ignite().cache("myEntitycache");
}
MyDao
#Repository
public class MyDao {
#Autowired
private IgniteCache<Long, MyEntity> myEntityCache;
...
}
In principle Spring performs the bean setup in few phases as explained in chapter 1.3.2. Instantiating Beans docs:
Bean definition discovery - resources like #Configuration classes or XML files are scanned and bean signatures are collected.
Eager beans instantiation e.g. singletons - from the definitions collected in point 1 while resolving dependencies between definitions. That's why there is no explicit bean instantiation order as the process is driven from dependencies.
Lazy beans instantiation e.g. #Lazy annotated - when the context is already up, this beans will be constructed only when accessed from code.

Spring boot defining configuration beans per user

I am using Spring boot. I have some question regarding the spring boot beans.
But I have doubt
I use bean which are default scope that is singleton. So they will have only one instance per application.
#Configuration
public class ...{
#Bean
public void method() {}
}
And
Now i use bean which scope is prototype. So they will have each instance per request.
#Configuration
public class ...{
#Bean
#Scope("prototype")
public void method() {}
}
But
I want single instance per user..? all request use single instance per user.
#Configuration
class Abc {
#Bean
#Scope("session")
public YourBean getYourBean() {
return new YourBean();
}
}
You will need to define one singleton bean with a property using prototype bean:(xml example)
With #bean definition:
#Component
#Scope("singleton")
public class SingletonBean {
// ..
#Autowired
private PrototypeBean prototypeBean;
//..
}
#Component
#Scope("prototype")
public class PrototypeBean {
//.......
}
Example: https://www.baeldung.com/spring-inject-prototype-bean-into-singleton

Does registering bean inside #Component class respect #Scope?

This website says beans registered inside component classes are not cglib proxied and do not go through the spring container. So does this mean if I register a bean inside a component class (snippet below), adding #Scope("request") wont make any difference, and a new instance of AnotherBean will always be created whenever testBean.anotherBean() is called from some external class?
#Component
public class TestBean {
#Bean
#Scope("request")
public AnotherBean anotherBean() {
return new AnotherBean();
}
}
The bean that is not cglib proxied is the #Component itself, not the bean registered using the #Bean annotation. If you are not calling the anotherBean method explicitly, it won't make a difference because the proxy is used to return the bean when the method annotated with #Bean is called. See the example
The bean testBeanComponent is not cglib proxied :
#Component
public class TestBeanComponent {
#Bean
#Scope("request")
public AnotherBeanComponent anotherBeanComponent() {
return new AnotherBeanComponent();
}
}
The bean testBeanConfiguration is cglib proxied :
#Configuration
public class TestBeanConfiguration {
#Bean
#Scope("request")
public AnotherBeanConfiguration anotherBeanConfiguration() {
return new AnotherBeanConfiguration();
}
}
What it mean :
#Service
public class TestService {
#Autowired //Inject a normal bean
private TestBeanComponent testBeanComponent;
#Autowired //Inject a proxy
private TestBeanConfiguration testBeanConfiguration;
public void test() {
//Calling anotherBeanComponent always return a new instance of AnotherBeanComponent
testBeanComponent.anotherBeanComponent()
.equals(testBeanComponent.anotherBeanComponent()); // is false
//Calling anotherBeanConfiguration return the bean managed by the container
testBeanConfiguration.anotherBeanConfiguration()
.equals(testBeanConfiguration.anotherBeanConfiguration()); // is true
}
}
But if you are injecting the bean instead of using the method, everything will work as you expected :
#Service
public class TestService2 {
#Autowired //Inject a proxy with scope request
private AnotherBeanComponent anotherBeanComponent;
#Autowired //Inject a proxy with scope request
private AnotherBeanConfiguration anotherBeanConfiguration;
}

Using #Qualifier and #Bean together in Java Config Spring

I have follow code
interface Drivable {
}
#Component
class Bmw implements Drivable {
}
#Component
class Mercedes implements Drivable {
}
class Driver {
private Drivable drivable;
public Driver(Drivable drivable) {
this.drivable = drivable;
}
}
And Spring Java Config
#Configuration
#ComponentScan
class CarConfig {
#Bean
#Qualifier("mercedes")//the code won't work
public Driver getDriver(Drivable drivable) {
return new Driver(drivable);
}
#Bean//I've added the bean
public Drivable getMercedes() {
return new Mercedes();
}
}
Can I use #Qualifier annotation with #Bean annotation if I want to specify type of object that should pass to method? I can't find in Spring doc how I can solve the problem. Thx.
I think you got the usage of #Qualifier bit wrong.
If we have more than one bean that qualifies for spring injection, then we use #Qualifer to specify which needs to be used for injection.
In this case you have two beans Bmw and Mercedes both implementing Drivable interface.
Presuming I got your intent correct, you want spring to inject Mercedes bean into the Driver object.
So for that, you need to specify public Driver getDriver(#Qualifier("mercedes") Drivable drivable) in the CarConfig class.
#Configuration
#ComponentScan
class CarConfig {
#Bean
public Driver getDriver(#Qualifier("mercedes") Drivable drivable) {
return new Driver(drivable);
}
And then you can use AnnotationConfigApplicationContext to load the spring context and subsequently get the Driver bean as below:
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(CarConfig.class);
Driver bean = ctx.getBean(Driver.class);
}
}
Just to extend the example, let us say if you want to create a Driver bean for each of Bmw and Mercedes then the sample code would be:
#Configuration
#ComponentScan
class CarConfig {
#Bean(name="mercedesDriver")
public Driver getMercedesDriver(#Qualifier("mercedes") Drivable drivable) {
return new Driver(drivable);
}
#Bean(name="bmwDriver")
public Driver getBmwDriver(#Qualifier("bmw") Drivable drivable) {
return new Driver(drivable);
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(CarConfig.class);
System.out.println(Arrays.asList(ctx.getBeanNamesForType(Driver.class)));
Driver mercedesBean = ctx.getBean("mercedesDriver", Driver.class);
Driver bmwBean = ctx.getBean("bmwDriver", Driver.class);
}
}
#Configuration
#ComponentScan
class CarConfig {
#Bean
public Driver mercedesDriver(Drivable bmw) {
return new Driver(bmw);
}
// same as
#Bean("mercedesDriver")
public Driver getDriver(Drivable bmw) {
return new Driver(bmw);
}
.....
#Autowired private Driver mercedesDriver;
In Spring you an relate on dependency injection by name. In the first example the name is defined by the method name. In the second example the method name is not important, because wie name the bean through the Annotation. Its very easy and simple. Not really need for #Qualifier. Only if the name should be different, but why?
You could define qualifiers for each specific implementations of interface Drivable. Once you did, now you could autowire them into CarConfig class and you have to be create Beans for each Drivers(Mercedz & Benz) along with qualifier names.
Find below Implementation:
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Bmw {
}
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Mercedes {
}
Now your Interface Implementations of Driver should be annotated with Qualifiers as below
#Compoenent
#Bmw
public interface Bmw implements Drivable{
}
#Component
#Mercedes
public interface Mercedes implements Drivable{
}
Your CarConfig class should be as below:
#Configuration
public class CarConfig{
#autowire
#Bmw
private Drivable bmwDriver;
#autowire
#Mercedes
private Drivable mercedesDriver;
#Bean
public Bean getBmwDriver(){
return new Bmw(bmwDriver);
}
#Bean
public Bean getMercedesDriver(){
return new Mercedes(mercedesDriver);
}
}
NOTE: if you are creating bean with #Bean, it will be injected byType if there is duplicates then it will injected byName. we no need to mention #Bean(name="bmwDriver") .
so you can directly use qualifier("bmwDriver") wherever you need in classes.

Resources