大家知道,在Springboot+Spring Data Jpa的项目里,dao层只需要继承JpaRepository接口,就可以实现Mybatis中@Repository+mapper的效果,不需要任何多余的配置,就可以将dao层注入bean。类似于这样: public interface BookRepository extends JpaRepository<Book, Long> 这样一句话,就可以实现很多的增删改查效果,例如findAll(),findById()等等,可以说是非常的简单高效。 那么很多刚开始用Spring Data Jpa的同学就会很不理解,为什么这样一句话,就可以实现那么多的功能呢,不添加一个@Repository,心里总有些不踏实的感觉。 那么我们来看一下,Spring Data Jpa是怎么做到的。 一、JpaRepository的继承结构 首先我们来看看JpaRepository的继承结构。很容易看到JpaRepository的定义: public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> 可以看到JpaRepository继承了两个接口,一个PagingAndSortingRepository和一个QueryByExampleExecutor。 这两个接口的定义分别是: public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> 和public interface QueryByExampleExecutor<T> CrudRepository的定义为 public interface CrudRepository<T, ID> extends Repository<T, ID> 可以看到,最终的继承结果继承到了Repository里面。 而这一系列的继承,就为我们提供了 save(S entity); saveAll(Iterable<S> entities); findById(ID id); existsById(ID id); findAll(); findAllById(Iterable<ID> ids); count(); deleteById(ID id); delete(T entity); deleteAll(Iterable<? extends T> entities); deleteAll(); findOne(Example<S> example); findAll(Example<S> example); findAll(Example<S> example, Sort sort); findAll(Example<S> example, Pageable pageable); count(Example<S> example); exists(Example<S> example); findAll(Sort sort); findAll(Pageable pageable); 等很多的功能。 二、JpaRepository为什么不需要@Repository注解 经过简单的Debug,我们就可以轻松定位到Spring注入bean的位置,是在org.springframework.context.annotation包里面的ClassPathScanningCandidateComponentProvider类中的scanCandidateComponents方法里面,其中关键的代码在下面标蓝的isCandidateComponent(metadataReader)判断里面。
而这个函数会将目标接口及其父接口一层层的往上对比,如果与该类自身的 includeFilters中的某个filter比中,则会返回true,意味着该实现将会作为bean被Spring管理起来,从而可以直接用@Autowired引用。 那么我们先来看看includeFilters里面到底有些什么东西,查询代码可以看到,该类在初始化的时候,添加了Component和ManagedBean。很显然,这与我们的Repository还是毫无关系的。事实上也是如此,在Spring启动时,第一遍扫描并没有把我们的BookRepository注入bean。 直到org.springframework.data.repository.config包中的 RepositoryConfigurationDelegate执行的时候,才会开始扫描,而这个类执行的时候,会启动一个继承了ClassPathScanningCandidateComponentProvider类的RepositoryComponentProvider。 而在这个类里面,我们可以看到Repository最终被加载到了includeFilters里面。 
此时,再扫描对应的包的时候,继承自Repository的所有dao层类,就被会注入成bean,供人们调用了。 喜欢本文的话,欢迎关注活在信息时代哦:)
|