使用基于Spring的XML配置时,可以轻松地修饰同一接口的多个实现并指定顺序。例如,日志记录服务包装了一个包装实际服务的事务服务。
如何使用javax.inject
注释实现相同的目标?
答案 0 :(得分:7)
您可以将@Named
与@Inject
一起使用来指定要注入的bean。
注入服务的一个简单示例:
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);
}
}
这会将您的配置公开到您的代码中,因此我建议您在@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 {
}
答案 1 :(得分: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));
}
}
答案 2 :(得分: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 )和构造函数参数中的样板。