程序员应知应会之Spring Data Jpa为什么不用写@Repository注解?

大家知道,在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>
【程序员应知应会之Spring Data Jpa为什么不用写@Repository注解?】可以看到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)判断里面 。

程序员应知应会之Spring Data Jpa为什么不用写@Repository注解?

文章插图
 
而这个函数会将目标接口及其父接口一层层地往上对比,如果与该类自身的
includeFilters中的某个filter比中,则会返回true,意味着该实现将会作为bean被Spring管理起来,从而可以直接用@Autowired引用 。
那么我们先来看看includeFilters里面到底有些什么东西,查询代码可以看到,该类在初始化的时候,添加了Component和ManagedBean 。很显然,这与我们的Repository还是毫无关系的 。事实上也是如此,在Spring启动时,第一遍扫描并没有把我们的BookRepository注入bean 。
直到
org.springframework.data.repository.config包中的
RepositoryConfigurationDelegate执行的时候,才会开始扫描,而这个类执行的时候,会启动一个继承了ClassPathScanningCandidateComponentProvider类的RepositoryComponentProvider 。
而在这个类里面,我们可以看到Repository最终被加载到了includeFilters里面 。
程序员应知应会之Spring Data Jpa为什么不用写@Repository注解?

文章插图
 
此时,再扫描对应的包的时候,继承自Repository的所有dao层类,就被会注入成bean,供人们调用了 。




    推荐阅读