Spring의 XML 기반 구성을 사용할 때 동일한 인터페이스의 여러 구현을 쉽게 꾸며서 순서를 지정할 수 있습니다. 예를 들어 로깅 서비스는 실제 서비스를 래핑하는 트랜잭션 서비스를 래핑합니다.데코레이터 패턴과 @Inject
javax.inject
주석을 사용하여 어떻게 동일한 결과를 얻을 수 있습니까?
Spring의 XML 기반 구성을 사용할 때 동일한 인터페이스의 여러 구현을 쉽게 꾸며서 순서를 지정할 수 있습니다. 예를 들어 로깅 서비스는 실제 서비스를 래핑하는 트랜잭션 서비스를 래핑합니다.데코레이터 패턴과 @Inject
javax.inject
주석을 사용하여 어떻게 동일한 결과를 얻을 수 있습니까?
이것은 일반적으로 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));
}
}
.
주입 된 서비스와 간단한 예 :
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 {
}
@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). 생성자 매개 변수의 상용구가 있습니다.
위에 표시된 바인딩 구성이보기 흉하게 보입니다. 상용구 문제를 해결하기 위해 [decorice] (https://github.com/beluchin/decorice)를 들여다 볼 수 있습니다. – beluchin
답변 해 주셔서 감사합니다. 저는 실제로 Spring을 사용하고 있습니다. 그래서 이것은 가능하지 않습니다. 단지'javax.inject' 패키지만을 사용하려고합니다. 하지만 어쨌든 알고있는 것이 좋습니다. –