Spring component detection without xml bean definitions - spring

Is it correct that one can create spring beans using just the #Component annotation as long as context component scanning is configured?
Using spring 3.0.5 with Java 6.
My test case is:
#ContextConfiguration(locations={"classpath:spring-bean.xml"})
public class ServerServiceUnitTest extends AbstractJUnit4SpringContextTests {
#Autowired
private ServerService serverService;
#Test
public void test_server_service() throws Exception {
serverService.doSomething();
//additional test code here
}
}
The spring-bean.xml file contains:
<?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: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/>
</beans>
My class I want to be a bean is:
#Component("ServerService")
public class ServerServiceImpl implements ServerService {
private static final String SERVER_NAME = "test.nowhere.com";
//method definitions.....'
}
Should that not be sufficient for spring to instantiate the ServerService bean and do the autowiring?
The error I get is:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [serversystem.ServerService] 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 sure I'm missing something simple.

You have not defined in your spring-beans.xml the <context:component-scan> element:
<context:component-scan base-package="the.package.with.your.service"/>
The inclusion of
<context:annotation-config/>
only allows you to use #Required, #Autowired, and #Inject annotations for configuration. By specifying the <context:component-scan>, you are telling Spring where to look for #Component annotations.

if you are using annotated controllers and other features
you should include
<mvc:annotation-driven/>
you should use
<context:component-scan base-package="spring3.example.controllers"/>
to specify the package in which controller classes are stored.

Related

#Configuration annotation causes IllegalArgumentException "is not an enhanced class"

I'm rather new to Spring and I'm working with an old 3.2.4.RELEASE version of it inside a Jetty container. I'm getting a rather odd error when I try to use component-scanner indicating that the #Configuration class I've set up is not an enhanced class:
Caused by: java.lang.IllegalArgumentException: class com.datasource.DBConfiguration$$EnhancerByCGLIB$$8a295c55 is not an enhanced class
at org.springframework.cglib.proxy.Enhancer.setCallbacksHelper(Enhancer.java:621) ~[spring-core-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at org.springframework.cglib.proxy.Enhancer.registerStaticCallbacks(Enhancer.java:594) ~[spring-core-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:120) ~[spring-context-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:92) ~[spring-context-3.2.4.RELEASE.jar:3.2.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:356) ~[spring-context-3.2.4.RELEASE.jar:3.2.4.RELEASE]
... 78 common frames omitted
My spring beans file is on the classpath and looks like the following
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="com.datasource" /></beans>
The configuration class that I'm trying to setup looks like the following:
#Configuration
public class DBConfiguration {
#Bean
public DataAccessInterface getDataAccessInterface(){
return new DBDatasource();
}
}
The class that I want to inject the DBDatasource into looks like the following:
#Service
public class DBService {
private DataAccessInterface dai;
#Autowired
public DBService(DataAccessInterface dai){
this.dai = dai;
}
}
What does the exception mean when it says that it is not an enhanced class? Am I not using component scan and the configuration annotation properly?
Please include
#ComponentScan(basePackages = "com.example", includeFilters = {#Filter(filterType = ANNOTATION, value = CONFIGURATION)}, excludeFilters = {#Filter(filterType = ASSIGNABLE_TYPE, value = DBConfiguration)})

#PostConstruct spring does not get called without bean declaration

Why post construct does not get called without putting bean in applicationContext.xml
Here is my class which contains #PostConstruct annotation.
package org.stalwartz.config;
import javax.annotation.PostConstruct;
import javax.inject.Singleton;
#Singleton
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Below is my applicationContext.xml
<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" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.directwebremoting.org/schema/spring-dwr
http://www.directwebremoting.org/schema/spring-dwr/spring-dwr-3.0.xsd">
<dwr:annotation-config />
<dwr:annotation-scan base-package="org.stalwartz" scanDataTransferObject="true" scanRemoteProxy="true" />
<dwr:url-mapping />
<!-- <bean id="proeprtyLoader" class="org.stalwartz.config.PropertyLoader"></bean> -->
<dwr:controller id="dwrController" debug="false">
<dwr:config-param name="activeReverseAjaxEnabled" value="true" />
</dwr:controller>
<context:annotation-config>
<context:component-scan base-package="org.stalwartz" annotation-config="true"></context:component-scan>
</context:annotation-config>
<mvc:annotation-driven />
...
...
...
</beans>
Looks simple, but it does not work without uncommenting bean declaration.
In Spring environment initialization callback method (the one annotated by #PostConstruct) make sense only on spring-managed-beans. To make instance(s) of your PropertyLoader class managed, you must do one of the following:
Explicitly register your class in context configuration (as you did)
<bean id="proeprtyLoader" class="org.stalwartz.config.PropertyLoader"></bean>
Let component scanning do the work (as you nearly did), but classes must be annotated by one of #Component, #Repository, #Service, #Controller.
Note from Spring documentation: The use of <context:component-scan> implicitly enables the functionality of <context:annotation-config>. There is usually no need to include the <context:annotation-config> element when using <context:component-scan>.
Because putting bean in applicationContext.xml you are adding bean to Spring container, which has interceptor for this annotation. When Spring inject beans it checks #PostConstruct annotation, between others.
When you call simple new PropertyLoader() JVM will not search for the #PostConstruct annotation.
From doc of #PostConstruct annotation:
The PostConstruct annotation is used on a method that needs to be executed
after dependency injection is done to perform any initialization. This
method MUST be invoked before the class is put into service. This
annotation MUST be supported on all classes that support dependency
injection. The method annotated with PostConstruct MUST be invoked even
if the class does not request any resources to be injected.
Singleton is a scope annotation. It can be used to declare 'singletone' scope for a particular bean, but not instantiate it. See this article.
If you want to instantiate your class as singleton you can try Spring Service annotation.
#Service
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Also, you can replace annotation-config tag with component-scan. Here is a good article about differences of annotation-config and component-scan tags.
you are using #Singleton from javax.inject package which is not picked up as bean by spring container. Change it to :
package org.stalwartz.config;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
#Component
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
and the spring will auto detect PropertyLoader and will include it in Spring container as bean via the #Component annotation and this bean will be with singleton scope
by default a bean is singleton scoped in Spring, and #PostConstruct is usually used for service beans and service beans must scoped prototype and here because you need multiple objects for that particular class, Spring will provide you singleton instance.
also by doing this spring will attempt multiple times to find this service bean and finally throws below exception:
java.lang.NoClassDefFoundError: Could not initialize class org.springframework.beans.factory.BeanCreationException
so try like this in annotation way:
package org.stalwartz.config;
import javax.annotation.PostConstruct;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope("prototype") //you have to make it prototype explicitly
public class PropertyLoader {
#PostConstruct
public void init() {
System.out.println("PropertyLoader.init()");
}
}
Now every thing is good, and work fine for you.
add this dependency to pom.xml
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>

Exception Autowiring Service interface in controller class

I and trying to autowire a service interface in the controller but i am getting an error, please help. Codes below :
Exception
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'todoComponentController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.rapidinstinct.avia.plat.service.component.TodoCS com.rapidinstinct.avia.plat.service.rest.controller.TodoComponentController.tocs; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.rapidinstinct.avia.plat.service.component.TodoCS] 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)}
Controller class
#RestController
#RequestMapping ("/todo")
public class TodoComponentController extends BaseController {
private static Logger _LOG = LoggerFactory.getLogger(TodoComponentController.class);
#Autowired TodoCS tocs;
#RequestMapping(method = RequestMethod.POST)
public create() {
return null;
}
}
Service Interface
#Service
public interface TodoCS extends ComponentService {
}
XML file
<?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:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
>
<context:annotation-config />
<context:component-scan base-package="com.rapidinstinct.avia.plat.service, com.rapidinstinct.avia.plat.cbo.mapper, com.rapidinstinct.avia.plat.bo.project.mapper"/>
</beans>
You have to annotate the implementation of TodoCS Not the interface itself.
#Service
public class TodoCSImpl implements TodoCS {
}
Then you can autowire the class.
You need a class which implements TodoCS interface. Then, annotation the implementation class with #Component annotation.
Spring, at time of context initialization is trying to search a bean name TodoCS in package : com.rapidinstinct.avia.plat.service.component.TodoCS.
And in configuartion file you mentioned package name as : com.rapidinstinct.avia.plat.service. Try adding com.rapidinstinct.avia.plat.service.component.TodoCS in base package scan as below:
<context:component-scan base-package="com.rapidinstinct.avia.plat.service, com.rapidinstinct.avia.plat.cbo.mapper, com.rapidinstinct.avia.plat.bo.project.mapper,com.rapidinstinct.avia.plat.service.component"/>

Problems with #Autowire annotation (null)

I have problems with two services that i autowired in a validator class. The services works ok because in my controller are autowired. I've an applicationContext.xml file and MyApp-servlet.xml file. My base package is es.unican.meteo and i have problems with the package es.unican.meteo.validator. The package es.unican.meteo.controller and es.unican.meteo.service can autowire the services properly.
applicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
....
some beans
...
</beans>
Myapp-servlet.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<!-- Enabling Spring beans auto-discovery -->
<context:component-scan base-package="es.unican.meteo" />
<!-- Enabling Spring MVC configuration through annotations -->
<mvc:annotation-driven />
Class ResetPasswordValidator:
package es.unican.meteo.validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import es.unican.meteo.model.User;
import es.unican.meteo.service.MessageService;
import es.unican.meteo.service.UserService;
public class ResetPasswordValidation implements Validator{
#Autowired
private UserService userService;
#Autowired
private MessageService messageService;
public boolean supports(Class<?> clazz) {
return User.class.equals(clazz);
}
public void validate(Object target, Errors errors) {
User user = (User)target;
if(userService.getUserByEmail(user.getEmail())==null){
errors.rejectValue("email", messageService.getMessage("app.error.nonexistentemail"));
}
}
}
I can see the controllers, services and autowired elements in the Spring elements. It seems like spring is not detecting the autowired properties in the package validator. Any ideas?
Edit: Log of the ResetPasswordValidation (Autowire fields)
12:48:50,697 DEBUG main support.DefaultListableBeanFactory:217 - Creating shared instance of singleton bean 'resetPasswordValidation'
12:48:50,697 DEBUG main support.DefaultListableBeanFactory:430 - Creating instance of bean 'resetPasswordValidation'
12:48:50,701 DEBUG main annotation.InjectionMetadata:60 - Found injected element on class [es.unican.meteo.validator.ResetPasswordValidation]: AutowiredFieldElement for private es.unican.meteo.service.UserService es.unican.meteo.validator.ResetPasswordValidation.userService
12:48:50,702 DEBUG main annotation.InjectionMetadata:60 - Found injected element on class [es.unican.meteo.validator.ResetPasswordValidation]: AutowiredFieldElement for private es.unican.meteo.service.MessageService es.unican.meteo.validator.ResetPasswordValidation.messageService
12:48:50,702 DEBUG main support.DefaultListableBeanFactory:504 - Eagerly caching bean 'resetPasswordValidation' to allow for resolving potential circular references
12:48:50,707 DEBUG main annotation.InjectionMetadata:85 - Processing injected method of bean 'resetPasswordValidation': AutowiredFieldElement for private es.unican.meteo.service.UserService es.unican.meteo.validator.ResetPasswordValidation.userService
Make sure you annotate the class so Spring picks it up as a bean. Autowiring can only occur on beans/classes managed by the DI container.
Adding #Component will cause the class to be picked up by Spring's component scanning, causing ResetPasswordValidation to become a bean. At this point, it should be eligible to have fields autowired.
#Component
public class ResetPasswordValidation implements Validator

Spring Autowire Fundamentals

I am a newbie in Spring and am trying to understand the below concept.
Assume that accountDAO is a dependency of AccountService.
Scenario 1:
<bean id="accServiceRef" class="com.service.AccountService">
<property name="accountDAO " ref="accDAORef"/>
</bean>
<bean id="accDAORef" class="com.dao.AccountDAO"/>
Scenario 2:
<bean id="accServiceRef" class="com.service.AccountService" autowire="byName"/>
<bean id="accDAORef" class="com.dao.AccountDAO"/>
In AccountService Class:
public class AccountService {
AccountDAO accountDAO;
....
....
}
In the second scenario, How is the dependency injected ? When we say it is autowired by Name , how exactly is it being done. Which name is matched while injecing the dependency?
Thanks in advance!
Use #Component and #Autowire, it's the Spring 3.0 way
#Component
public class AccountService {
#Autowired
private AccountDAO accountDAO;
/* ... */
}
Put a component scan in your app context rather than declare the beans directly.
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com"/>
</beans>
<bean id="accServiceRef" class="com.service.accountService" autowire="byName">
</bean>
<bean id="accDAORef" class="com.dao.accountDAO">
</bean>
and
public class AccountService {
AccountDAO accountDAO;
/* more stuff */
}
When spring finds the autowire property inside accServiceRef bean, it will scan the instance variables inside the AccountService class for a matching name. If any of the instance variable name matches the bean name in the xml file, that bean will be injected into the AccountService class. In this case, a match is found for accountDAO.
Hope it makes sense.

Resources