我们在定义
Repository
的时候通常定义的时一个接口,而并没有去实现这个接口,那么Jpa是如何让开发者无需自己实现接口就可以使用Repository
去操作数据库?
动态代理!!!
Repository原理
试想一下JPA是如何做的?
- 通过动态代理将接口实例化成对应的类。
- 解析方法名或根据指定的方法名
- 并根据得到的结果转换为数据库操作
尝试写一个示例
- 自定义一个
Repository
public interface CustomRepository<T, ID> extends Repository<T, ID> {
/**
* 查询前num条数据
* @param num
* @return
*/
List<T> findByFirst(Long num);
}
- 对自定义
Repository
的代理逻辑
/**
* @author yuanmengfan(mf.yuan @ qq.com) on 2024/8/7 23:26
* 自定义Repository的代理
*/
@Data
public class CustomRepositoryInvocationHandler implements InvocationHandler {
// 用于操作数据库
private EntityManager em;
// 知晓操作那个对象
private Class<?> entityClass;
public CustomRepositoryInvocationHandler(EntityManager em, Class<?> entityClass) {
this.em = em;
this.entityClass = entityClass;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
Object result = null;
String name = method.getName();
switch (name) {
// 根据方法名定义不同操作数据库的逻辑
case "findByFirst":
CriteriaBuilder cn = em.getCriteriaBuilder();
CriteriaQuery query = cn.createQuery(entityClass);
Root from = query.from(entityClass);
result = em.createQuery(query.select(from)).setMaxResults(Math.toIntExact((Long) args[0])).getResultList();
break;
}
return result;
}
}
- 测试下创建出来的代理对象是否能正常运行
@Test
public void test() {
// 初始化IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = applicationContext.getBean(LocalContainerEntityManagerFactoryBean.class);
// 创建EntityManager
EntityManager em = localContainerEntityManagerFactoryBean.getNativeEntityManagerFactory().createEntityManager();
Class<?> proxyClass = UserCustomRepository.class;
// 获取接口泛型信息
ParameterizedType parameterizedType = (ParameterizedType) proxyClass.getGenericInterfaces()[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Class entityClass = (Class) actualTypeArguments[0];
Class idClass = (Class) actualTypeArguments[1];
// 通过动态代理创建对象
UserCustomRepository proxyInstance = (UserCustomRepository) Proxy.newProxyInstance(
proxyClass.getClassLoader()
, new Class[]{proxyClass}
, new CustomRepositoryInvocationHandler(em, entityClass)
);
System.out.println(proxyInstance.findByFirst(5L));
}
已经可以根据我们指定的类型来操作数据库了。
源码跟踪验证
@Test
public void test(){
// 初始化IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);
UserJpaRepository bean = applicationContext.getBean(UserJpaRepository.class);
Optional<TUser> byId = bean.findById(5L);
}
能发现这里在IOC容器
中的是代理对象。
可以明显的看出这里是使用的JDK
动态代理。
这里可以看见实际上我们的对象是SimpleJpaRepository
实际调用findById
也是通过EntityManage
去操作数据库
Spring是如何整合Jpa呢?
疑问
怎么知道要注册那些Repository
?
@ComponentScan+@Component
// 添加
@ComponentScan(basePackages = "com.mfyuan")
public class JpaConfig{}
// 添加
@Component
public interface UserCustomRepository extends CustomRepository<TUser, Long> {}
@Test
public void test1() {
// 初始化IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);
// 尝试从容器中获取Bean
UserCustomRepository bean = applicationContext.getBean(UserCustomRepository.class);
}
为什么还是拿不到这个Bean
呢?
因为Repository
是接口不是类,不会被注册到IOC
容器中。在扫描成BeanDefinition
是被过滤掉了。
ConfigurationClassParser#doProcessConfigurationClass
ComponentScanAnnotationParser#parse
处理@Component
的解析
ClassPathBeanDefinitionScanner#doScan
#findCandidateComponents
#scanCandidateComponents
#isCandidateComponent(metadataReader)
判断是否包含@Component
注解
#isCandidateComponent(sbd) 判断是否为接口或者抽象类
:::
如何解决不扫描接口的问题呢?
ClassPathBeanDefinitionScanner
:自定义一个扫描器(让接口也可以别扫描到)。BeanDefinitionRegistryPostProcessor
:在BeanDefinition
注册的过程中可以允许进干预。
:::
public class CustomClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
public CustomClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
super(registry);
// 添加过滤器只扫描实现CustomRepository接口的类
}
/**
* 允许接口被扫描
*
* @param beanDefinition the bean definition to check
* @return
*/
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return metadata.isInterface();
}
}
@Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
CustomClassPathBeanDefinitionScanner definitionScanner = new CustomClassPathBeanDefinitionScanner(registry);
// 清空原有filter
definitionScanner.resetFilters(false);
// 添加过滤器只扫描实现CustomRepository接口的类
definitionScanner.addIncludeFilter(new AssignableTypeFilter(CustomRepository.class));
// 能将 com.mfyuan.repository 下的接口也进行注册了
definitionScanner.scan("com.mfyuan.repository");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistryPostProcessor.super.postProcessBeanFactory(beanFactory);
}
}
到现在我们的接口就可以被扫描成BeanDefinition
,但是又遇到一个问题?
与之前的错误不同的是已经不再提前找不到对应类型的Bean了,而是接口不能实例化的。
:::
如何将对应的Repository接口
注入到容器中?
可以将通过动态代理后的对象放入到容器中,这里就解决了接口不能实例化的问题。
但是不可能一个一个去创建动态代理的对象吧,这样想想就很痛苦。
使用FactoryBean
来定义创建过程。与前面Repository
原理结合。
:::
@Data
public class CustomRepositoryFactoryBean implements FactoryBean {
private Class<?> repositoryInterface;
@Autowired
private LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean;
public CustomRepositoryFactoryBean(Class<?> repositoryInterface) {
this.repositoryInterface = repositoryInterface;
}
/**
* FactoryBean的特性这个方法的返回值是注册到容器中对象
*/
@Override
public Object getObject() {
// 创建EntityManager
EntityManager em = localContainerEntityManagerFactoryBean.getNativeEntityManagerFactory().createEntityManager();
// 获取接口泛型信息
ParameterizedType parameterizedType = (ParameterizedType) repositoryInterface.getGenericInterfaces()[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Class<?> entityClass = (Class<?>) actualTypeArguments[0];
// Class idClass = (Class) actualTypeArguments[1];
// 通过动态代理创建对象
return Proxy.newProxyInstance(
repositoryInterface.getClassLoader()
, new Class[]{repositoryInterface}
, new CustomRepositoryInvocationHandler(em, entityClass)
);
}
/**
* FactoryBean的特性这个方法的返回值是注册到容器的对象类型
*/
@Override
public Class<?> getObjectType() {
return repositoryInterface;
}
}
重写CustomClassPathBeanDefinitionScanner#doScan
对扫描到的BeanDefinition
进行修改
public class CustomClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
// .....
@SneakyThrows
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 这里拿到的就行自定义扫描器 扫出来的所有BeanDefinition
Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();
// 得到扫描到的repositoryClass
String repositoryClassName = beanDefinition.getBeanClassName();
// 设置BeanDefinition的Class为FactoryBean
// mybatis 这里也是这样做的 只不过是新注册了一个BeanDefinition
beanDefinition.setBeanClass(CustomRepositoryFactoryBean.class);
// 添加一个构造参数
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(repositoryClassName);
}
return beanDefinitionHolders;
}
}
测试
@Test
public void test1() {
// 初始化IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);
// 尝试从容器中获取Bean
UserCustomRepository bean = applicationContext.getBean(UserCustomRepository.class);
System.out.println(bean.findByFirst(5L));
}
能够走自定义Repository
里面的方法了。
解释
CustomRepository
:相当于一个顶层接口,他去定义对应的统一的数据库操作。类似JpaRepository
,CrudRepository
等
CustomRepositoryInvocationHandler
:相当于是对顶层接口所有的方法进行了一个实现。类似SimpleJpaRepository
源码验证
// 导入JpaRepositoriesRegistrar
@Import(JpaRepositoriesRegistrar.class)
public @interface EnableJpaRepositories {}
// 实现了ImportBeanDefinitionRegistrar就代表有动态注册BeanDefinition的能力
class JpaRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {}
public abstract class RepositoryBeanDefinitionRegistrarSupport
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry,
BeanNameGenerator generator) {
// ...
// 委托RepositoryConfigurationDelegate进行注册
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,
environment);
delegate.registerRepositoriesIn(registry, extension);
}
@Override
public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {
// 定义的扫描器
RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);
scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
scanner.setEnvironment(environment);
scanner.setResourceLoader(loader);
getExcludeFilters().forEach(scanner::addExcludeFilter);
// 查找满足条件的组件
return Streamable.of(() -> getBasePackages().stream()//
.flatMap(it -> scanner.findCandidateComponents(it).stream()));
}
}
public class RepositoryConfigurationDelegate {
// ...
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
RepositoryConfigurationExtension extension) {
Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configurations = extension
.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode);
}
}
public abstract class RepositoryConfigurationExtensionSupport implements RepositoryConfigurationExtension {
public <T extends RepositoryConfigurationSource> Collection<RepositoryConfiguration<T>> getRepositoryConfigurations(
T configSource, ResourceLoader loader, boolean strictMatchesOnly) {
// ...
// 调用RepositoryBeanDefinitionRegistrarSupport.getCandidates 获得合适的对象
for (BeanDefinition candidate : configSource.getCandidates(loader)) {}
// ...
}
}
class RepositoryComponentProvider extends ClassPathScanningCandidateComponentProvider {
public RepositoryComponentProvider(Iterable<? extends TypeFilter> includeFilters, BeanDefinitionRegistry registry) {
// ....
if (includeFilters.iterator().hasNext()) {
for (TypeFilter filter : includeFilters) {
addIncludeFilter(filter);
}
} else {
// 添加 是Repository接口的类
super.addIncludeFilter(new InterfaceTypeFilter(Repository.class));
// 添加定义了RepositoryDefinition注解的类
super.addIncludeFilter(new AnnotationTypeFilter(RepositoryDefinition.class, true, true));
}
// 排除带有NoRepositoryBean注解的类
addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));
}
@Override
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
// 这里就走到的Spring的findCandidateComponents 里面
// 关键的两个方法则是isCandidateComponent(metadataReader) 与 isCandidateComponent(sbd)
Set<BeanDefinition> candidates = super.findCandidateComponents(basePackage);
// ...
}
// 这样的话就能将Repository接口及@RepositoryDefinition扫描成BeanDefinition了
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
boolean isNonRepositoryInterface = !ClassUtils.isGenericRepositoryInterface(beanDefinition.getBeanClassName());
boolean isTopLevelType = !beanDefinition.getMetadata().hasEnclosingClass();
boolean isConsiderNestedRepositories = isConsiderNestedRepositoryInterfaces();
return isNonRepositoryInterface && (isTopLevelType || isConsiderNestedRepositories);
}
}
public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID>
implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T>, BeanClassLoaderAware,
BeanFactoryAware, ApplicationEventPublisherAware {
public void afterPropertiesSet() {
// ...
// 传入repositoryInterface来得到代理对象
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
// ...
}
}
public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, BeanFactoryAware {
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
// ...
// 这里就是最终创建代理对象的地方,会判断是JDK动态代理还是CGLIB代理
T repository = (T) result.getProxy(classLoader);
// ...
return repository;
}
}
@EnableJpaRepositories(basePackages = "com.mfyuan.repository")
@Import(JpaRepositoriesRegistrar.class)
JpaRepositoriesRegistrar
实现ImportBeanDefinitionRegistrar
拥有动态注册的能力与BeanDefinitionRegistryPostProcessor
一样- 自定义扫描器
RepositoryComponentProvide
添加扫描Repository
口及RepositoryDefinition
注解的includeFilter
及NoRepositoryBean
注解的excludeFilter
- 将扫描成功的获选Bean的信息创建成
BeanDefinition
并修改它的BeanClassName
(实际是JpaRepositoryFactoryBean
) JpaRepositoryFactoryBean
是一个BeanFactory
,会为我们创建出一个动态代理的对象并放入到IOC
容器中。