spring泛型注入

諸葛小亮發表於2021-05-09

泛型依賴注入

Spring 4.0版本中更新了很多新功能,其中比較重要的一個就是對帶泛型的Bean進行依賴注入的支援。
泛型依賴注入允許我們在使用spring進行依賴注入的同時,利用泛型的優點對程式碼進行精簡,同時在不增加程式碼的情況下增加程式碼的複用性。
Spring預設按照欄位的類進行依賴注入,而Spring4的新特性就是把泛型的具體型別也作為類的一種分類方法(Qualifier)。

背景

假設有兩個實體類StudentTeacher

@Data
public class Student implements IEntity{
    private long id;
}

@Data
public class Teacher implements IEntity{
    private long id;
}

實體的儲存,是通過倉儲操作的,一般所有的實體倉儲方法都是一致的,只有具體的實體型別不一樣,定義倉儲介面

public interface IRepository<TEntity extends IEntity>{
    void add(TEntity entity);
    List<TEntity> findAll();
   ...
}

定義倉儲實現的基類,在本例中,使用List儲存

public abstract class BaseRepository <TEntity extends IEntity> implements IRepository<TEntity>{
    List<TEntity> datasource = new ArrayList<>();
    @Override
    public void add(TEntity entity){
        this.datasource.add(entity);
    }

    @Override
    public List<TEntity> findAll(){
        return datasource;
    }

}

泛型依賴注入的Bean

BaseRepository 是一個抽象類,不適宜注入到spring中,定義一個可以注入的bean

@Repository()
@Scope("prototype")
public class DefaultRepository<TEntity extends IEntity> extends BaseRepository<TEntity>{
}

注意@Scope("prototype")註解,表示DefaultRepository的bean的作用是瞬態的,每次獲取bean時都會建立一個新的bean,如果不新增作用域,預設spring的bean是單例的,這樣注入的倉儲例項會是同一個例項。
test中依賴注入@Autowired IRepository<Student> studentRepository; @Autowired IRepository<Teacher> teacherRepository;,
驗證這兩個倉儲的型別都是DefaultRepository型別,同時驗證操作student不會影響到teacher


@ExtendWith(SpringExtension.class)
@ContextConfiguration(
        classes = {DemoTests.DemoTestsConfiguration.class})
public class DemoTests {

    @Autowired
    IRepository<Student> studentRepository;

    @Autowired
    IRepository<Teacher> teacherRepository;

    @Test
    public void test(){

        assertThat(studentIRepository.getClass())
                .isEqualTo(DefaultRepository.class);
        assertThat(teacherIRepository.getClass())
                .isEqualTo(DefaultRepository.class);

        studentIRepository.add(new Student());

        assertThat(studentIRepository.findAll())
                .hasSize(1);

        assertThat(teacherIRepository.findAll())
                .hasSize(0);
    }



    @ComponentScan({
            "org.example"
    })
    @Configuration
    public static class DemoTestsConfiguration {
    }

}

倉儲擴充套件

在上一部分,所有的倉儲操作,都定義在了BaseRepository中,如果遇到了倉儲中未提供的方法,則需要對單個實體的倉儲進行擴充套件。
自定義倉儲介面,繼承自IRepository<Student>

public interface IStudentRepository extends IRepository<Student>{
    Student findById(long id);
}

實現自定義倉儲介面

@Repository
public class StudentRepository extends BaseRepository<Student> implements IStudentRepository {
    @Override
    public Student findById(long id) {
        return null;
    }
}

使用例子如下


@ExtendWith(SpringExtension.class)
@ContextConfiguration(
        classes = {DemoTests.DemoTestsConfiguration.class})
public class DemoTests {

    @Autowired
    IRepository<Teacher> teacherRepository;

    @Autowired
    IStudentRepository studentRepository;

    @Test
    public void repositoryType(){

        assertThat(studentRepository.getClass())
                .isEqualTo(StudentRepository.class);
        assertThat(teacherRepository.getClass())
                .isEqualTo(DefaultRepository.class);
    }



    @ComponentScan({
            "org.example"
    })
    @Configuration
    public static class DemoTestsConfiguration {
    }

}

總結

  1. 使用泛型依賴注入,可以減少重複程式碼和類的數目,在本例中,無需對StudentTeacher兩個實體定義倉儲介面和實現,使用統一的倉儲介面和預設實現即可完成大部分的操作。
  2. 在專案中,我們使用mybatis-plus做為倉儲層運算元據庫,對實體的操作,都需要定義一個Mapper介面和一個Service介面,如果使用泛型注入,是否可以減少Service的程式碼量呢?
  3. 本文中使用的@Repository() @Scope("prototype") 兩個註解註冊倉儲例項的Bean,@Scope("prototype")註解會導致每個實體的倉儲都是瞬態的,如何保持每個實體對應的倉儲Bean是同一個例項呢? 下一篇會介紹另外一種註冊Bean的方式: spring bean的動態注入

關注我的公眾號,一起探索新技術的海洋

相關文章