18 October 2011

Spring and JSR 330 scopes

Spring 3 added support for JSR 330 configuration, so you can use @Named and @Inject instead of @Component and @Autowired. Unfortunately Spring's default scoping is not compatible with JSR 330. Here is how it works:

Default behavior

By default Spring uses it's default scoping behaviour on JSR 330 configured beans like below.

@Named
public class GreetingService {
}

This means, that GreetingService will become a bean with singleton scope.

JSR 330 scopes

In difference to Spring JSR 330 defines, that the default scope of beans is prototype, which means a new object is created everytime it will be injected somewhere. If you want a different scoping you can create your own annotation for this like described here. JSR 330 already comes with a predifined scope annotation for singletons. To make Spring behave as specified in JSR 330, you can use a different scope resolver like below.

<context:component-scan base-package="my.package"
  scope-resolver="org.springframework.context.annotation.Jsr330ScopeMetadataResolver" />

Using this additional configuration, will make the above GreetingService bean prototype scoped. To make it a singleton bean again, you will need to use the singleton scope annotation.

@Named
@Singleton
public class GreetingService {
}

Custom scopes for JSR 330

Spring comes with further scopes like request or session. JSR 330 does not support them out of the box. You will have to create your own annotations for this. Let's look, how to do this for the request scope.

  1. Create your own scope annotation:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Request {
}
  1. Extend Jsr330ScopeMetadataResolver to map your annotation on Spring's scope:
public class CustomScopeMetadataResolver extends Jsr330ScopeMetadataResolver {

  public Jsr330SpringScopeMetadataResolver() {
    registerScope(Request.class.getName(), WebApplicationContext.SCOPE_REQUEST);
  }

}
  1. Use your custom resolver in your Spring configuration:
<context:component-scan base-package="my.package"
  scope-resolver="my.resolver.package.CustomScopeMetadataResolver" />

The above sample will make all beans, that are annotated with your own @Request annotation request scoped.