在使用Spring的基于XML的配置时,很容易修饰同一接口的多个实现并指定顺序。例如,日志记录服务包装了一个事务服务,该事务服务包装了实际的服务。
如何使用javax.inject注解实现相同的功能?
发布于 2010-07-01 00:56:00
您可以结合使用@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类:
@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 {
}发布于 2010-06-30 22:34:51
这是您通常使用AOP做的事情,而不是手动编写和包装实现(并不是说您不能这样做)。
对于使用Guice的面向方面编程,您需要创建一个事务性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));
}
}发布于 2011-06-01 15:26:54
@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)和样板。
https://stackoverflow.com/questions/3149137
复制相似问题