1. 程式人生 > >Spring4新特性:泛型限定式依賴注入

Spring4新特性:泛型限定式依賴注入

Spring 4.0已經發布RELEASE版本,不僅支援Java8,而且向下相容到JavaSE6/JavaEE6,並移出了相關廢棄類,新新增如Java8的支援、Groovy式Bean定義DSL、對核心容器進行增強、對Web框架的增強、Websocket模組的實現、測試的增強等。其中兩個我一直想要的增強就是:支援泛型依賴注入、對cglib類代理不再要求必須有空參構造器了。具體更新請參考:

1、相關程式碼:

1.1、Entity

  1. publicclass User implements Serializable {  
  2.     private Long id;  
  3.     private String name;  
  4. }  
  5. publicclass Organization implements Serializable {  
  6.     private Long id;  
  7.     private String name;  
  8. }  
1.2、Repository
  1. publicabstractclass BaseRepository<M extends Serializable> {  
  2.     publicvoid save(M m) {  
  3.         System.out.println("=====repository save:" + m);  
  4.     }  
  5. }  
  6. @Repository
  7. publicclass UserRepository extends BaseRepository<User> {  
  8. }  
  9. @Repository
  10. publicclass OrganizationRepository extends BaseRepository<Organization> {  
  11. }  
對於Repository,我們一般是這樣實現的:首先寫一個模板父類,把通用的CRUD等程式碼放在BaseRepository;然後子類繼承後,只需要新增額外的實現。

1.3、Service

1.3.1、以前Service寫法

  1. publicabstract
    class BaseService<M extends Serializable> {  
  2.     private BaseRepository<M> repository;  
  3.     publicvoid setRepository(BaseRepository<M> repository) {  
  4.         this.repository = repository;  
  5.     }  
  6.     publicvoid save(M m) {  
  7.         repository.save(m);  
  8.     }  
  9. }  
  10. @Service
  11. publicclass UserService extends BaseService<User> {  
  12.     @Autowired
  13.     publicvoid setUserRepository(UserRepository userRepository) {  
  14.         setRepository(userRepository);  
  15.     }  
  16. }  
  17. @Service
  18. publicclass OrganizationService extends BaseService<Organization> {  
  19.     @Autowired
  20.     publicvoid setOrganizationRepository(OrganizationRepository organizationRepository) {  
  21.         setRepository(organizationRepository);  
  22.     }  
  23. }  
可以看到,以前必須再寫一個setter方法,然後指定注入的具體型別,然後進行注入;

1.3.2、泛型Service的寫法

  1. publicabstractclass BaseService<M extends Serializable> {  
  2.     @Autowired
  3.     protected BaseRepository<M> repository;  
  4.     publicvoid save(M m) {  
  5.         repository.save(m);  
  6.     }  
  7. }  
  8. @Service
  9. publicclass UserService extends BaseService<User> {  
  10. }  
  11. @Service
  12. publicclass OrganizationService extends BaseService<Organization> {  
  13. }  

大家可以看到,現在的寫法非常簡潔。支援泛型式依賴注入。

這個也是我之前非常想要的一個功能,這樣對於那些基本的CRUD式程式碼,可以簡化更多的程式碼。

如果大家用過Spring data jpa的話,以後注入的話也可以使用泛型限定式依賴注入 :

  1. @Autowired
  2. private Repository<User> userRepository; 

1.4 Map依賴注入:

  1. @Autowired
  2. private Map<String, BaseService> map;  

這樣會注入:key是bean名字;value就是所有實現了BaseService的Bean,假設使用上一篇的例子,則會得到:

{orga[email protected]617029,[email protected]}

1.5 List/陣列注入:

  1. @Autowired
  2. private List<BaseService> list; 
這樣會注入所有實現了BaseService的Bean;但是順序是不確定的,如果我們想要按照某個順序獲取;在Spring4中可以使用@Order或實現Ordered介面來實現,如:
  1. @Order(value = 1)  
  2. @Service
  3. publicclass UserService extends BaseService<User> {  
  4. }  
這種方式在一些需要多型的場景下是非常有用的。

1.6 @Lazy可以延遲依賴注入:

  1. @Lazy
  2. @Service
  3. publicclass UserService extends BaseService<User> {  
  1. @Lazy
  2. @Autowired
  3. private UserService userService; 
我們可以把@Lazy放在@Autowired之上,即依賴注入也是延遲的;當我們呼叫userService時才會注入。即延遲依賴注入到使用時。同樣適用於@Bean。

1.7 @Conditional

@Conditional類似於@Profile(一般用於如我們有開發環境、測試環境、正式機環境,為了方便切換不同的環境可以使用@Profile指定各個環境的配置,然後通過某個配置來開啟某一個環境,方便切換,但是@Conditional的優點是允許自己定義規則。可以指定在如@Component、@Bean、@Configuration等註解的類上,以絕對Bean是否建立等。首先來看看使用@Profile的用例,假設我們有個使用者模組:

1、在測試/開發期間呼叫本機的模擬介面方便開發;

2、在部署到正式機時換成呼叫遠端介面;

  1. publicabstractclass UserService extends BaseService<User> {  
  2. }  
  3. @Profile("local")  
  4. @Service
  5. publicclass LocalUserService extends UserService {  
  6. }  
  7. @Profile("remote")  
  8. @Service
  9. publicclass RemoteUserService extends UserService {  
  10. }  
我們在寫測試用例時,可以指定我們使用哪個Profile:
  1. @ActiveProfiles("remote")  
  2. @RunWith(SpringJUnit4ClassRunner.class)  
  3. @ContextConfiguration(locations =  "classpath:spring-config.xml")  
  4. publicclass ServiceTest {  
  5.     @Autowired
  6.     private UserService userService;  
  7. }  
這種方式非常簡單。如果想自定義如@Profile之類的註解等,那麼@Conditional就派上用場了;假設我們系統中有好多本地/遠端介面,那麼我們定義兩個註解@Local和@Remote註解要比使用@Profile方便的多;如:
  1. @Retention(RetentionPolicy.RUNTIME)  
  2. @Target({ElementType.TYPE, ElementType.METHOD})  
  3. @Conditional(CustomCondition.class)  
  4. public@interface Local {  
  5. }  
  6. @Retention(RetentionPolicy.RUNTIME)  
  7. @Target({ElementType.TYPE, ElementType.METHOD})  
  8. @Conditional(CustomCondition.class)  
  9. public@interface Remote {  
  10. }  

  1. publicclass CustomCondition implements Condition {  
  2.     @Override
  3.     publicboolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {  
  4.         boolean isLocalBean = metadata.isAnnotated("com.sishuok.spring4.annotation.Local");  
  5.         boolean isRemoteBean = metadata.isAnnotated("com.sishuok.spring4.annotation.Remote");  
  6.         //如果bean沒有註解@Local或@Remote,返回true,表示建立Bean
  7.         if(!isLocalBean && !isRemoteBean) {  
  8.             returntrue;  
  9.         }  
  10.         boolean isLocalProfile = context.getEnvironment().acceptsProfiles("local");  
  11.         //如果profile=local 且 bean註解了@Local,則返回true 表示建立bean;
  12.         if(isLocalProfile) {  
  13.             return isLocalBean;  
  14.         }  
  15.         //否則預設返回註解了@Remote或沒有註解@Remote的Bean
  16.         return isRemoteBean;  
  17.     }  
  18. }