Есть ли какой-нибудь способ узнать в Spring, какой бин использовать, находясь в родительском классе? - PullRequest
0 голосов
/ 23 октября 2019

У меня есть конкретная проблема, в которой я не могу использовать @Qualifier, потому что мне нужен компонент в родительском классе. Моя идея состоит в том, чтобы удалить свойство baseComponent и создать абстрактный метод в BaseController, например getComponent(), и вернуть желаемый bean-компонент для BaseComponent ... но, возможно, есть более чистый способ сделать это с помощью конфигурации.

@RestController
public abstract class BaseController {

    @Autowired
    private BaseComponent baseComponent;

    @GetMapping("/something")
    public void doSomething() {
        baseComponent.printSomething();
    }

}

@RestController
@RequestMapping(value = "/foo")
public class FooController extends BaseController {

}

@RestController
@RequestMapping(value = "/bar")
public class BarController extends BaseController {

}

public interface BaseComponent {
    void printSomething();
}

@Component
public class FooComponent implements BaseComponent {

    @Override
    public void printSomething() {
        System.out.println("foo!");
    }

}

@Component
public class BarComponent implements BaseComponent{

    @Override
    public void printSomething() {
        System.out.println("bar!");
    }

}

Ответы [ 3 ]

0 голосов
/ 23 октября 2019

Это одна из причин, по которой мне не нравится автоматическое подключение непосредственно к частному полю. Я хотел бы сделать это, внедрив BaseComponent через конструктор BaseController:

public abstract class BaseController {

   private final BaseComponent baseComponent;

   protected BaseController(BaseComponent baseComponent){
       this.baseComponent = baseComponent;
   }

   @GetMapping("/something")
   public ResponseEntity<String> getSomething(){
       return new ResponseEntity<String>(baseComponent.getSomething(), HttpStatus.OK);
   }
}

@RestController
@RequestMapping("/foo")
public class FooController extends BaseController{

   @Autowired
   public FooController(@Qualifier("fooComponent") BaseComponent baseComponent) {
      super(baseComponent);
   }
}

@RestController
@RequestMapping("/bar")
public class BarController extends BaseController{

   @Autowired
   public BarController(@Qualifier("barComponent") BaseComponent baseComponent){
       super(baseComponent);
   }
}

@Component
public class BarComponent implements BaseComponent {

   @Override
   public String getSomething() {
       return "bar";
   }
}

@Component
public class FooComponent implements BaseComponent {

   @Override
   public String getSomething() {
     return "foo";
   }
}

Запросы к /thing / bar вернут bar, а запросы к чему-то / foo вернут foo.

Обратите внимание, чтоабстрактный BaseComponent фактически не объявляется как какой-либо компонент Spring и не имеет каких-либо зависимостей, которые автоматически добавляются. Вместо этого подклассы являются компонентами, а зависимости связаны в свои конструкторы и передаются через super до BaseComponent. Конструкторы подкласса предоставляют место для аннотации @Qualifier, чтобы указать, какой BaseComponent вы хотите.

Теоретически мне не нравится объявлять два класса, которые идентичны, кроме аннотаций. Однако на практике я обнаружил, что иногда проще всего объявить классы для хранения аннотаций Spring. Это лучше, чем в прежние времена конфигурации XML.

0 голосов
/ 23 октября 2019

Вы можете использовать дженерики, если не хотите жестко кодировать имя компонента:

@RestController
public abstract class BaseController<T extends BaseComponent> {

   @Autowired
   private T baseComponent;

   ...

}

@RestController
public class FooController extends BaseController<FooComponent> {
 ...
}
0 голосов
/ 23 октября 2019

Вы можете сделать это следующим образом

import org.springframework.beans.factory.BeanFactory;

@RestController
public abstract class BaseController {

    @Autowired
    private BeanFactory beanFactory;

    @GetMapping("/something")
    public void doSomething() {
        BaseComponent baseComponent = beanFactory.getBean("Foo", // or "Bar"
                                                          BaseComponent.class); 
        baseComponent.printSomething();
    }

}


@Component("Foo")
public class FooComponent implements BaseComponent {
...

}


@Component("Bar")
public class BarComponent implements BaseComponent{
...

}

Если я правильно понимаю, что вы хотите.

...