Why spring can't find my bean? - java

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

Related

Spring: How to make a service conditional on the availability of a PlatformTransactionManager bean?

I have a FooService that I would like to be available only when a PlatformTransactionManager is available.
If I define my service like this and no PlatformTransactionManager is available, then my application will fail to start:
#Service
public class FooService {
public FooService(final PlatformTransactionManager txManager) { ... }
...
}
I wanted to use ConditionalOnBean, which should only annotate auto configuration classes. I refactored my code like this:
#Configuration
public class FooAutoConfiguration {
#Bean
#ConditionalOnBean(PlatformTransactionManager.class)
public FooService fooService(final PlatformTransactionManager txManager) {
return new FooService(txManager);
}
}
public class FooService {
public FooService(final BarBean bar) { ... }
...
}
I wrote the following test for FooService:
#ExtendWith(SpringExtension.class)
#Import(FooAutoConfiguration.class)
public class FooServiceTest {
#Autowired
private FooService fooService;
#Test
public void test() {
System.out.println("fooService = " + fooService);
}
}
But I get the following exception when I try to run it:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.acme.FooServiceTest': Unsatisfied dependency expressed through field 'fooService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.acme.FooService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
(...)
However, I know that a PlatformTransactionManager bean is available, because the test runs fine when I #Autowire a PlatformTransactionManager in my test instead of a FooService.
Interestingly, I also tried to replace PlatformTransactionManager with WebClient.Builder, and everything ends up working as it should. What is so special about PlatformTransactionManager?
How can I write FooService so that it will work when a PlatformTransactionManager bean is available, and not prevent applications where no such bean is available from starting?
Add #AutoConfigureOrder annotation to ensure that your auto-configuration class is processed after the transaction manager bean is registered by Spring Boot:
#Configuration
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public class FooAutoConfiguration {
}

Autowiring fails in Junit testing and spring #configuration

I have two #Configuration classes. I need a bean from one configuration class to another. I have autowired the configuration 1 into 2. All works fine. When executing the unit testing, am getting the below exception.
setUpContext(com.trafigura.titan.framework.services.messaging.loader.SpringLoadTest)
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.xxx.MessagingServicesConfig': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.xxx.EMSJMSConfig com.xxx.MessagingServicesConfig.emsJmsConfig;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type[com.xxx.EMSJMSConfig] 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)}
Is there anything I need to do additionally to make this working?
Below is the setup for testing.
#Configuration
#Import({MessagingServicesConfig.class,...,EMSJMSConfig.class
})
public class MessagingConfig {}
#Profile("EMS-MESSAGING")
#Configuration
public class EMSJMSConfig {
#Bean
public javax.jms.ConnectionFactory jmsSubscriberConnectionFactory() throws JMSException {
SingleConnectionFactory singleConnectionFactory = new SingleConnectionFactory(tibjmsConnectionFactory());
return singleConnectionFactory;
}
}
#Configuration
public class MessagingServicesConfig {
#Autowired
private EMSJMSConfig emsJmsConfig;
#Bean(destroyMethod = "shutdown")
public MessagingService messagingService() throws JMSException {
...
ConnectionFactory cf=emsJmsConfig.jmsSubscriberConnectionFactory(); // Getting NPE at this line.
}
}
and finally the test class,
public class MessagingServicesConfigTest {
private MessagingServicesConfig config;
private EMSJMSConfig emsJmsConfig;
#BeforeMethod
public void setUp() throws Exception {
config = new MessagingServicesConfig();
... //what needs to be done here to have the EMSJMSConfig
}
#Test
public void testBuildsCorrectService() throws JMSException {
MessagingService service = config.messagingService();
...
}
}
By calling new you're creating object yourself, Spring doesn't know anything about it.
Moreover, you should have a test configuration which will be aware of your beans.
Use an appropriate Runner to load SpringContext.
#ContextConfiguration(classes = TestConfig.class)
#RunWith(SpringRunner.class)
class Tests {
#Autowired // if needed
private MessagingServicesConfig config;
}
While in TestConfig you can create beans or import configuration from the Application:
#Configuration
#Import({MessagingServicesConfig.class})
public class TestConfig {}
#Configuration
#Import({EMSJMSConfig.class})
public class MessagingServicesConfig {}
Or you can refer to your config classes directly:
#ContextConfiguration(classes = {MessagingServicesConfig.class, EMSJMSConfig.class})

SpringBoot - UnsatisfiedDependencyException Spring Boot not Autowiring Repository interface which extends CrudRepository

Spring Boot application is throwing an exception while starting the server.
Exception is:
Exception encountered during context initialization - cancelling
refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'userController': Unsatisfied dependency
expressed through field 'userService'; nested exception is
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'userService': Unsatisfied dependency
expressed through field 'userRepository'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'yhcmain.healthcare.repositories.UserRepository' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
APPLICATION FAILED TO START
Description:
Field userRepository in yhcmain.healthcare.service.user.UserServiceImpl required a bean of type 'yhcmain.healthcare.repositories.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'yhcmain.healthcare.repositories.UserRepository' in your configuration
Controller:
#RestController
#CrossOrigin("*")
public class UserController {
#Autowired
private ServiceResponse response;
#Autowired
private UserService userService;
#RequestMapping(value = "/index", method = RequestMethod.POST, headers = "Accept=application/json")
public ResponseEntity<ServiceResponse> signUpAttempt(#RequestBody User user) {
...
...
}
}
Service:
#Service("userService")
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
public User createUser(User user) {
return this.userRepository.save(user);
}
}
Repository:
#Repository("userRepository")
public interface UserRepository extends CrudRepository<User, String> {
}
Main Application:
#SpringBootApplication
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class HealthcareApplication implements WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(HealthcareApplication.class, args);
}
#Override
public void addCorsMappings(CorsRegistry registry) {
...
...
}
}
You need to provide Entity's primary key(ID in Long or Integer) in CrudRepository Interface in your repository definition and make sure that #ComponentScan("RootDirectoryURL") is working fine.
...
#Repository("userRepository")
public interface UserRepository extends CrudRepository<User, PrimaryKey> {}
...
Happy Coding.. :)
You need to add the following annotation to your configuration class (HealthcareApplication):
#EnableJpaRepositories("<repository-package>")

NoSuchBeanDefinitionException: No qualifying bean of type [duplicate]

This question already has an answer here:
What is a NoSuchBeanDefinitionException and how do I fix it?
(1 answer)
Closed 6 years ago.
I stack with above mentioned exception and really don't understate why it is appeared. I am using spring boot and declare bean through the annotation.
Application is executed by this class:
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
My problem bean has the following declaration:
#Service
public class OrderSvc extends AbstractService implements DAOService {
I try to put it in the following bean:
#RestController
public class OrderController {
#Autowired
CarSvc carSvc;
#Autowired
OrderSvc orderSvc;
and the exception is appeared: Could not autowire field: biz.Services.OrderSvc biz.controllers.rest.administrator.OrderController.orderSvc; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [biz.Services.OrderSvc] 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 have also CarSvc bean that is located at the same package as OrderSvc and extends the same classes but there are no problems with it injection
#Service
public class CarSvc extends AbstractService implements DAOService<Car> {
Do you have any ideas why this exception appears ?
Spring creates proxies for classes that declare #Transactional, so that it is able to add the transactional behaviour and intercepted calls to your object. If the bean extends any interface Spring is going to create a Dynamic Proxy using the JDK Reflection API and this can only be done by interface. The proxy is a new object implementing the same interface. So your target bean is not your implementation but a proxy. That is why you were getting a non qualify bean exception.
CGLIB, on the other hand, can create a proxy by subclassing.
So, to get it working, you need to change your bean type to the interface or you can configure cglib using #EnableTransactionManagement(proxyTargetClass = true).
Try to to autowire your beans using interfaces rather than implementations :
#RestController
public class OrderController {
#Autowired
#Qualifier("carSvc")
DAOService carSvc;
#Autowired
#Qualifier("orderSvc")
DAOService orderSvc;
}
Edit : But before that you have to give names to your services :
#Service("carSvc")
public class CarSvc extends AbstractService implements DAOService<Car> {}
#Service("orderSvc")
public class OrderSvc extends AbstractService implements DAOService<Order> {}
What's going on here is that Spring generate proxies of your services based on the CarSvc, OrderSvc and implement the DAOService but does not extend the CarSvc, OrderSvc.
//somthing like this
class CarSvcProxy implement DAOService {
public Object getOrder(Long id) {
try {
// ...
txManager.commit();
} catch (Exception ex) {
txManager.rollback();
}
}
}
#RestController
public class OrderController {
//So when you do this :
#Autowired
CarSvc carSvc;
//it's somehow like if you did :
CarSvc carSvc = new CarSvcProxy(); //carSvc != CarSvcProxy
//But this will work :
DAOService carSvc = new CarSvcProxy(); //because CarSvcProxy implement DAOService
}
I found the code which leads to exception but I really don't undestand why.
In my OrderSvc there is the following method:
#Transactional(readOnly = true)
public Object getOrder(Long id) {
final Order order = getDAO().findOne(id);
OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);
return orderDTO;
}
So if the annotation #Transactional(readOnly = true) was excluded the application can be excecuted without problem... Do you have any ideas why this annotation lead to NoSuchBeanDefinitionException ?

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