What is wrong with my #component? - java

I want to autowire my components , but it seems I cannot figure it out. I know how to do it with #Autowired & #Qualifier using context:annotation-config in my xml. But how can I do the same job with components? My snippet is:
The component where I want to inject bean.
#Component
public class Pianist implements Performer{
private Instruments instrument;
#Autowired
public void makeInstrument(Instruments instrument) {
this.instrument = instrument;
}
#Override
public void perform() {
instrument.play();
}
My component which will be injected:
#Component
public class Piano implements Instruments{
#Override
public void play() {
System.out.println("Piano");
}
}
My xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan
base-package = "com.city.lt">
</context:component-scan>
</beans>
My main:
ApplicationContext context = new ClassPathXmlApplicationContext("builder.xml");
Performer performer = (Performer)context.getBean("Pianist");
performer.perform();
Then I try to run it, I am getting this error:
Could not autowire method: public void com.city.lt.Pianist.makeInstrument(com.city.lt.Instruments);
What is wrong with that? Thanks
EDITED: stacktrace
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pianist': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.city.lt.Pianist.makeInstrument(com.city.lt.Instruments); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.city.lt.Instruments] is defined: expected single matching bean but found 2: [guitar, piano]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at com.city.lt.main.main(main.java:11)

you need the #Autowired on a setter/constructor/or field.
If you chose setter, please name it like a setter
#Autowired
public void setInstrument(Instruments instrument) {
this.instrument = instrument;
}
All Beans created start with a lower case (by default):
Performer performer = (Performer)context.getBean("pianist");
The problem is that you have 2 implementations of Instrument. Spring does not know which one to chose (because it is really uncreative and doesn't care about music)
You need to tell Spring what exactly it should use. Ether by annotating one with #Primary or wire all implementations into a Collection or use the implementation class you want to wire as field type or use #Qualifier("piano") with your autowired annotation.
#Autowired
public void makeInstrument(#Qualifier("piano") Instruments instrument) {
this.instrument = instrument;
}

If you don't need the "makeInstrument" method after, it's easier this way:
#Component
public class Pianist implements Performer{
#Autowired
private Instruments instrument;
#Override
public void perform() {
instrument.play();
}
}

Take a look at the stack trace
No unique bean of type [com.city.lt.Instruments] is defined: expected single matching bean but found 2: [guitar, piano]
You have 2 classes implementing the Instruments interface. Spring, by default, when using #Autowired does this by type. As there are 2 beans of the same type it doesn't know which one to inject. (Which is also what your stack trace tells you).
To only way to make this work is to use, indeed, a #Qualifier to specify which one to inject.

Related

Spring #Autowire when using multi-level interfaces

I want to find a solution to configure my spring to support multi-level inheritance of interfaces.The considered scenario is as follow:
public class MainClass {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) ctx.getBean("textEditor");
te.spellCheck();
}
//--------------------
#Component
public class TextEditor {
#Autowired
private SpellChecker1 spellChecker;
public void spellCheck() {
spellChecker.checkSpelling();
}
}
//--------------------
public interface SpellChecker1 extends SpellChecker2{
}
//--------------------
public interface SpellChecker2 {
public void checkSpelling();
}
//--------------------
#Service
public class SpellCheckerImpl implements SpellChecker2 {
public SpellCheckerImpl() {
System.out.println("Inside SpellChecker constructor.");
}
#Override
public void checkSpelling() {
System.out.println("Inside checkSpelling.");
}
}
//--------------------
Also, my Beans.xml file is contain:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.mycompany.test"/>
<bean id="textEditor" class="com.mycompany.test.TextEditor" >
</bean>
</beans>
//The following error is issued when running the the MainClass
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'textEditor': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.mycompany.test.SpellChecker1 com.mycompany.test.TextEditor.spellChecker; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mycompany.test.SpellChecker1] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.

Error creating bean with name 'Individual_Controller': Injection of autowired dependencies failed;

My tomcat is refusing to launch my application due to this error
Error creating bean with na
me 'Individual_Controller': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Cou
ld not autowire field: private net.service.datastore.Indiv
idual_Service net.controller.Individual_Controller.S
ervice; nested exception is org.springframework.beans.factory.NoSuchBeanDefiniti
onException: No qualifying bean of type
[net.service.datastore.Individual_Service] found for dependency: expected at least 1 bean which qu
alifies as autowire candidate for this dependency. Dependency annotations: {#org
.springframework.beans.factory.annotation.Autowired(required=true)}
expected at least 1 bean which qualifies a
s autowire candidate for this dependency. Dependency annotations: {#org.springfr
amework.beans.factory.annotation.Autowired(required=true)}
this is my service class
public long createT(Individual individual);
public Individual updateT(Individual individual);
public void deleteT(String tin);
public List<Individual> getAllTs();
public Individual getT(String t);
public List<Individual> getAllTs(String individual);
this is my controller class that is calling the service layer
#Autowired
private Individual_Service service;
#RequestMapping("searchT")
public ModelAndView searchT(#RequestParam("searchName") String searchName) {
logger.info("Searching the T: "+searchName);
List<Individual> tList = service.getAllTs(searchName);
return new ModelAndView("serviceDescription", "tList", tList);
}
this is the complete controller class
#Controller
public class IndividualController {
private static final Logger logger = Logger.getLogger(IndividualController.class);
public IndividualController() {
System.out.println("Individual_Controller()");
}
#Autowired
private IndividualService service;
#RequestMapping("searchT")
public ModelAndView searchT(#RequestParam("searchName") String searchName) {
logger.info("Searching the T: "+searchName);
List<Individual> tinList = service.getAllTs(searchName);
return new ModelAndView("serviceDescription", "tList", tList);
}
complete service interface class for the individual_service
package net.service.datastore;
import java.util.List;
import net.model.Individual;
public interface IndividualService {
public long createT(Individual individual);
public Individual updateT(Individual individual);
public void deleteT(String t);
public List<Individual> getAllTs();
public Individual getT(String t);
public List<Individual> getAllTs(String individual);
}
Please what could be wrong?
Looks like no bean of class Individual_Service is loaded in Application Context. There can be multiple reasons for that.
If you are using xml based configuration , please make sure that xml file is either included in web.xml using ContextLoaderListner.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-bean.xml
</param-value>
</context-param>
or in dispatcher-servlet.xml / applicationContext.xml
<import resource="classpath:bean.xml" />
If you are using annotation based Approach , make sure the class is properly annotated with #Component or #Bean or #Service or any other annotation based on application requirement.
Make sure path in context:component-scan is covering the package of Individual_Service.java
<context:component-scan base-package="net.service.datastore.*" />
Hope one of these point resolve your issue.
If not can you please provide your web.xml , dispatcher-servlet.xml or Spring configuration class and Individual_Service.java .
There's some information missing to provide a complete answer.
But basically, you should make sure of the following:
Your IndividualService implementation class should be annotated with #Service
Your IndividualController class should be annotated with #Controller
The field IndividualService in IndividualController should be annotated with #Autowired (or #Inject)
Both classes should be scanned in your Spring context config class (or file)
Here is how it should look like :
IndividualService.java :
package com.company.myapp.service;
//...imports...
#Service
public class IndividualService {
//.. fields/methods...
}
IndividualController.java :
package com.company.myapp.controller;
//...imports...
#Controller
public class IndividualController{
#Autowired
private IndividualService individualService;
//.. other fields/methods...
}
MyAppConfiguration.java :
package com.company.myapp;
//...imports...
#Configuration
#EnableWebMvc
//...other Spring config annotation...
#ComponentScan(basePackages = "com.company.myapp")
public class MyAppSpringConfiguration{
//...other configuration...
}
To know which class is your Spring config class, you should have a look at your webapp descriptor file (web.xml), and see which contextConfigLocation is provided as param to the Spring servlet (DispatcherServlet). If your're user servlet 3+ without web.xml, look for a class that implements WebApplicationInitializer.

Error in creation of bean while using #Required and #Autowired

I am new to spring.I am trying to make use #Required and #Autowired in my code but its giving me org.springframework.beans.factory.BeanCreationException.Below is my code.
1) StudentAuto.java
public class StudentAuto
{
#Autowired
private String name;
#Autowired
private String city;
public String getCity() {
return city;
}
#Required
public void setCity(String city) {
this.city = city;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2)spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config></context:annotation-config>
<bean id='stu' class='com.bean.StudentAuto' >
</bean>
<bean name='name' class='java.lang.String'>
<constructor-arg value="nm"></constructor-arg>
</bean>
<bean name='city' class='java.lang.String'>
<constructor-arg value="ci"></constructor-arg>
</bean>
</beans>
3)TestApp.java
public class TestApp {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
StudentAuto auto=context.getBean("stu", StudentAuto.class);
System.out.println(auto.getCity());
System.out.println(auto.getName());
}
}
and error stack trace is below.
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'stu' defined in class path resource [spring.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanInitializationException: Property 'city' is required for bean 'stu'
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1079)
at com.bean.TestApp.main(TestApp.java:14)
Caused by: org.springframework.beans.factory.BeanInitializationException: Property 'city' is required for bean 'stu'
at org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.postProcessPropertyValues(RequiredAnnotationBeanPostProcessor.java:149)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1074)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
... 7 more
Please help me on this issue.
The javadoc for #Required states
Marks a method (typically a JavaBean setter method) as being
'required': that is, the setter method must be configured to be
dependency-injected with a value.
Note that the annotated method is not necessarily a setter but that is usually what it is.
#Required methods are processed by RequiredAnnotationBeanPostProcessor which states that
This neatly pushes responsibility for such checking onto the container
(where it arguably belongs), and obviates the need (in part) for a
developer to code a method that simply checks that all required
properties have actually been set.
So the purpose is to guarantee that properties are set by checking if the container has actually invoked the method.
The typical pattern is
class Foo {
private String value;
#Required
public void setValue(String value) {
this.value = value;
}
}
with a bean definition
<bean class="Foo" id="fooBean">
<property name="value" value="some value"/>
</bean>
If you had not added the <property>, the container would complain and throw exceptions, just like it does with your configuration
<bean id='stu' class='com.bean.StudentAuto' >
</bean>
Here, the container is not using the #Required method to set the property. It is using reflection on the Field directly because of #Autowired. Therefore the #Required annotation is not validated.
1. DOC:
#Required
This annotation simply indicates that the affected bean property must be populated at configuration time, through an explicit property value in a bean definition or through autowiring.
2. Please pay attention:
#Required annotation is used for validation checking, not for dependency injection.
3. A way to fix:
As the error log shows: Property 'city' is required for bean 'stu'. So, you should add a propery tag into the stu bean - inject city manually:
<bean id="stu" class="com.bean.StudentAuto">
<property name="city" value="London"/>
</bean>

Why spring can't find my bean?

I created an interface and a class:
public interface UserService {
List<User> listAll();
}
#Transactional
public class DefaultUserService implements UserService {
private String tableName;
public List<User> listAll() { someDao.listAllFromTable(tableName); }
public void setTableName(String tableName) { this.tableName = tableName; }
}
Also in my application context xml file context.xml, I defined:
<bean id="userService" class="mypackage.DefaultUserService">
<property name="tableName" value="myusers" />
</bean>
Then I want to test the DefaultUserService:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:context-test.xml"})
#TransactionConfiguration(transactionManager = "testTransactionManager")
#Transactional
public class UserServiceTest {
#Autowired
private DefaultUserService userService;
#Before
public void setup() {
userService.setTableName("mytesttable");
}
#Test
public void test() {
// test with userService;
userService.listAll();
}
}
Notice it uses context-test.xml, which imported the original context.xml:
<import resource="classpath:context.xml"/>
Unfortunately, when the test starts, spring throws exception:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'mypackage.UserServiceTest':
Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field:
private mypackage.DefaultUserService mypackage.userService
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [mypackage.DefaultUserService] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I'm not sure where is wrong, why spring can't find the bean DefaultUserService I defined?
It's because #Transactional places the bean is behind a jdk proxy implementing UserService interface, after that the bean is only available as UserService and not DefaultUserService.
See https://stackoverflow.com/a/18875681/241986.
You can try setting the table name with a property placeholder #Value("${someprop}") and define that property in test context, or create another interface that will expose setTableName(), and autowire that helper interface into the test case.
I'm not sure there are any easy solutions of the problem, I think this task can be subsumed under the problem of bean redefinition in Spring test-context framework
Spring beans redefinition in unit test environment
Try to replace the class DefaultUserService to the interface UserService
public class UserServiceTest {
#Autowired
private UserService userService;
....
}
You have not defined the getter for your property tableName in your implementing class.Spring IOC container works on the POJO model

Can Spring Autowire beans created in a BeanFactoryPostProcessor

I have a standard bean with some properties that need to be autowired.
#Service
public class MyServiceImpl implements MyService {
#Autowired
private FirstRepository first;
public MyServiceImpl() {
}
I use a Java Config to find the beans:
#Configuration
#ComponentScan(basePackages = "com.company", excludeFilters = { #Filter(Configuration.class) })
public class MainConfig {
}
However, the FirstRepository Bean doesn't exist so I create it in a BeanFactoryPostProcessor:
public class RepoGeneratorPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition jpaR = new GenericBeanDefinition();
jpaR.setBeanClass(JpaRepositoryFactoryBean.class);
jpaR.setAutowireCandidate(true);
jpaR.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
jpaR.setLazyInit(false);
jpaR.setPropertyValues(new MutablePropertyValues().add("repositoryInterface", FirstRepository.class));
RootBeanDefinition definition = new RootBeanDefinition();
definition.setBeanClass(FirstRepository.class);
definition.setAutowireCandidate(true);
definition.setFactoryBeanName("&jpaR");
definition.setFactoryMethodName("getObject");
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_NAME);
definition.setLazyInit(false);
definition.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;
registry.registerBeanDefinition("jpaR", jpaR);
registry.registerBeanDefinition("first", definition);
}
When I start my application I get the following exception which seems to suggest that Spring can't find the FirstRepository bean.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.company.FirstRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.
If I remove the #Autowired annotation I can see after start up that the FirstRepository bean is properly created.
Any suggestions?
This exception is saying that there is no bean defined for the FirstRepository class when the project is being built. Which I cannot see it here either.
The simplest solution would be to have a bean definition in your application-context.xml like this:
<bean id="firstRepository" class="your.package.FirstRepository" autowire="byName"/>
In this case, at the start up, there will be that bean definition.
I don't think you need the & before the beanname in
definition.setFactoryBeanName("&jpaR");
I used something like that in my project
definition.setFactoryBeanName("jpaR");
and it worked as expected
The & is needed if you need to get the factory bean of the bean named first.
&first should return jpaR.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-extension-factorybean

Categories