2010-06-30 91 views
9

當使用基於Spring的XML配置時,很容易裝飾同一接口的多個實現並指定順序。例如,日誌服務包裝了一個包裝實際服務的事務服務。裝飾模式和@Inject

如何使用javax.inject註釋實現相同?

回答

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

感謝您的回答。我實際上使用Spring,所以對我來說這是不可能的,我只想使用'javax.inject'包。但無論如何,這很好。 – 2010-06-30 14:52:35

7

可以一起使用@Named@Inject指定要注入哪個Bean。

一個簡單的例子有注射服務:

​​

而且相應的事務裝飾類:

@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 { 
} 
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)和構造函數params中的樣板。

+0

上面顯示的綁定配置確實看起來很醜。你可以看看[decorice](https://github.com/beluchin/decorice)來解決模板問題。 – beluchin 2015-05-29 21:37:24