装饰模式和@Inject

Rob*_*anu 9 java spring dependency-injection guice

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

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

Esp*_*pen 7

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

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

public class ServiceTest {

    @Inject
    @Named("transactionDecorator")
    private Service service;
}
Run Code Online (Sandbox Code Playgroud)

和相应的事务装饰器类:

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

    @Inject
    @Named("serviceBean")
    public ServiceDecoratorTransactionSupport(Service service) {
        super(service);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class ServiceTest {

    @Inject
    private Service service;
Run Code Online (Sandbox Code Playgroud)

和配置类:

@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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

@Service
@Primary
public class ServiceDecoratorSecuritySupport extends ServiceDecorator {
}
Run Code Online (Sandbox Code Playgroud)


Col*_*inD 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));
  }
}
Run Code Online (Sandbox Code Playgroud)