Spring代理根据注释和运行时值选择实现

时间:2014-08-13 16:02:11

标签: java spring proxy scope

我想为组件注入接口的代理实现,然后让spring根据运行时属性(以及实现类中的注释的值)选择正确的实现。所以我的组件不必关心选择正确的组件。

有点像范围。但我认为范围仅用于处理同一实现类的不同实例。我错了吗?

我希望这可以为任意接口运行,而无需为每个新服务创建服务定位器或其他构造。

这是一个例子。

假设我有一个定义服务的接口

package test;

public interface IService {
  void doSomething();
}

和两个实现:

package test;

import javax.inject.Named;

@Named
@MyAnnotation("service1")
public class Service1 implements IService {

  @Override
  public void doSomething() {
    System.out.println("this");
  }
}

...

package test;

import javax.inject.Named;

@Named
@MyAnnotation("service2")
public class Service2 implements IService {

  @Override
  public void doSomething() {
    System.out.println("that");
  }
}

现在我想将一个IService注入另一个组件,让spring根据一些可查询的运行时属性和MyAnnotation的值选择正确的实现。

有没有办法在春天以一般方式做到这一点?

编辑:

我有一个拥有一些价值的上下文。在这种情况下,它是本地的线程。

package test;
public class MyValueHolder {

    private static final ThreadLocal<String> value = new ThreadLocal<>();

    public static void set(String newValue) {
        value.set(newValue);
    }

    public static String get() {
        return value.get();
    }

    public static void reset() {
        value.remove();
    }
}

我有一个使用IService

的组件
package test;

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class MyComponent {

    @Inject
    private IService service;

    public void myImportantWorkflow(){

        MyValueHolder.set("service1");
        service.doSomething();

        MyValueHolder.set("service2");
        service.doSomething();
    }
}

注入的服务应该只是一个代理。根据{{​​1}}中设置的值,对MyValueHolder的调用应委托给service1或service2。因此,在此示例中,它应该在第一次调用中委托给service1上的doSomething,而在第二次调用中委托给service2。

我可以编写一个实现doSomething接口的委托人,并将其用于这一项服务。但是我必须为其他所有服务重复这一点。我希望春天可以用代理几乎单独做这样的事情。当然,我必须提供一些方法来根据本地线程中的值hold来查找bean并将其注册到spring。但我不知道如果不修改弹簧框架,这是否可行。如果有可能如何实现这一点。

2 个答案:

答案 0 :(得分:1)

您可以使用ProxyFactoryBean创建代理,使用TargetSource进行查找。

例如(未经测试)

public class AnnotatedBeanTargetSource implements TargetSource, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;
    private Class<? extends Annotation> annotationType;
    private Class<?> implementedIterface;
    private Map<String, Object> beans;

    @Override
    public Class<?> getTargetClass() {
        return this.implementedIterface;
    }

    @Override
    public boolean isStatic() {
        return false;
    }

    @Override
    public Object getTarget() throws Exception {
        if (this.beans == null) {
            this.beans = lookupTargets();
        }

        return this.beans.get(MyValueHolder.get());
    }

    protected Map<String, Object> lookupTargets() { 
        Map<String, Object> resolvedBeans = new HashMap<String, Object>();
        String[] candidates = beanFactory.getBeanNamesForAnnotation(annotationType);
        for (String beanName : candidates) {
            Class<?> type = beanFactory.getType(beanName);

            if (this.implementedIterface.isAssignableFrom(type)) {
                Annotation ann = AnnotationUtils.getAnnotation(type, annotationType);
                resolvedBeans.put((String) AnnotationUtils.getValue(ann), beanFactory.getBean(beanName));
            }
        }

        return resolvedBeans;
    }

    @Override
    public void releaseTarget(Object target) throws Exception {
        // nothing to do
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;

    }

    public Class<? extends Annotation> getAnnotationType() {
        return annotationType;
    }

    public void setAnnotationType(Class<? extends Annotation> annotationType) {
        this.annotationType = annotationType;
    }

    public Class<?> getImplementedIterface() {
        return implementedIterface;
    }

    public void setImplementedIterface(Class<?> implementedIterface) {
        this.implementedIterface = implementedIterface;
    }
}

答案 1 :(得分:1)

这就是我要做的事情:

@Named
public class MyComponent {

    // introduce a marker interface for Injecting proxies
    @InjectDynamic
    IService service
    ...

    public void useIService() {
       service.doSomething();
       ...
       service.doSomethingElse();
       ...
       service.doFinally();
    }
}

定义一个BeanPostProcessor,用@InjectDynamic注释的字段扫描bean,然后创建并注入一个实现该字段所需类型的代理。

Proxy实现将在applicationContext中查找实现Supplier<T>(Java 8或guava版本)的bean,其中<T>是用@InjectDynamic注释的字段的类型。

然后你可以定义

@Name
public IServiceSupplier implements Supplier<IService> {
     @Override
     public IService get() {
            // here you implement the look-up logic for IService
     }
}

通过这种方式,当前实现的活动查找与Proxy分离,并且可以按目标类型进行更改。