Дополнительное вложенное внедрение Spring Services - PullRequest
2 голосов
/ 17 апреля 2019

Имеется интерфейс IService и 3 его реализации: ServiceA, ServiceALogger и ServiceAMetrics.

ServiceALogger и ServiceAMetrics являются оболочками ServiceA и могут быть созданы по желанию. Более того, существует комбинация, в которой ServiceAMetrics и ServiceALogger оба созданы.

Я знаю, как реализовать это с помощью методов @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();
    }
}

1 Ответ

1 голос
/ 18 апреля 2019

Похоже, я нашел возможное решение. Это не элегантный, но работает.

Я использую аннотации @Priority, чтобы определить, какой бин следует вводить, когда их несколько. И @Qualifier для разрыва круговой зависимости между ServiceAMetrics и ServiceALogger.

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();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...