首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >装饰器模式和@Inject

装饰器模式和@Inject
EN

Stack Overflow用户
提问于 2010-06-30 20:17:01
回答 3查看 9.8K关注 0票数 9

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

如何使用javax.inject注解实现相同的功能?

EN

回答 3

Stack Overflow用户

发布于 2010-07-01 00:56:00

您可以结合使用@Named@Inject来指定要注入的bean。

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

代码语言:javascript
复制
public class ServiceTest {

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

和对应的事务装饰器类:

代码语言:javascript
复制
@org.springframework.stereotype.Service("transactionDecorator")
public class ServiceDecoratorTransactionSupport extends ServiceDecorator {

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

这会将您的配置公开到您的代码中,因此我建议在@Configuration类中进行修饰逻辑,并使用@Primary注释日志记录服务。使用这种方法,您的测试类可能如下所示:

代码语言:javascript
复制
public class ServiceTest {

    @Inject
    private Service service;

和configuration类:

代码语言:javascript
复制
@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将此装饰器注入到给定类型的实例中。

代码语言:javascript
复制
@Service
@Primary
public class ServiceDecoratorSecuritySupport extends ServiceDecorator {
}
票数 7
EN

Stack Overflow用户

发布于 2010-06-30 22:34:51

这是您通常使用AOP做的事情,而不是手动编写和包装实现(并不是说您不能这样做)。

对于使用Guice的面向方面编程,您需要创建一个事务性MethodInterceptor和一个日志记录MethodInterceptor,然后使用bindInterceptor(Matcher, Matcher, MethodInterceptor)设置应该拦截哪些类型和方法。第一个Matcher匹配要拦截的类型,第二个匹配要拦截的方法。两者都可以是Matchers.any(),匹配类型或方法上的特定注释(比如@Transactional),或者您想要的任何内容。然后自动截获和处理匹配方法。基本上,装饰器模式的样板文件要少得多。

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

代码语言:javascript
复制
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));
  }
}
票数 4
EN

Stack Overflow用户

发布于 2011-06-01 15:26:54

代码语言:javascript
复制
@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;
  }
  ...
  ...
}

下面是它的使用方法:

代码语言:javascript
复制
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;
  }
}

第一个装饰者:

代码语言:javascript
复制
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);
  }
}

第二个装饰者:

代码语言:javascript
复制
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"));
  }
}

模块:

代码语言:javascript
复制
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)和样板。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3149137

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档