2010-06-30 3 views
9

Spring의 XML 기반 구성을 사용할 때 동일한 인터페이스의 여러 구현을 쉽게 꾸며서 순서를 지정할 수 있습니다. 예를 들어 로깅 서비스는 실제 서비스를 래핑하는 트랜잭션 서비스를 래핑합니다.데코레이터 패턴과 @Inject

javax.inject 주석을 사용하여 어떻게 동일한 결과를 얻을 수 있습니까?

답변

4

이것은 일반적으로 AOP를 사용하기 때문에 구현을 수동으로 작성하고 배치하는 것이 아니라 할 수 있습니다.

Guice가 포함 된 AOP의 경우 트랜잭션 MethodInterceptor과 로깅 MethodInterceptor을 만든 다음 bindInterceptor(Matcher, Matcher, MethodInterceptor)을 사용하여 가로채는 유형과 메소드를 설정해야합니다. 첫 번째 Matcher은 가로 채기 유형과 일치하고, 두 번째는 가로 채기 방법과 일치합니다. 어느 하나가 Matchers.any() 일 수 있으며, 유형이나 방법 (@Transactional)에 대한 특정 주석 또는 원하는 것을 매치 할 수 있습니다. 일치하는 메소드는 인터셉트되어 자동으로 처리됩니다. 기본적으로 훨씬 적은 상용구가있는 데코레이터 패턴입니다. 수동으로 그것을 할

는 한 가지 방법은 다음과 같습니다 당신은 주입되는 콩 지정 @Inject과 함께 @Named을 사용할 수 있습니다

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' 패키지만을 사용하려고합니다. 하지만 어쨌든 알고있는 것이 좋습니다. –

7

.

주입 된 서비스와 간단한 예 :

public class ServiceTest { 

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

그리고 해당 트랜잭션 데코레이터 클래스 : 이것은 당신의 코드로 구성을 노출

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

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

, 그래서 장식 로직을하는 것이 좋습니다 예를 들어 @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); 
    } 
} 

바인딩 구성이보기 싫다면 멋진 모양의 작성기/DSL을 만들 수 있습니다.
단점은 (수동 체인 구축과 비교하여) 동일한 모듈을 두 번 연결할 수 없다는 것입니다 (예 : D2-> D2-> D1-> Impl). 생성자 매개 변수의 상용구가 있습니다.

+0

위에 표시된 바인딩 구성이보기 흉하게 보입니다. 상용구 문제를 해결하기 위해 [decorice] (https://github.com/beluchin/decorice)를 들여다 볼 수 있습니다. – beluchin