views:

75

answers:

2

When using Spring's based XML configuration, it's easy to decorate multiple implementations of the same interface and specify the order. For instance, a logging service wraps a transactional service which wraps the actual service.

How can I achieve the same using the javax.inject annotations?

+2  A: 

This is the sort of thing you typically use AOP for, rather than writing and wrapping implementations manually (not that you can't do that).

For AOP with Guice, you'd want to create a transactional MethodInterceptor and a logging MethodInterceptor, then use bindInterceptor(Matcher, Matcher, MethodInterceptor) to set which types and methods should be intercepted. The first Matcher matches types to intercept, the second matches methods to intercept. Either can be Matchers.any(), match a specific annotation on a type or method (@Transactional, say) or whatever you want. Matching methods are then intercepted and handled automatically. Decorator pattern with a lot less boilerplate, basically.

To do it manually, one way would be:

class ServiceModule extends PrivateModule {
  @Override protected void configure() {
    bind(Service.class).annotatedWith(Real.class).to(RealService.class);
  }

  @Provides @Exposed
  protected Service provideService(@Real Service service) {
    return new LoggingService(new TransactionalService(service));
  }
}
ColinD
Thanks for the answer. I'm actually using Spring, so this isn't possible for me, I was looking to use just the `javax.inject` package. But it's good to know anyway.
Robert Munteanu
+3  A: 

You can use @Named together with @Inject to specify which bean to inject.

A simple example with an injected service:

public class ServiceTest {

    @Inject
    @Named("transactionDecorator")
    private Service service;
}

And the corresponding transaction decorator class:

@org.springframework.stereotype.Service("transactionDecorator")
public class ServiceDecoratorTransactionSupport extends ServiceDecorator {

    @Inject
    @Named("serviceBean")
    public ServiceDecoratorTransactionSupport(Service service) {
        super(service);
    }
}

This exposes your configuration into your code, so I would recommend doing the decorating logic in a @Configuration class and annotate for example the logging service with @Primary. With this approach your test class can look something like this:

public class ServiceTest {

    @Inject
    private Service service;

And the configuration class:

@Configuration
public class DecoratorConfig {

    @Bean
    @Primary
    public ServiceDecorator serviceDecoratorSecurity() {
        return new ServiceDecoratorSecuritySupport(
                  serviceDecoratorTransactionSupport());
    }

    @Bean
    public ServiceDecorator serviceDecoratorTransactionSupport() {
        return new ServiceDecoratorTransactionSupport(serviceBean());
    }

    @Bean
    public Service serviceBean() {
        return new ServiceImpl(serviceRepositoryEverythingOkayStub());
    }

    @Bean
    public ServiceRepository serviceRepositoryEverythingOkayStub() {
        return new ServiceRepositoryEverythingOkStub();
    }
}

My second example doesn't expose any details about which implementation that will be returned, but it depends on several Spring specific classes.

You can also combine the two solutions. For example use Spring's @Primary annotation on a decorator and let Spring inject this decorator into the instance of the given type.

@Service
@Primary
public class ServiceDecoratorSecuritySupport extends ServiceDecorator {
}
Espen