0

我们一直在 AWS Lambdas 中使用 Guice 进行 DI,但现在正在转向 Spring Boot 和长期运行的服务。

我们在 Guice 中将功能切换作为动态代理,但需要在 Spring 中实现。

假设我们有一个SomeFeature接口和两个实现DisabledImplementation,并且EnabledImplementation.

我可以通过标记DisabledImplementationwith@Component("some.feature.disabled")EnabledImplementationwith@Component("some.feature.enabled")然后编写一个这样的实现来非常接近:

@Primary
@Component
public class FlippingFeature implements SomeFeature {

    private final SomeFeature enabled;
    private final SomeFeature disabled;
    private final FeatureFlip featureFlip;

    @Inject
    public FlippingFeature(@Named("some.feature.enabled") SomeFeature enabled,
                           @Named("some.feature.disabled") SomeFeature disabled,
                           FeatureFlip featureFlip) {
        this.enabled = enabled;
        this.disabled = disabled;
        this.featureFlip = featureFlip;
    }

    @Override
    public String foo() {
        return featureFlip.isEnabled("some.feature") ? enabled.foo() : disabled.foo();
    }
}

但我宁愿根本不编写FlippingFeature课程,而是使用隐藏的动态代理来完成。我可以用自定义BeanFactoryPostProcessor或其他东西来做到这一点吗?

4

1 回答 1

-1

我现在有一个相当不错的解决方案。

@Qualifier
@Retention(RUNTIME)
// tag the disabled feature implementation w/ this annotation
public @interface Disabled {}

@Qualifier
@Retention(RUNTIME)
// tag the enabled feature implementation w/ this annotation
public @interface Enabled {}

@Target(TYPE)
@Retention(RUNTIME)
// tag the feature interface w/ this annotation
public @interface Feature {
    String value();
}

// create a concrete implementation of this class for each feature interface and annotate w/ @Primary
// note the use of @Enabled and @Disabled injection qualifiers
public abstract class FeatureProxyFactoryBean<T> implements FactoryBean<T> {

    private final Class<T> type;
    private FeatureFlag featureFlag;
    protected T enabled;
    protected T disabled;

    protected FeatureProxyFactoryBean(Class<T> type) {
        this.type = type;
    }

    @Autowired
    public void setFeatureFlag(FeatureFlag featureFlag) {
        this.featureFlag = featureFlag;
    }

    @Autowired
    public void setEnabled(@Enabled T enabled) {
        this.enabled = enabled;
    }

    @Autowired
    public void setDisabled(@Disabled T disabled) {
        this.disabled = disabled;
    }

    @Override
    public T getObject() {
        Feature feature = type.getAnnotation(Feature.class);
        if (feature == null) {
            throw new IllegalArgumentException(type.getName() + " must be annotated with @Feature");
        }
        String key = feature.value();

        ClassLoader classLoader = FeatureProxyFactoryBean.class.getClassLoader();
        Class<?>[] interfaces = {type};
        return (T) Proxy.newProxyInstance(classLoader, interfaces,
                (proxy1, method, args) -> featureFlag.isEnabled(key) ?
                        method.invoke(enabled, args) :
                        method.invoke(disabled, args));
    }

    @Override
    public Class<T> getObjectType() {
        return type;
    }
}

// test classes

@Feature("test_key")
public interface SomeFeature {
    String foo();
}

@Disabled
@Component
public class DisabledFeature implements SomeFeature {
    @Override
    public String foo() {
        return "disabled";
    }
}

@Enabled
@Component
public class EnabledFeature implements SomeFeature {
    @Override
    public String foo() {
        return "enabled";
    }
}

@Primary
@Component
public class SomeFeatureProxyFactoryBean extends FeatureProxyFactoryBean<SomeFeature> {
    public SomeFeatureProxyFactoryBean() {
        super(SomeFeature.class);
    }
}

然后在需要的地方注入,由于注释@Inject SomeFeature someFeature,它将获得代理实例。@Primary

现在我们可以在 Launchdarkly 中打开和关闭该功能,它(几乎)会立即反映在所有正在运行的实例中,而无需重新启动或重新初始化 Spring 上下文。

于 2018-04-11T14:49:45.493 回答