2010-06-30 10 views
9

Podczas korzystania z konfiguracji XML opartej na języku Spring, można łatwo udekorować wiele implementacji tego samego interfejsu i określić kolejność. Na przykład usługa rejestrowania opakowuje usługę transakcyjną, która otacza rzeczywistą usługę.Wzór dekoratora i @Inject

Jak mogę to osiągnąć, używając adnotacji javax.inject?

Odpowiedz

4

Jest to typ, do którego zazwyczaj używasz AOP, zamiast ręcznie pisać i pakować implementacje (nie, że nie możesz tego zrobić).

Dla AOP z Guice, że chcesz stworzyć transakcyjnej MethodInterceptor i rejestrowanie MethodInterceptor, a następnie użyć bindInterceptor(Matcher, Matcher, MethodInterceptor) ustawić, jakie typy i metody powinny zostać przechwycone. Pierwszy Matcher dopasowuje typy do przechwycenia, drugi dopasowuje metody do przechwycenia. Może to być Matchers.any(), dopasować określoną adnotację do typu lub metody (@Transactional, powiedzmy) lub cokolwiek chcesz. Pasujące metody są następnie przechwytywane i obsługiwane automatycznie. Wzór dekoratora z dużo mniejszą podstawą, w zasadzie.

Aby to zrobić ręcznie, jednym ze sposobów byłoby:

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

Dzięki za odpowiedź. Używam Springa, więc nie jest to możliwe dla mnie, szukałem tylko pakietu 'javax.inject'. Ale i tak warto to wiedzieć. –

7

Można użyć @Named wraz z @Inject aby określić, które fasola wstrzyknąć.

Prosty przykład z wtryskiwanego usługi:

public class ServiceTest { 

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

a odpowiadający klasie dekorator transakcji:

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

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

Naraża konfigurację w kodzie, więc polecam zrobić logikę zdobienia w klasa @Configuration i zanotuj na przykład usługę rejestrowania z @Primary. Dzięki takiemu podejściu klasa test może wyglądać następująco:

public class ServiceTest { 

    @Inject 
    private Service service; 

a klasa konfiguracji:

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

Mój drugi przykład nie wystawiać żadnych szczegółów, o których realizacja które będą zwracane, ale zależy od kilku klas wiosennych.

Można również łączyć oba rozwiązania. Na przykład użyj adnotacji Spring na @Primary na dekoratorze i pozwól Springowi wstrzyknąć ten dekorator do instancji danego typu.

@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; 
    } 
    ... 
    ... 
} 

Oto jak go używać:

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

Pierwszy dekorator:

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

drugie dekorator:

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

Moduł:

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

Jeśli konfiguracja Wiązania wygląda brzydko, można utworzyć Konstruktor/DSL, który wygląda ładnie.
Wadą jest to, że (w porównaniu z ręcznym budowaniem łańcucha) nie można dwukrotnie łączyć tego samego modułu (tj. D2-> D2-> D1-> Impl) i zestawu znaków w parametrach konstruktora.

+0

konfiguracja wiązania pokazana powyżej wygląda brzydko. Możesz zajrzeć do [decorice] (https://github.com/beluchin/decorice), aby rozwiązać problem bojlerów. – beluchin