2010-06-30 158 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