In my previous articles I have explained about the some of the popular annotations in Spring @Required, @Autowired and @Qualifier. You could note from these posts that we declared all the beans or components in XML bean configuration file; this helps Spring container detect and register beans or components. In this post I shall discuss about another feature of Spring i.e.Component Scanning. This means Spring is able to auto scan, detect and instantiate beans from pre-defined project package, hence saving us from tedious beans/component declaration in XML file.
also read: follow us on @twitter and @facebook
- Spring Tutorials ( Collection for Spring reference tutorials)
- Spring Framework Interview Questions
- Introduction to Spring LDAP
- How to write Custom Spring Callback Methods?
Stereotype Annotations
The basic annotation denoting a Spring-managed component is @Component (Denotes a auto scan component.). Other more particular stereotypes include
- @Repository: Denotes DAO component in the persistence layer.
- @Service: Denotes a Service component in the business layer.
- @Controller: Denotes a controller component in the presentation layer.
If you look at the source code for each of these components (org.springframework.stereotype.Repository, org.springframework.stereotype.Service, org.springframework.stereotype.Controller), they internally use the @Component annotation. Though you can use only @Component annotation throughout your code, this would not be considered as a good practise. Hence using @Repository, @Service, @Controller annotations helps us seperate the persistence, business and presentation layers.
Example for Auto Scan Components
The following example demonstrates the use of all the above annotations mentioned. Let us have working Eclipse IDE in place and follow the following steps to create a Spring application:
- Create a project: Create a project with a name SpringClassPathScanningExample and create a package com.javabeat under the src directory in the created project.
- Add Libraries: Add required Spring libraries using Add External JARs option as explained in the article Customizing callback methods.
- Create source files: Create Java classes Product, interface ProductDao, implementation ProductDaoImpl, ProductService and MainApp under the com.javabeat package.
- Create configuration file: Create XML based configuration file bean.xml under src directory.
Contents of Product.java are:
package com.javabeat; public class Product { String name; public Product(String name) { super(); this.name = name; } public String getName() { return "Product name is:" + name; } public void setName(String name) { this.name = name; } }
This class contains only name field and its respective getter and setter methods.
ProductDao is an interface for the Data Access Object (DAO), which is responsible for accessing data from the database. This has a single method getProduct(), which Product object from the table by its ID. Contents of ProductDao.java are:
package com.javabeat; public interface ProductDao { public Product getProduct(String id); }
In a production application, we need to implement this DAO interface using a data-access technology such as JDBC or object/relational mapping. But for testing purposes, let’s use maps to store the Product instances as shown below. Contents of ProductDaoImpl.java are:
package com.javabeat; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Component; @Component public class ProductDaoImpl implements ProductDao { private Map<String, Product> products; public ProductDaoImpl() { products = new HashMap<String, Product>(); products.put("P1", new Product("Product1")); products.put("P2", new Product("Product2")); products.put("P3", new Product("Product3")); } public Product getProduct(String id) { return products.get(id); } }
The component scanning feature as explained above, can automatically scan, detect, and instantiate your components from the classpath. By default, Spring can detect all components with a stereotype annotation. We can apply basic annotation @Component to ProductDaoImpl class as shown above.
We also need a service object, acting as a facade, to provide the sequence generation service. Internally, this service object will interact with the DAO to handle the Product generation requests. So it requires a reference to the DAO as in the service class below. Contents of ProductService.java are:
package com.javabeat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class ProductService { @Autowired private ProductDao productDao; public Product getProductDetail(String productId) { Product product = productDao.getProduct(productId); return product; } }
We will also apply @Component stereotype annotation to the ProductService class for Spring to detect it. In addition, we have applied the @Autowired annotation to the DAO field for Spring to auto-wire it by type. Note that because we’re using the annotation on a field, you don’t need a setter method here.
Now that we have applied the stereotype annotations to our component classes, we ask Spring to scan them by declaring a single XML element: . In this element, you need to specify the package for scanning your components (You can use commas to separate multiple packages for scanning.). Then the specified package and all its sub packages will be scanned. Spring will give 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. In our case the base package name is com.javabeat. Contents of bean.xml are:
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:component-scan base-package="com.javabeat"/> </beans>
Contents of MainApp are as below:
package com.javabeat; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( new String[] { "bean.xml" }); ProductService productService = (ProductService) context.getBean("productService"); System.out.println(productService.getProductDetail("P1").getName()); System.out.println(productService.getProductDetail("P2").getName()); System.out.println(productService.getProductDetail("P3").getName()); } }
Here you can note the statement:
ProductService productService = (ProductService) context.getBean("productService");
This element will also register an AutowiredAnnotationBeanPostProcessor instance that can auto-wire properties with the @Autowired annotation.
Execute the code
The final step is run the application. Once all the above code is ready execute and the below output appears on the console:
Product name is:Product1 Product name is:Product2 Product name is:Product3
As mentioned in section Stereotype Annotations, @Component is a basic stereotype for denoting components of general purposes. The other types @Repository and @Service annotations can be used for our above example as shown below, without affecting its functionality and result. @Repository stereotype denotes a DAO component in the persistence layer, hence we will apply it to our ProductDaoImpl as shown below:
package com.javabeat; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Repository; @Repository public class ProductDaoImpl implements ProductDao { private Map<String, Product> products; public ProductDaoImpl() { products = new HashMap<String, Product>(); products.put("P1", new Product("Product1")); products.put("P2", new Product("Product2")); products.put("P3", new Product("Product3")); } public Product getProduct(String id) { return products.get(id); } }
@Service stereotype denotes a service component in the service layer. Hence it can be applied as below:
package com.javabeat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class ProductService { @Autowired private ProductDao productDao; public Product getProductDetail(String productId) { Product product = productDao.getProduct(productId); return product; } }
Filtering Components to Scan
By default, Spring will detect all classes annotated with @Component, @Repository, @Service, @Controller,or your custom annotation type that is itself annotated with @Component. You can customize the scan by applying one or more include/exclude filters.
Spring supports five types of filter expressions.
- annotation: You can specify an annotation type for filtering.
- assignable: You can specify a class/interface for filtering.
- aspectj: Allow you to specify an AspectJ pointcut expression for matching the classes.
- regex: Allows you to specify a regular expression for matching the classes.
- custom: A custom implementation of the org.springframework.core.type .TypeFilter interface.
You can disable the default filters with the use-default-filters attribute.
For example, the following component scan includes all classes whose name contains the word Dao or Service, and excludes the classes with the @Controller annotation:
<beans ...> <context:component-scan base-package="com.javabeat"> <context:include-filter type="regex" expression="com\.javabeat\..*Dao.*" /> <context:include-filter type="regex" expression="com\.javabeat\..*Service.*" /> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> </beans>
Because you have applied include filters to detect all classes whose name contains the word Dao or Service, the ProductDaoImpl and ProductService components can be auto-detected even without a stereotype annotation.
Naming Detected Components
By default, Spring will name the detected components by lowercasing the first character of the nonqualified class name. For example, the ProductService class will be named as productService. You can define the name for a component explicitly by specifying it in the stereotype annotation’s value as shown below:
package com.javabeat; ....... import org.springframework.stereotype.Service; @Service("productService") public class ProductService { @Autowired private ProductDao productDao; ......... ......... } } }
package com.javabeat; .......... import org.springframework.stereotype.Repository; @Repository("productDao") public class ProductDaoImpl implements ProductDao { ........ ........ }
We can develop our own naming strategy by implementing the BeanNameGenerator interface and specifying it in the name-generator attribute of the element.
Providing Scope for Auto detected Components
By default most common scope for auto detected components is singleton. However, sometimes you need other scopes, which Spring 2.5 provides with a new @Scope annotation. We simply need to provide the name of the scope within the annotation as shown below:
package com.javabeat; ....... import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; @Scope("prototype") @Repository("productDao") public class ProductDaoImpl implements ProductDao { private Map<String, Product> products; ...... ..... } }
Summary
In this post we saw how Spring auto detects the components from classpath, hence relieving us of the hassles of XML configurations.
By default, it can detect all components with particular stereotype annotations. But you can further include or exclude your components with filters. You can also provide scopes for the auto detected components. Component scanning is a powerful feature that can reduce the amount of configurations. If you are interested in receiving the future articles, please subscribe here. also read: follow us on @twitter and @facebook