Just How Does Spring Do Its Classpath Component Scanning Magic? - No Fluff Just Stuff

Just How Does Spring Do Its Classpath Component Scanning Magic?

Posted by: Scott Leberknight on June 23, 2008

One really cool feature in Spring 2.5+ is classpath component scanning. For example, instead of manually defining and wiring up all the beans comprising your Spring-based application, you simply add a few "driver" snippets of XML to your application context configuration, and then annotate your component classes with @Component (or any specialization such as @Controller and @Service). I am calling the XML snippets a "driver" because all they do is enable a specific feature, such as classpath scanning or component autowiring:

<!-- Enable autowiring via @Autowire annotations -->
<context:annotation-config/>

<!-- Scan for components in a package (and its subpackages)  -->
<context:component-scan base-package="org.springframework.samples.petclinic.web"/>

After seeing this was pretty cool, I wanted to know how exactly they did the classpath scanning. It boils down to doing some gymnastics with class loaders and resource path matching and using the ClassLoader.getResources(String name) method which returns an Enumeration containing URLs representing classpath resources. Those resources (Java classes) are then checked to see if they contain the @Component annotation (or a specialization of it) and if so, are considered a "candidate" component. Other filtering can take place but by default those components are then defined programmatically as Spring beans. When annotation configuration is enabled, autowiring of components also takes place, so I can have Spring scan the classpath for all my web controllers, and then automatically inject dependencies such as services or data access objects. Cool!

The actual classes that perform this magic are the ClassPathScanningCandidateComponentProvider which, by default, uses a PathMatchingResourcePatternResolver to find matching classpath resources using ClassLoader.getResources(). As a quick example, you can see this in action by writing a simple script, like so:

ClassPathScanningCandidateComponentProvider provider =
    new ClassPathScanningCandidateComponentProvider(true);
String basePackage = "org/springframework/samples/petclinic";
Set<BeanDefinition> components = provider.findCandidateComponents(basePackage);
for (BeanDefinition component : components) {
    System.out.printf("Component: %s\n", component.getBeanClassName());
}

Running this code (using the PetClinic sample application shipped with Spring), you get the following output:

Component: org.springframework.samples.petclinic.hibernate.HibernateClinic
Component: org.springframework.samples.petclinic.jdbc.SimpleJdbcClinic
Component: org.springframework.samples.petclinic.jpa.EntityManagerClinic
Component: org.springframework.samples.petclinic.web.AddOwnerForm
Component: org.springframework.samples.petclinic.web.AddPetForm
Component: org.springframework.samples.petclinic.web.AddVisitForm
Component: org.springframework.samples.petclinic.web.ClinicController
Component: org.springframework.samples.petclinic.web.EditOwnerForm
Component: org.springframework.samples.petclinic.web.EditPetForm
Component: org.springframework.samples.petclinic.web.FindOwnersForm

For a more fun experiment, I tried to scan using "org" as the base package...

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at org.springframework.asm.ClassReader.a(Unknown Source)
	at org.springframework.asm.ClassReader.(Unknown Source)
	at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:76)
	at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:68)
	at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(ClassPathScanningCandidateComponentProvider.java:181)
	at org.springframework.samples.petclinic.ClassPathScannerTester.main(ClassPathScannerTester.java:17)

Ok, so you need to restrict the scan space that Spring will use or else you could run out of memory scanning every possible class in your system! Regardless, with classpath scanning of components and autowiring of dependencies, you can cut down the amount of XML in Spring-based apps a lot.

Scott Leberknight

About Scott Leberknight

Scott is Chief Architect at Near Infinity Corporation, an enterprise software development and consulting services company based in Reston, Virginia. He has been developing enterprise and web applications for 14 years professionally, and has developed applications using Java, Ruby, Groovy, and even an iPhone application with Objective-C. His main areas of interest include alternative persistence technologies, object-oriented design, system architecture, testing, and frameworks like Spring, Hibernate, and Ruby on Rails. In addition, Scott enjoys learning new languages to make himself a better and more well-rounded developer a la The Pragmatic Programmers' advice to “learn one language per year.”

Scott holds a B.S. in Engineering Science and Mechanics from Virginia Tech, and an M. Eng. in Systems Engineering from the University of Maryland. Scott speaks at the No Fluff Just Stuff Symposiums and various other conferences. In his (sparse) spare time, Scott enjoys spending time with his wife, three children, and cat. He also tries to find time to play soccer, go snowboarding, and mountain bike whenever he can.

Why Attend the NFJS Tour?

  • » Cutting-Edge Technologies
  • » Agile Practices
  • » Peer Exchange

Current Topics:

  • Languages on the JVM: Scala, Groovy, Clojure
  • Enterprise Java
  • Core Java, Java 8
  • Agility
  • Testing: Geb, Spock, Easyb
  • REST
  • NoSQL: MongoDB, Cassandra
  • Hadoop
  • Spring 4
  • Cloud
  • Automation Tools: Gradle, Git, Jenkins, Sonar
  • HTML5, CSS3, AngularJS, jQuery, Usability
  • Mobile Apps - iPhone and Android
  • More...
Learn More »