给定一个接口IService,以及它的3种实现:ServiceAServiceALoggerServiceAMetrics
ServiceALoggerServiceAMetricsServiceA的包装,可以选择实例化。此外,还有一种组合,其中ServiceAMetricsServiceALogger都实例化。

我知道如何使用@Configuration@Bean方法来实现它,但是可以使用类注释(@Primary@Order ...)来实现吗?

这是一个演示概念的代码段:

package com.foo;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.stereotype.Service;

interface IService {
    void foo();
}

class LoggerCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return false;
    }
}

class MetricsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return false;
    }
}

@Service
class ServiceA implements IService {

    @Override
    public void foo() {
        System.out.println("I'm foo");
    }
}

@Service
@Conditional(LoggerCondition.class)
class ServiceALogger implements IService {
    private final IService service;

    public ServiceALogger(IService service) {
        this.service = service;
    }

    @Override
    public void foo() {
        System.out.println("print some logs");
        service.foo();
    }
}

@Service
@Conditional(MetricsCondition.class)
class ServiceAMetrics implements IService {
    private final IService service;

    public ServiceAMetrics(IService service) {
        this.service = service;
    }

    @Override
    public void foo() {
        System.out.println("send some metrics");
        service.foo();
    }
}

@Configuration
@ComponentScan("com.foo")
public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(Main.class);
        ctx.refresh();

        IService bean = ctx.getBean(IService.class);
        bean.foo();
    }
}

最佳答案

看来我找到了可能的解决方案。这不是一个优雅的方法,但确实有效。

我使用@Priority批注来确定当它们有多个实例时应该注入哪种bean。而@Qualifier打破了ServiceAMetricsServiceALogger之间的循环依赖关系。

package com.foo;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.stereotype.Service;

import javax.annotation.Priority;
import java.util.List;

interface IService {
    void foo();
}

class LoggerCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return true;
    }
}

class MetricsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return true;
    }
}

@Service
@Qualifier("main")
@Priority(Integer.MAX_VALUE)
class ServiceA implements IService {
    @Override
    public void foo() {
        System.out.println("I'm foo");
    }
}

@Service
@Conditional(LoggerCondition.class)
@Priority(Integer.MAX_VALUE - 1)
class ServiceALogger implements IService {
    private final IService service;

    // using this @Qualifier prevents circular dependency
    public ServiceALogger(@Qualifier("main") IService service) {
        this.service = service;
    }

    @Override
    public void foo() {
        System.out.println("print some logs");
        service.foo();
    }
}

@Service
@Conditional(MetricsCondition.class)
@Priority(Integer.MAX_VALUE - 2)
class ServiceAMetrics implements IService {
    private final IService service;

    public ServiceAMetrics(IService service) {
        this.service = service;
    }

    @Override
    public void foo() {
        System.out.println("send some metrics");
        service.foo();
    }
}

@Configuration
@ComponentScan("com.foo")
public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(Main.class);
        ctx.refresh();

        IService bean = ctx.getBean(IService.class);
        bean.foo();
    }
}

08-18 12:19