Scanning Components from the Classpath

Suppose you are asked to develop your sequence generator application using database sequences, and store the prefix and suffix of each sequence in a table. First, you create the domain class Sequence containing the id, prefix, and suffix properties.

package com.sequence;
public class Sequence {
private String id;
private String prefix;
private String suffix;
// Constructors, Getters, and Setters
}

Then you create an interface for the Data Access Object (DAO), which is responsible for accessing data from the database. The getSequence() method loads a Sequence object from the table by its ID, while the getNextValue() method retrieves the next value of a particular database sequence.

package com.sequence;
public interface SequenceDao {
public Sequence getSequence(String sequenceId);
public int getNextValue(String sequenceId);
}

In a production application, you should 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 sequence instances and values.

package com.sequence;
public class SequenceDaoImpl implements SequenceDao {
private Map<String, Sequence> sequences;
private Map<String, Integer> values;
public SequenceDaoImpl() {
sequences = new HashMap<String, Sequence>();
sequences.put("IT", new Sequence("IT", "30", "A"));
values = new HashMap<String, Integer>();
values.put("IT", 100000);
}
public Sequence getSequence(String sequenceId) {
return sequences.get(sequenceId);
}
public synchronized int getNextValue(String sequenceId) {
int value = values.get(sequenceId);
values.put(sequenceId, value + 1);
return value;
}
}

You also need a service object, acting as a façade, to provide the sequence generation service. Internally, this service object will interact with the DAO to handle the sequence generation requests. So, it requires a reference to the DAO.

package com.sequence;
public class SequenceService {
private SequenceDao sequenceDao;
public void setSequenceDao(SequenceDao sequenceDao) {
this.sequenceDao = sequenceDao;
}
public String generate(String sequenceId) {
Sequence sequence = sequenceDao.getSequence(sequenceId);
int value = sequenceDao.getNextValue(sequenceId);
return sequence.getPrefix() + value + sequence.getSuffix();
}
}

Finally, you have to configure these components in the bean configuration file to make the sequence generator application work. You can auto-wire your components to reduce the amount of configurations.

<beans …>
<bean id="sequenceService"
class="com.sequence.SequenceService"
autowire="byType" />
<bean id="sequenceDao"
class="com.sequence.SequenceDaoImpl" />
</beans>

Then you can test the preceding components with the following Main class:

package com.sequence;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");
SequenceService sequenceService =
(SequenceService) context.getBean("sequenceService");
System.out.println(sequenceService.generate("IT"));
System.out.println(sequenceService.generate("IT"));
}
}

Scanning Components Automatically

The component scanning feature provided by Spring 2.5 can automatically scan, detect, and instantiate your components from the classpath. By default, Spring is able to detect all components with a stereotype annotation. The basic annotation type that denotes a Springmanaged component is @Component. You can apply it to your SequenceDaoImpl class.

package com.sequence;
import org.springframework.stereotype.Component;
@Component
public class SequenceDaoImpl implements SequenceDao {
}

Also, you apply this stereotype annotation to the SequenceService class for Spring to detect it. In addition, you apply the @Autowired annotation to the DAO field for Spring to autowire it by type.

package com.sequence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SequenceService {
@Autowired
private SequenceDao sequenceDao;
}

With the stereotype annotations applied to your component classes, you can ask Spring to scan them by declaring a single XML element, <context:component-scan>. In this element, you need to specify the package for scanning your components. Then the specified package and all its subpackages will be scanned. You can use commas to separate multiple packages for scanning.

Note that this element will also register an AutowiredAnnotationBeanPostProcessor instance, which is able to auto-wire properties with the @Autowired annotation.

<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-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.sequence" />
</beans>

The @Component annotation is the basic stereotype for denoting components of general purposes. Actually, there are other specific stereotypes denoting components in different layers. First, the @Repository stereotype denotes a DAO component in the persistence layer.

package com.sequence;
import org.springframework.stereotype.Repository;
@Repository
public class SequenceDaoImpl implements SequenceDao {
}

Then, the @Service stereotype denotes a service component in the service layer. package com.sequence;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SequenceService {
90 CHAPTER 3 n BEAN CONFIGURATION IN SPRING
@Autowired
private SequenceDao sequenceDao;
}

There’s another component stereotype, @Controller, denoting a controller component in the presentation layer.

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 four types of filter expressions. The annotation and assignable types are for you to specify an annotation type and a class/interface for filtering. The regex and aspectj types allow you to specify a regular expression and an AspectJ pointcut expression for matching the classes.

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.sequence">
<context:include-filter type="regex"
expression="com\.apress\.springrecipes\.sequence\..*Dao.*" />
<context:include-filter type="regex"
expression="com\.apress\.springrecipes\.sequence\..*Service.*" />
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
</beans>

As you have applied include filters to detect all classes whose name contains the word Dao or Service, the SequenceDaoImpl and SequenceService 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 non-qualified class name. For example, the SequenceService class will be named as sequenceService. You can define the name for a component explicitly by specifying it in the stereotype annotation’s value.

package com.sequence;
import org.springframework.stereotype.Service;
@Service("sequenceService")
public class SequenceService {
}
package com.sequence;
import org.springframework.stereotype.Repository;
@Repository("sequenceDao")
public class SequenceDaoImpl implements SequenceDao {
}

You can develop your own naming strategy by implementing the BeanNameGenerator interface and specifying it in the name-generator attribute of the <context:component-scan> element.