Spring服务的可选嵌套注入

时间:2019-04-17 16:56:01

标签: java spring dependency-injection

给出一个接口IService及其3个实现:ServiceAServiceALoggerServiceAMetrics

ServiceALoggerServiceAMetricsServiceA的包装,可以选择实例化。此外,还有一种组合,其中ServiceAMetricsServiceALogger都被实例化。

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

以下是演示概念的代码段:

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();
    }
}

1 个答案:

答案 0 :(得分:1)

好像我找到了可能的解决方案。这不是一个优雅的方法,但是可以。

我使用@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();
    }
}