Spring componentscan custom annotation

Everyone seems to think annotations are cool and want to use them now days. A lot has been written about the pros and cons of using annotations and personally I think they should be used with caution. Mixing annotation domains to a point of total confusion, spreading configuration related meta-data across large code bases to a point where refactoring becomes very hard are only some of the risks. Nevertheless, annotations are cool.

I wanted to add annotation functionality to some “aspect” of our code base. An aspect being an interface Foo and multiple implementations of Foo in various artifacts (e.g., FooA from project A, FooB from project B and so on).

1
2
3
public interface Foo{
  void bar();
}
1
2
3
4
5
6
public class FooA implements Foo{
  @Override
  public void bar(){
    System.out.println("I am number 6!");
  }
}

First thing I created a Fooish type-level (class-level) annotation

1
2
3
4
5
6
7
8
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Fooish {
  boolean cool() default true;
  String[] tags() default { "all" };
}

Next I annotated my classes with my new Fooish annotation:

1
2
3
4
5
6
7
@Fooish(cool=false, tags= {"sixfoo"})
public class FooA implements Foo{
  @Override
  public void bar(){
    System.out.println("I am number 6!");
  }
}

Meanwhile, in another Foo across the galaxy from A:

1
2
3
4
5
6
7
@Fooish(cool=true, tags= {"freefoo"})
public class FooB implements Foo{
  @Override
  public void bar(){
    System.out.println("I am not a number, I am a free man!");
  }
}

I am now ready to do some classpath scanning for my Foos. At first I went a looked the reflections library which I have to say is quite cool.

I popped the maven repository and dependencies:

1
2
3
4
5
<repository>
    <id>reflections-repo</id>
    <name>Reflections Maven2 Repository</name>
</repository>
01
02
03
04
05
06
07
08
09
10
<dependency>
  <groupId>org.reflections</groupId>
  <artifactId>reflections</artifactId>
  <version>0.9.5</version>
</dependency>
<dependency>
  <groupId>com.google.collections</groupId>
  <artifactId>google-collections</artifactId>
  <version>1.0</version>
</dependency>

And now I am ready to start scanning for annotations:

01
02
03
04
05
06
07
08
09
10
11
12
final StopWatch sw = new StopWatch();
sw.start();
final Reflections reflections = new Reflections("org.projectx", new TypeAnnotationsScanner());
Set<Class<?>> theFoos = reflections.getTypesAnnotatedWith(Fooish.class);
sw.stop();
System.out.println("Classpath scanning took: " + sw.getTime() + "ms");
for (final Class<?> theFoo : theFoos) {
final Fooish annotation = theFoo.getAnnotation(Fooish.class);
  if (Arrays.asList(annotation.tags()).contains("freefoo")) {
     System.out.println("This foo "+ theFoo.getClass().getName() + ", is a free foo");
  }
}

The reflections API has much more than is shown here, the output of the scan showed:

1
Classpath scanning took: 1782ms

Not bad for quite a large code base, but still, incurring a 2 seconds penalty for service load time is severe a production environment where services might be candidate to frequent restarts. Continuous deployment requires it to be snappy. A side note here, you should never try to do this kind of scanning during runtime, only load time. Reflection-based operations are costly in Java.

Next I suddenly remembered that Spring does this kind of work when you do stuff like <mvc:annotation-driven /> and <context:component-scan /> in you bean configuration file. There’s gotta be a way to hook up into that process use it for my own good. Turns out there is.

Spring allows you to load your annotated classes into their container if you also annotate them with the @Component type-level annotation. So my Foos need another annotation:

1
2
3
4
5
6
7
8
@Fooish(cool=true, tags= {"freefoo"})
@Component
public class FooB implements Foo{
  @Override
  public void bar(){
    System.out.println("I am not a number, I am a free man!");
  }
}

To load all my Foos into the Spring application context I added the following to my Spring configuration file:

1
2
3
<context:component-scan base-package="org.projectx">
  <context:include-filter type="annotation" expression="com.projectX.annotation.Fooish" />
</context:component-scan>

You can have other component scanners in the Spring configuration but this one is dedicated only to the classes annotated with @Component and @Fooish.

Now I want a Fooish handler which handles all instances of Fooish beans loaded to the context, I can easily do it with by implementing the ApplicationContextAware interface, then calling the getBeansWithAnnotation method of the ApplicationContext.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class MyFooishHandler implements ApplicationContextAware, InitializingBean {
  private ApplicationContext applicationContext;
  @Override
  public void afterPropertiesSet() throws Exception {
    final Map<String, Object> myFoos = applicationContext.getBeansWithAnnotation(Fooish.class);
    for (final Object myFoo : myFoos.values()) {
      final Class<? extends Object> fooClass = myFoo.getClass();
      final Fooish annotation = fooClass.getAnnotation(Fooish.class);
      System.out.println("Found foo class: " + fooClass + ", with tags: " + annotation.tags());
    }
  }
  @Override
  public void setApplicationContext(final ApplicationContext applicationContext)
      throws BeansException {
    this.applicationContext = applicationContext;
  }
}

I am sure there’s some overhead here to but the up side is once I annotate my Foos with @Component I can also take advantage of other features of the Spring container and this method doesn’t require additional dependencies on the classpath assuming Spring’s already there.

From here

The above article was written in 2010 and now if we need to use java config rather than the xml base config. Here is an example with exclude filter which also apply to the include filter we use above. Other than annotation, The FilterType could also be Regex/AspectJ/Custom etc.. according to the spring doc.

And here is a good discussion on design patterns used in Web app

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s