spring-data-jpa的优点很多,比如继承Repository接口,在注解中书写JPQL语句即可访问数据库;支持方法名解析方式访问数据库;使用Predicate支持动态查询等,在此不一一列举了。这些都是使用spring-data-jpa中的种种优点,要想将之使用的更好更优雅,就要从spring-data-jpa的加载和运行机制进行探秘。
此文系作者查看spring-data-jpa源码及参考javadoc文档成文,所以文中难免会有错误和纰漏,恳请读者指出。下面进入正题。
1.运行环境创建及加载Repository接口
spring-data-jpa在运行时和springframework框架实现了无缝对接。在使用spring的@Repository注解生成Repository实例时,使用动态代理类的方式对Repository接口进行了实例化并放入spring容器中备用。
下面是spring-data-jpa中占重要地位的RepositoryFactoryBeanSupport和RepositoryFactorySupport的类图:
RepositoryFactoryBeanSupport的作用是,为spring的FactoryBean接口开发的适配器,可以很便捷的通过spring配置设置repository工厂;而RepositoryFactorySupport的作用是,创建一个给定repository接口实例的工厂bean。创建一个实现配置的repository接口的代理,并应用一个advice将控制交给QueryExecuterMethodInterceptor。
通过查看源码,我们可以看到在RepositoryFactoryBeanSupport中持有一个私有RepositoryFactorySupport类变量,在执行RepositoryFactoryBeanSupport.afterPropertiesSet()方法时,通过createRepositoryFactory()方法,new了一个JpaRepositoryFactory实例并赋值给了类变量factory(RepositoryFactorySupport),查看JpaRepositoryFactory的构造器,可以看到持有了entityManager等实例,以方便后面的使用。RepositoryFactoryBeanSupport中的private T initAndReturn()是另一个相当重要的方法,方法说明是:
Returns the previously initialized repository proxy or creates and returns the proxy if previously uninitialized.返回先前实例化了的repository代理实例,或者,如果先前没有实例化,那么生成并且返回一个代理实例。
为什么这么说明呢?我们来看代码,这个方法被另外两个方法调用,一个是afterPropertiesSet(),这是实现InitialingBean接口需要实现的一个加载方法,一般会先调用;另一个则是实现FactoryBean接口需要实现的getObject()方法,在运行中会调用之返回一个Repository接口的实例。所以,一般来说initAndReturn()至少会有两次被调用的机会。initAndReturn()的实现比较简单:
private T initAndReturn() { Assert.notNull(repositoryInterface, "Repository interface must not be null on initialization!"); if (this.repository == null) { this.repository = this.factory.getRepository(repositoryInterface, customImplementation); } return this.repository; }
通过RepositoryFactorySupport(JpaRepositoryFactory实例)的getRepository(repositoryInterface, customImplementation);获取了一个org.springframework.data.jpa.repository.support.SimpleJpaRepository代理类实例(定义为泛型T extends Repository),在构造这个实例时,进行了一系列的运行环境准备:
在RepositoryFactorySupport的内部类QueryExecutorMethodInterceptor中获取Query查找策略类QueryLookupStrategy实例(此处一般是CreateIfNotFoundQueryLookupStrategy实例),然后执行resolveQuery(method, repositoryInformation, factory, namedQueries);方法将RepositoryQuery实例执行循环放入RepositoryFactorySupport(JpaRepositoryFactory实例)的类变量queries,一个ConcurrentHashMap<Method, RepositoryQuery>中。
下图是QueryLookupStrategy的类图:
构造RepositoryQuery实例时对应了一个JpaQueryMethod实例,RepositoryQuery实例实际上是SimpleJpaQuery实例。
上面的描述总括上来说,就是一个通过spring框架生成(注意不是注入生成)的JpaRepositoryFactoryBean实例实现了InitialzingBean接口,在public void afterPropertiesSet()方法调用时生成了一个SimpleJpaRepository代理实例。SimpleJpaRepository实例中持有本次实例化的Repository代理类,以及在QueryExecutorMethodInterceptor构造器中实例化了的多个JpaQueryMethod,顾名思义,JpaQueryMethod就是jpa的带有@Query注解的方法数据存储类,所以Repository接口有多少个方法,就会包含多少个JpaQueryMethod实例被加入监听序列。