装饰模式和@Inject

时间:2010-06-30 12:17:01

标签: java spring dependency-injection guice

使用基于Spring的XML配置时,可以轻松地修饰同一接口的多个实现并指定顺序。例如,日志记录服务包装了一个包装实际服务的事务服务。

如何使用javax.inject注释实现相同的目标?

3 个答案:

答案 0 :(得分:7)

您可以将@Named@Inject一起使用来指定要注入的bean。

注入服务的一个简单示例:

public class ServiceTest {

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

和相应的事务装饰器类:

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

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

这会将您的配置公开到您的代码中,因此我建议您在@Configuration类中执行装饰逻辑,并使用@Primary注释日志服务。使用这种方法,您的测试类可以看起来像这样:

public class ServiceTest {

    @Inject
    private Service service;

配置类:

@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();
    }
}

我的第二个示例没有公开有关将返回哪个实现的任何细节,但它取决于几个Spring特定的类。

您也可以将两种解决方案结合起来。例如,在装饰器上使用Spring的@Primary注释,让Spring将这个装饰器注入到给定类型的实例中。

@Service
@Primary
public class ServiceDecoratorSecuritySupport extends ServiceDecorator {
}

答案 1 :(得分:4)

这是你通常使用AOP的东西,而不是手动编写和包装实现(不是你不能这样做)。

对于使用Guice的AOP,您需要创建一个事务MethodInterceptor和一个日志记录MethodInterceptor,然后使用bindInterceptor(Matcher, Matcher, MethodInterceptor)来设置应拦截哪些类型和方法。第一个Matcher匹配要拦截的类型,第二个匹配拦截方法。可以是Matchers.any(),匹配类型或方法上的特定注释(例如@Transactional)或任何您想要的内容。然后截取并自动处理匹配方法。装饰模式,基本上没有太多样板。

要手动完成,一种方法是:

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));
  }
}

答案 2 :(得分:2)

@Target(PARAMETER)
@Retention(RUNTIME)
@BindingAnnotation
public @interface Decorate {
  Class<?> value();
}

/* see com.google.inject.name.NamedImpl for rest of  
    the methods DecorateImpl must implement */
public class DecorateImpl implements Decorate, Serializable {

  private final Class<?> value;

  private DecorateImpl(Class<?> val) {
    value = val;
  }

  public static Decorate get(Class<?> clazz) {
    return new DecorateImpl(clazz);
  }

  public Class<?> value() {
    return value;
  }
  ...
  ...
}

以下是如何使用它:

public interface ApService {
  String foo(String s);
}

public class ApImpl implements ApService {

  private final String name;

  @Inject
  public ApImpl(@Named("ApImpl.name") String name) {
    this.name = name;
  }

  @Override
  public String foo(String s) {
    return name + ":" + s;
  }
}

第一个装饰者:

public class ApDecorator implements ApService {

  private final ApService dcrtd;
  private final String name;

  @Inject
  public ApDecorator(@Decorate(ApDecorator.class) ApService dcrtd,
      @Named("ApDecorator.name") String name) {
    this.dcrtd = dcrtd;
    this.name = name;
  }

  public String foo(String s) {
    return name + ":" + s + ":"+dcrtd.foo(s);
  }
}

第二个装饰者:

public class D2 implements ApService {

  private final ApService dcrt;

  @Inject
  public D2(@Decorate(D2.class) ApService dcrt) {
    this.dcrt = dcrt;
  }

  @Override
  public String foo(String s) {
    return "D2:" + s + ":" + dcrt.foo(s);
  }
}

public class DecoratingTest {

  @Test
  public void test_decorating_provider() throws Exception {
    Injector inj = Guice.createInjector(new DecoratingModule());
    ApService mi = inj.getInstance(ApService.class);
    assertTrue(mi.foo("z").matches("D2:z:D:z:I:z"));
  }
}

模块:

class DecoratingModule extends AbstractModule {

  @Override
  protected void configure() {
    bindConstant().annotatedWith(Names.named("ApImpl.name")).to("I");
    bindConstant().annotatedWith(Names.named("ApDecorator.name")).to("D");
    bind(ApService.class).
      annotatedWith(DecorateImpl.get(ApDecorator.class)).
      to(AnImpl.class);
    bind(ApService.class).
      annotatedWith(DecorateImpl.get(D2.class)).
      to(ApDecorator.class);
    bind(ApService.class).to(D2.class);
  }
}

如果绑定配置看起来很丑,你可以创建看起来不错的Builder / DSL 缺点是(与手动链构建相比),您不能将相同的模块链接两次(即 D2-> D2-> D1-> Impl )和构造函数参数中的样板。