簡體   English   中英

Spring注入通用類型

[英]Spring Inject Generic Type

假設我們有這樣一個界面:

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可以提供一些東西來幫助我! 歡迎一些魔術!

答案很簡單:你不需要任何魔法,它只適用於Spring。 你有你的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> ,並且您沒有計划擁有更多 - 其他人可能會進入並添加它們,或許多其他方案。

這里是您的模塊(應用程序和核心)之間的關系:

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 ,並讓每個子類擴展它,並為自己定義要使用的通用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;
   }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM