Spring non primary annotation in Java config - java

In XML config I can write (Class2 extends Class1):
<bean name="bean1" class="com.comp.Class1" primary="true"/>
<bean name="bean2" class="com.comp.Class2" primary="false"/>
But how can I reach the same effect using Java config? It's seems that place only one #Primary on bean1.
But then I got error "expected single matching bean but found 2" when autowiring by interface which implemented by Class1.

I am assuming you already know about qualifiers and your use-case is not to use that.
I think something like below should work in your case.
#Configuration
public class Config {
#Bean
public Employee JohnEmployee() {
return new Employee("John");
}
#Bean
#Primary
public Employee TonyEmployee() {
return new Employee("Tony");
}
}
MAIN JAVA CLASS
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(Config.class);
Employee employee = context.getBean(Employee.class);
System.out.println(employee);

Related

What happens when we define bean with different names with Annotations and XML config?

The coach object is created with Annotations and coach2 is created with XML configuration file. They both refering to the tennisCoach class.
In singleton scope beans refer to the same place in memory but in this situation memory place is different.
I have two questions.
Why Spring lets us create two objects?
Why it doesn't give an error about why an id is assigned to this object with annotation and we define the object again in XML or vice versa.
Does Spring create objects in prototype scope?
Coach coach = context.getBean("tennisCoach", Coach.class);
Coach coach2 = context.getBean("myCoach", Coach.class);
System.out.println(coach); //output: demo.TennisCoach#451001e5
System.out.println(coach2); //output: demo.TennisCoach#2b40ff9c
applicationContext.xml
<bean id="myCoach" class="demo.TennisCoach"></bean>
TennisCoach.java
#Component
public class TennisCoach implements Coach {...}
I know that one definition is enough for bean but I was just wondering about this situation.
Let me be more clear. Scope prototype creates a new bean every time you inject it. So if you have bean A and you inject it in bean B and C you will have two instance of bean A created.
Indeed let’s make some examples with singletons with xml and annotation. let’s say you have this xml configuration:
<bean id="myCoach1" class="demo.TennisCoach"></bean>
<bean id="myCoach2" class="demo.TennisCoach"></bean>
<bean id="bean1" class="demo.Bean1">
<property name=“coach” ref=“myCoach1” />
</bean>
<bean id="bean2" class="demo.Bean2">
<property name=“coach” ref=“myCoach2”/>
</bean>
So what’s happening here we are creating two beans of type demo.TennisCoach and injecting them by name in bean1 and bean2.
We can also use annotation configuration to achieve the same results:
#Configuration
public class Configuration {
#Bean(name = “myCoach1”)
public TennisCoach createMyCoach1() {
return new TennisCoach();
}
#Bean(name = “myCoach2”)
public TennisCoach createMyCoach2() {
return new TennisCoach();
}
#Bean
public Bean1 createBean1(#Qualifaer(“myCoach1” TennisCoach coach) {
return new Bean1(coach);
}
#Bean
public Bean2 createBean2(#Qualifaer(“myCoach2” TennisCoach coach) {
return new Bean2(coach);
}
}
Note the usage of #Qualifier to tell spring witch bean to inject. If you don’t use it spring doesn’t know which of the two TennisCoach should inject because by default it injects by type.
The same happens with #Autowired it by default injects by type but you can combine it with #Qualifier to inject the correct bean if you have more than one of the same type.
You can even mix xml and annotation (I don’t suggest it:
#Configuration
#ImportResource({“classpath:application-context.xml”})
public class Configuration {
#Bean
public Bean1 createBean1(#Qualifaer(“myCoach1” TennisCoach coach) {
return new Bean1(coach);
}
#Bean
public Bean2 createBean2(#Qualifaer(“myCoach2” TennisCoach coach) {
return new Bean2(coach);
}
}

Eliminating Spring.xml from Spring Framework

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.

#scope - spring - setting scope "prototype" using annotations, behaves like singleton. Where am i going wrong?

When i try to use #scope("prototype") over a class, I see it behaves similar to "singleton" I am not sure where I am wrong. Any help on this is much appreciated.
Employee class - setting scope - prototype
import org.springframework.context.annotation.Scope;
#Scope("prototype")
public class Employee {
private String emp_name;
public String getEmp_name() {
return emp_name;
}
public void setEmp_name(String emp_name) {
this.emp_name = emp_name;
}
}
Department class- setting scope - singleton
import org.springframework.context.annotation.Scope;
#Scope("singleton")
public class Department {
private String dep_name;
public String getDep_name() {
return dep_name;
}
public void setDep_name(String dep_name) {
this.dep_name = dep_name;
}
}
Beans.xml
<context:component-scan base-package="com"/>
<!-- Scope Annotations -->
<bean id="dep_scope" class="com.scope.annotation.Department" >
</bean>
<bean id="emp_scope" class="com.scope.annotation.Employee" >
</bean>
Main App
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
Employee emp = (Employee) context.getBean("emp_scope");
emp.setEmp_name("Saro");
System.out.println("first"+emp.getEmp_name());
Employee emp2 = (Employee) context.getBean("emp_scope");
System.out.println("second"+emp2.getEmp_name());
Department dep = (Department) context.getBean("dep_scope");
dep.setDep_name("Maths");
System.out.println("first"+dep.getDep_name());
Department dep2 = (Department) context.getBean("dep_scope");
System.out.println("second"+dep2.getDep_name());
}
}
Output :
firstSaro
secondSaro
firstMaths
secondMaths
I expected secondnull instead of secondSaro
Just try to define scope in beans.xml file in bean tag.... you are using both annotation and xml ... just use either..
Or you can try to define bean using annotation ...
for that you can use #Bean annotation.
#Configuration
public class Config {
#Bean(scope=DefaultScopes.PROTOTYPE)
public TestBean testBean(){
return new TestBean();
}
}
And In your main logic you can use bellow code
TestBean testBean = ctx.getBean(TestBean.class);
It behaves as a singleton because this is the ways it is defined in the your xml conf. Default scope is singleton, prototype should be declares like follows
<bean id="dep_scope" class="com.scope.annotation.Department" scope="prototype">
</bean>
If you want to use annotations, javadoc states that #Scope should be used in conjunction with #Component, like this
#Component
#Scope("prototype")
public class Employee {
//...
}
public class ExampleUsingEmployee {
#Inject
private Employee enployee; //will be automatically injected by spring
// ... do other stuff
}
You should avoid (if possible) using both xml and annotations.
You have put
context:component-scan base-package="com"
which will try to instantiate beans if they are annotated with #Component or the other bean annotations like #Service, etc. But this does not make spring process the other annotations configuration.
if you want to process the other spring annotations you must register
context:annotation-config> .
too in order to ask spring to look for the annotations in the defined beans.
in summary
context:component-scan : will instantiate beans within the package if they are annotated with suitable annotation
context:annotation-config : will process the annotations in the configured beans irrespective of whether they are defined via annotations or xml.

Spring 4, MyBatis, multiple data sources with annotations

I am currently in a Spring 4 application that uses MyBatis and is completely annotation-driven (that cannot change per architecture requirements). I am trying to add a second data source definition with a completely separate set of mapping configurations.
The problem I am having is that I cannot get the two data sources to play nicely together.
I created a new, virtually identical class and added #Qualifier data to the new file.
The configuration for the classes looks like this:
Data Source 1
#Configuration
#MapperScan (basePackages = "com.myproject.package1", annotationClass = Mapper.class)
public class DataSource1 {
#Bean
#Qualifier ("DS1")
public DataSource getDataSource() {
/* configuration loaded */
}
#Bean
#Qualifier ("DS1")
public SqlSessionFactory getSqlSessionFactory() {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(getDataSource());
/* mapper resources added */
return bean.getObject();
}
}
Data Source 2
#Configuration
#MapperScan (basePackages = "com.myproject.package2", annotationClass = Mapper.class)
public class DataSource2 {
#Bean
#Qualifier ("DS2")
public DataSource getDataSource() {
/* configuration loaded */
}
#Bean
#Qualifier ("DS2")
public SqlSessionFactory getSqlSessionFactory() {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(getDataSource());
/* mapper resources added */
return bean.getObject();
}
}
When this runs I get exception messages like:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
If I comment-out the data in DS2, DS1 works just fine again. I tried adding the mapper scanning configuration data in another bean and setting the name of the SqlSessionFactoryBean to pass into it but that did not work.
Suggestions?
UPDATE
I looked at this post and updated to use the following.
#Bean (name = "the_factory_1")
public SqlSessionFactory getSqlSessionFactory() { /* same code */ }
#Bean
public MapperScannerConfigurer getMapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.myproject.package1");
configurer.setAnnotationClass(Mapper.class);
configurer.setSqlSessionFactoryBeanName("the_factory_1");
return configurer;
}
However, that leads me to this error:
No qualifying bean of type [com.myproject.package1.mapper.MyMapper] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
When I debug only one #Bean for the factory gets invoked.
UPDATE 2
If I move everything to a single file all is fine. However, that is not ideal as I want the DataSource definitions to be separated. That's my only hurdle right now.
You can use ace-mybatis, it simplifies configuration.
Add one bean.
#Bean
public static AceMapperScannerConfigurer mapperScannerConfigurer() {
return AceMapperScannerConfigurer.builder()
.basePackage("com.myproject.package1")
.build();
}
And then mark your mapper interfaces with #AceMapper and specify sqlSessionFactory
#AceMapper(sqlSessionFactoryBeanName = "firstSqlSessionFactory")
public interface UserMapper {
Stream<User> selectUsers();
}
#AceMapper(sqlSessionFactoryBeanName = "secondSqlSessionFactory")
public interface ClientMapper {
Stream<Client> selectClients();
}
Please use DAOFactory pattern to get connections for multiple datasources like DS1 and DS2 and use DAOUtil class to provide required configuration using annotation

Injecting object into Spring Configuration

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");

Categories

Resources