泛型依賴注入
Spring 4.0版本中更新了很多新功能,其中比較重要的一個就是對帶泛型的Bean進行依賴注入的支援。
泛型依賴注入允許我們在使用spring進行依賴注入的同時,利用泛型的優點對程式碼進行精簡,同時在不增加程式碼的情況下增加程式碼的複用性。
Spring預設按照欄位的類進行依賴注入,而Spring4的新特性就是把泛型的具體型別也作為類的一種分類方法(Qualifier)。
背景
假設有兩個實體類Student
和Teacher
@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 {
}
}
總結
- 使用泛型依賴注入,可以減少重複程式碼和類的數目,在本例中,無需對
Student
和Teacher
兩個實體定義倉儲介面和實現,使用統一的倉儲介面和預設實現即可完成大部分的操作。 - 在專案中,我們使用
mybatis-plus
做為倉儲層運算元據庫,對實體的操作,都需要定義一個Mapper
介面和一個Service
介面,如果使用泛型注入,是否可以減少Service
的程式碼量呢? - 本文中使用的
@Repository() @Scope("prototype")
兩個註解註冊倉儲例項的Bean,@Scope("prototype")
註解會導致每個實體的倉儲都是瞬態的,如何保持每個實體對應的倉儲Bean是同一個例項呢? 下一篇會介紹另外一種註冊Bean的方式: spring bean的動態注入