Generic dependency injection

Spring 4.0 has a number of new features, one of the most important being dependency injection support for generics beans. Generic dependency injection allows us to use Spring for dependency injection while taking advantage of generics to simplify code and increase its reusability without adding more code. Spring defaults to dependency injection by field class, and a new feature in Spring4 is that generic concrete types are also used as a classification method for classes (Qualifier).

background

Suppose there are two entity classes Student and Teacher

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

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

Entity storage, is through warehousing operation, generally all physical warehousing methods are consistent, only the specific entity type is not the same, the definition of warehousing interface

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

Defines the base class for the repository implementation, in this case, using List storage

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;
    }

}
Copy the code

Generic dependency injection beans

BaseRepository is an abstract class that is not suitable for injection into Spring. Define a bean that can be injected

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

Note the @scope (“prototype”) annotation that indicates that the bean from DefaultRepository is transient and that a new bean is created each time the bean is acquired. Without Scope, spring’s beans are singletons by default. The injected repository instance will be the same instance. Dependency injection @autowired IRepository

studentRepository; And the @autowired IRepository < the Teacher > teacherRepository; , verify that these two repositories are of DefaultRepository type, and verify that operation student does not affect 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 {
    }

}
Copy the code

Storage extension

In the previous section, all warehousing operations were defined in BaseRepository, and the warehousing of a single entity needs to be extended if methods not provided in the repository are encountered. Custom repository interface, inherited from IRepository

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

Implement custom warehouse interface

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

Examples of use are as follows


@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 {
    }

}
Copy the code

conclusion

  1. With generic dependency injection, you can reduce the number of duplicate code and classes, and in this case, no pair is requiredStudentandTeacherThe two entities define the warehouse interface and implementation, with a unified warehouse interface and default implementation doing most of the work.