Spring’s component scanning feature automatically scans and detects the components from the classpath.
To mark a class as a spring component, all you need to do is annotate the class with @Component
.
You also need to provide <context:component-scan>
element in your context XML. This will let spring know that it needs to scan certain packages to detect the components.
In this article, we will see some examples of @Component
and the specialized annotations @Repository
, @Service
and Controller
.
This example uses the following tools and frameworks:
Dependencies
Add the following dependencies:
spring-core
spring-context
spring-beans
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javarticles.spring</groupId> <artifactId>springListExample</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> </dependencies> <properties> <spring.version>4.1.6.RELEASE</spring.version> </properties> </project>
Examples of @Component
@Component
represents a generic stereotype for any Spring-managed component. If a class is annotated with @Component
, spring can scan and detect it while building the context object.
Let’s see an example.
Class Company
is annotated with @Component
and the bean ID is provided as the value.
If you don’t provide any ID, spring will still load the component. It will assign the bean a name created by lower-casing the first character of the class and using the rest of the camel-cased name for the bean name. You can also provide your own custom bean ID resolving strategy.
Company:
package com.javarticles.spring.components; import org.springframework.stereotype.Component; @Component("dreamCompany") public class Company { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return "Company Bean"; } }
Note we have used @Autowired
annotation so that Spring can auto-wire it by type.
Employee:
package com.javarticles.spring.components; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope("prototype") public class Employee { private String name; @Autowired private Company company; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Company getCompany() { return company; } public void setCompany(Company company) { this.company = company; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString() { return "Employee Bean, " + company; } }
To allow spring to scan the classes, we need to provide a XML element <context:component-scan>
. In this element, you need to specify the package for scanning your components using attribute base-package
. Then the specified package and all its sub-packages will be scanned. If more than one packages, we need provide them comma separated.
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" 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-2.5.xsd"> <context:component-scan base-package="com.javarticles.spring.components" /> </beans>
SpringComponentScanningExample:
package com.javarticles.spring.components; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringComponentScanningExample { public static void main(String[] args) { ClassPathXmlApplicationContext context = null; try { context = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println(context.getBean("dreamCompany")); System.out.println(context.getBean("employee")); } finally { if (context != null) { context.close(); } } } }
Output:
Company Bean Employee Bean, Company Bean
Disable auto-wiring and the common annotations
By default AutowiredAnnotationBeanPostProcessor
and CommonAnnotationBeanPostProcessor
are both included implicitly when using the <component-scan>
element.
AutowiredAnnotationBeanPostProcessor
takes care of auto-wiring whereas CommonAnnotationBeanPostProcessor
takes care of the common lifecycle annotations @PostConstruct
and @PreDestroy
.
If a method is annotated with @PostConstruct
then it will be called during post bean initialization phase. If the method is annotated with @PreDestory
, it will be called before the bean is going to be removed from the container.
ClassEmployeeTax
has a @PostConstruct
and @PreDestroy
method. Since by default, annotation-config
is true, the above mentioned annotation processors will automatically be registered.
EmployeeTax:
package com.javarticles.spring.components; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Component public class EmployeeTax { @Autowired private Employee employee; @PostConstruct public void init() { System.out.println("init EmployeeTax bean"); } @PreDestroy public void cleanup() { System.out.println("cleanup EmployeeTax bean"); } public Employee getEmployee() { return employee; } }
annotationsScanContext.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" 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-2.5.xsd"> <context:component-scan base-package="com.javarticles.spring.components" /> </beans>
If you load the above context, you will notice spring automatically auto-wires the employee bean and calls the lifecycle callbacks @PostConstruct
and @PreDestroy
annotated methods.
SpringDisableAnnotationsExample:
package com.javarticles.spring.components; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringDisableAnnotationsExample { public static void main(String[] args) { ClassPathXmlApplicationContext context = null; System.out.println("Load context file with default annotation-config=true"); try { context = new ClassPathXmlApplicationContext("annotationsScanContext.xml"); } finally { if (context != null) { context.close(); } } } }
context.close()
will shutdown the context so it will automatically call the registered @PreDestroy
callback methods.
Output:
Load context file with default annotation-config=true init EmployeeTax bean cleanup EmployeeTax bean
Let’s now disable the default behavior of configuring annotations setting attribute annotations-config
to false.
disableAnnotationsScanContext.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" 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-2.5.xsd"> <context:component-scan base-package="com.javarticles.spring.components" annotation-config="false"/> </beans>
Since annotation-config
is set to false, annotations @Autowired
, @PostConstruct
and @PreDestroy
won’t get processed.
SpringDisableAnnotationsExample:
package com.javarticles.spring.components; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringDisableAnnotationsExample { public static void main(String[] args) { ClassPathXmlApplicationContext context = null; System.out.println("Load context file with default annotation-config=true"); try { context = new ClassPathXmlApplicationContext("annotationsScanContext.xml"); } finally { if (context != null) { context.close(); } } System.out.println("Load context file with default annotation-config=false"); try { context = new ClassPathXmlApplicationContext("disableAnnotationsScanContext.xml"); EmployeeTax employeeTax = (EmployeeTax) context.getBean("employeeTax"); System.out.println("is 'employee' autowired? " + (employeeTax.getEmployee() != null)); } finally { if (context != null) { context.close(); } } } }
employee
object will be null. You can also see that the lifecycle methods init()
and cleanup()
are not called.
Output:
Load context file with default annotation-config=true init EmployeeTax bean cleanup EmployeeTax bean Load context file with default annotation-config=false is 'employee' autowired? false
Specialized annotations @Repository, @Service and @Controller
@Repository
, @Service
and @Controller
serve as specializations of @Component
. They are meant for more specific use cases. We can use @Repository
annotation in the persistence layer, for example on a Data Access Object (DAO).
@Service
in service layer and @Controller
annotation on a class representing a controller.
If you use a @Component
instead of the specialized ones, spring will still go ahead and load them but the ideal way would be to use the specialized ones as it allows spring to do specialized processing based on the chosen annotation.
For example, Spring offers a solution allowing exception translation to be applied transparently through the @Repository
annotation which means irrespective of the persistence technology an exception thrown will be translated to a generic Spring’s org.springframework.dao.DataAccessException
DataAccessException hierarchy.
EmployeeDAO:
package com.javarticles.spring.repository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import com.javarticles.spring.components.Employee; @Repository public class EmployeeDAO { @Autowired private Employee employee; public Employee findEmployeeByName(String name) { employee.setName("name"); return employee; } public String toString() { return "EmployeeDao, " + employee; } }
Let’s add a Service component.
EmployeeManager:
package com.javarticles.spring.services; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.javarticles.spring.repository.EmployeeDAO; @Service public class EmployeeManager { @Autowired private EmployeeDAO employeeDAO; public String toString() { return "Employee Manager, " + employeeDAO; } }
Now we will add one controller, this should represent the web controller.
Note our example classes are all dummy, as our goal in this article is just to verify that spring loads them in the same way as it does with classes annotated with @Component
.
EmployeeDetailsController:
package com.javarticles.spring.controllers; import org.springframework.stereotype.Controller; @Controller public class EmployeeDetailsController { public String toString() { return "Employee Controller"; } }
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" 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-2.5.xsd"> <context:component-scan base-package="com.javarticles.spring.components,com.javarticles.spring.controllers,com.javarticles.spring.repository,com.javarticles.spring.services" /> </beans>
SpringComponentScanningExample:
package com.javarticles.spring.components; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringComponentScanningExample { public static void main(String[] args) { ClassPathXmlApplicationContext context = null; try { context = new ClassPathXmlApplicationContext("applicationContext.xml"); System.out.println(context.getBean("dreamCompany")); System.out.println(context.getBean("employee")); System.out.println(context.getBean("employeeManager")); System.out.println(context.getBean("employeeDAO")); System.out.println(context.getBean("employeeDetailsController")); } finally { if (context != null) { context.close(); } } } }
Output:
Company Bean Employee Bean, Company Bean Employee Manager, EmployeeDao, Employee Bean, Company Bean EmployeeDao, Employee Bean, Company Bean Employee Controller
Download the source code
This was an example about spring component scanning.