Тип пружинного впрыска - PullRequest
1 голос
/ 28 июня 2019

Скажем, у нас есть такой интерфейс:

public interface Validator<T> {
  boolean isValid(T data);
}

И это часть основного модуля.Несколько приложений могут использовать один и тот же основной модуль с другим значением для универсального T. Пример реализации (из определенного модуля приложения):

@Component
public class AppValidator implements Validator<String> {
  @Override
  public boolean isValid(String data) {
    return false;
  }
}

И затем в контроллере (который является частьюмодуля ядра):

@RestController
public class ValidateController {
  @Autowired
  private Validator validator;

  @RequestMapping("/")
  public void index() {
    validator.validate("");
  }
}

IntelliJ жалуется, что я использую необработанные типы;как вы можете видеть, я фактически делаю это в контроллере.

Мой вопрос: есть ли способ ввести зависимость ограниченным образом (вместо инъекции Validator, инъекции Validator<String>)?Но, конечно, связанный тип может меняться в зависимости от приложения, использующего модуль ядра?

Если это невозможно (вероятно, из-за стирания типа), что является лучшим способом для этого?Это просто использовать Object?Разве нет более приятной альтернативы, которая все еще обеспечивает безопасность типов?

Я видел, как где-то люди говорили, что во время компиляции можно сделать какое-то волшебство, чтобы изменить типы, но я не уверен, как, или дажеесли я прочитал это правильно?

Я использую Spring, так что я надеюсь, что Spring может предоставить мне что-то, чтобы помочь мне здесь!Немного волшебства можно только приветствовать!

Ответы [ 2 ]

2 голосов
/ 28 июня 2019

Ответ прост: вам не нужно никакого волшебства, оно просто работает весной.У вас есть AppValidator, а затем вы просто делаете (это вводится, глядя на общий тип):

@Autowired 
private Validator<String> appValidator;

Ну, все в порядке, теперь представьте, что у вас есть два Validator<String>, что тогда?required a single bean but found two exception - вот что.Вот почему это ужасная практика, никогда не делайте этого.

В моей работе один человек создал универсальный интерфейс с 3 универсальными типами, а затем на основе внедрения этих универсальных типов люди все еще ненавидят его.Выглядело это так, и да, это работает, если у вас нет одинаковых 3 универсальных типов в одном и том же порядке в нескольких реализациях:

@Autowired
private Invoker<String, Integer, Person> personInvoker;

@Autowired
private Invoker<Integer, String, Animal> animalInvoker;

Даже если у вас нетнесколько Validator<String> в вашем коде, и вы не планируете иметь больше - кто-то другой может войти и добавить их, или многие другие сценарии.

1 голос
/ 28 июня 2019

Вот отношения между вашими модулями (приложениями и ядром):

Application 1       Application 2      Application 3
     |                   |                   |
Validator<Foo>     Validator<Bar>     Validator<FooBar>
     |                   |                   |  
     |                   |                   |  
     |__ __ __ __ __ __ _| __ __ __ __ __ __ | 
                         |
                         | <<uses>>
                         |
                        \ /
                     Core Module    
                         |
                 ValidateController  (not generic rest controller)                   

Что-то здесь не так, так как вы хотите, чтобы общий компонент ValidateController опирался на определенный класс приложения Validator, но ValidateController не является универсальным классом, поэтому вы можете использовать только Object в качестве универсального типа, где вы будете использовать поле Validator.
Чтобы все было согласованно, вы должны создать эту недостающую ссылку.На самом деле вам нужны отдельные подклассы контроллера, потому что каждый контроллер должен использовать определенный экземпляр валидатора.
Вы можете, например, определить абстрактный класс / интерфейс ValidateController в модуле shared / code и оставить каждый подкласс расширяющим его и определяющимсам по себе универсальный Validator класс для использования.

Вот целевые отношения между вашими модулями:

Application 1        Application 2        Application 3
     |                   |                      |
Validator<Foo>       Validator<Bar>       Validator<FooBar>
FooController(bean)  BarController(bean)  FooBarController(bean)
     |                   |                      |  
     |                   |                      |  
     |__ __ __ __ __ ___ | __ ___ __ __ __ __ __| 
                         |
                         | <<uses>>
                         |
                        \ /
                     Core Module    
                         |
                 ValidateController<T>  (abstract class and not a bean)                   

Например, в основном / общем модуле:

public abstract class ValidateController<T> {

  private Validator<T> validator;

  ValidateController(Validator<T> validator){
     this.validator = validator;
  }

  @RequestMapping("/")
  public void index(T t) {
    boolean isValid = validator.validate(t);
  }

}

В приложении определите вашу реализацию валидатора:

@Component
public class AppValidator implements Validator<String> {
  @Override
  public boolean validate(String data) {
    return ...;
  }
}

И также определить подкласс StringController (или @Bean в качестве альтернативы), чтобы установить право Validator:

@RestController
public class StringController extends ValidateController<String>{

   public ValidateControllerApp(Validator<String> validator){
       this.validator = validator;
   }

}
...