谷歌外贸建站,长沙市住房和城乡建设局网站,诸城市房产信息网,锦州做网站的公司我们在定义Repository的时候通常定义的时一个接口#xff0c;而并没有去实现这个接口#xff0c;那么Jpa是如何让开发者无需自己实现接口就可以使用Repository去操作数据库#xff1f; 动态代理#xff01;#xff01;#xff01; Repository原理
试想一下JPA是如何做的… 我们在定义Repository的时候通常定义的时一个接口而并没有去实现这个接口那么Jpa是如何让开发者无需自己实现接口就可以使用Repository去操作数据库 动态代理 Repository原理
试想一下JPA是如何做的
通过动态代理将接口实例化成对应的类。解析方法名或根据指定的方法名并根据得到的结果转换为数据库操作
尝试写一个示例
自定义一个Repository
public interface CustomRepositoryT, ID extends RepositoryT, ID {/*** 查询前num条数据* param num* return*/ListT 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;}Overridepublic 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;}
}测试下创建出来的代理对象是否能正常运行
Testpublic void test() {// 初始化IOC容器AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(JpaConfig.class);LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean applicationContext.getBean(LocalContainerEntityManagerFactoryBean.class);// 创建EntityManagerEntityManager 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);OptionalTUser byId bean.findById(5L);
}能发现这里在IOC容器中的是代理对象。 可以明显的看出这里是使用的JDK动态代理。 这里可以看见实际上我们的对象是SimpleJpaRepository 实际调用findById也是通过EntityManage去操作数据库
Spring是如何整合Jpa呢
疑问
怎么知道要注册那些Repository
ComponentScanComponent
// 添加
ComponentScan(basePackages com.mfyuan)
public class JpaConfig{}// 添加
Component
public interface UserCustomRepository extends CustomRepositoryTUser, Long {}Test
public void test1() {// 初始化IOC容器AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(JpaConfig.class);// 尝试从容器中获取BeanUserCustomRepository 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*/Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {AnnotationMetadata metadata beanDefinition.getMetadata();return metadata.isInterface();}
}Component
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {Overridepublic 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);}Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinitionRegistryPostProcessor.super.postProcessBeanFactory(beanFactory);}
}到现在我们的接口就可以被扫描成BeanDefinition但是又遇到一个问题 与之前的错误不同的是已经不再提前找不到对应类型的Bean了而是接口不能实例化的。 :::
如何将对应的Repository接口注入到容器中 可以将通过动态代理后的对象放入到容器中这里就解决了接口不能实例化的问题。 但是不可能一个一个去创建动态代理的对象吧这样想想就很痛苦。 使用FactoryBean来定义创建过程。与前面Repository原理结合。 :::
Data
public class CustomRepositoryFactoryBean implements FactoryBean {private Class? repositoryInterface;Autowiredprivate LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean;public CustomRepositoryFactoryBean(Class? repositoryInterface) {this.repositoryInterface repositoryInterface;}/*** FactoryBean的特性这个方法的返回值是注册到容器中对象*/Overridepublic Object getObject() {// 创建EntityManagerEntityManager 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的特性这个方法的返回值是注册到容器的对象类型*/Overridepublic Class? getObjectType() {return repositoryInterface;}
}重写CustomClassPathBeanDefinitionScanner#doScan 对扫描到的BeanDefinition进行修改
public class CustomClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {// .....SneakyThrowsOverrideprotected SetBeanDefinitionHolder doScan(String... basePackages) {// 这里拿到的就行自定义扫描器 扫出来的所有BeanDefinitionSetBeanDefinitionHolder beanDefinitionHolders super.doScan(basePackages);for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {ScannedGenericBeanDefinition beanDefinition (ScannedGenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();// 得到扫描到的repositoryClassString repositoryClassName beanDefinition.getBeanClassName();// 设置BeanDefinition的Class为FactoryBean// mybatis 这里也是这样做的 只不过是新注册了一个BeanDefinitionbeanDefinition.setBeanClass(CustomRepositoryFactoryBean.class);// 添加一个构造参数beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(repositoryClassName);}return beanDefinitionHolders;}
}测试
Test
public void test1() {// 初始化IOC容器AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(JpaConfig.class);// 尝试从容器中获取BeanUserCustomRepository bean applicationContext.getBean(UserCustomRepository.class);System.out.println(bean.findByFirst(5L));
}能够走自定义Repository里面的方法了。
解释
CustomRepository相当于一个顶层接口他去定义对应的统一的数据库操作。类似JpaRepositoryCrudRepository等 CustomRepositoryInvocationHandler相当于是对顶层接口所有的方法进行了一个实现。类似SimpleJpaRepository
源码验证
// 导入JpaRepositoriesRegistrar
Import(JpaRepositoriesRegistrar.class)
public interface EnableJpaRepositories {}// 实现了ImportBeanDefinitionRegistrar就代表有动态注册BeanDefinition的能力
class JpaRepositoriesRegistrar extends RepositoryBeanDefinitionRegistrarSupport {}public abstract class RepositoryBeanDefinitionRegistrarSupportimplements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry,BeanNameGenerator generator) {// ...// 委托RepositoryConfigurationDelegate进行注册RepositoryConfigurationDelegate delegate new RepositoryConfigurationDelegate(configurationSource, resourceLoader,environment);delegate.registerRepositoriesIn(registry, extension);}Overridepublic StreamableBeanDefinition 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 ListBeanComponentDefinition registerRepositoriesIn(BeanDefinitionRegistry registry,RepositoryConfigurationExtension extension) {CollectionRepositoryConfigurationRepositoryConfigurationSource configurations extension.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode);}
}public abstract class RepositoryConfigurationExtensionSupport implements RepositoryConfigurationExtension {public T extends RepositoryConfigurationSource CollectionRepositoryConfigurationT 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));}Overridepublic SetBeanDefinition findCandidateComponents(String basePackage) {// 这里就走到的Spring的findCandidateComponents 里面// 关键的两个方法则是isCandidateComponent(metadataReader) 与 isCandidateComponent(sbd)SetBeanDefinition 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;}Overrideprotected 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 RepositoryFactoryBeanSupportT extends RepositoryS, ID, S, IDimplements InitializingBean, RepositoryFactoryInformationS, ID, FactoryBeanT, 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(ClassT 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实际是JpaRepositoryFactoryBeanJpaRepositoryFactoryBean是一个BeanFactory会为我们创建出一个动态代理的对象并放入到IOC容器中。