当前位置: 首页 > news >正文

安徽专业做网站的大公司快速的网站建设

安徽专业做网站的大公司,快速的网站建设,黄陌陌网站怎么做,建设工程管理是干嘛的我们在定义Repository的时候通常定义的时一个接口,而并没有去实现这个接口,那么Jpa是如何让开发者无需自己实现接口就可以使用Repository去操作数据库? 动态代理!!! Repository原理 试想一下JPA是如何做的…

我们在定义Repository的时候通常定义的时一个接口,而并没有去实现这个接口,那么Jpa是如何让开发者无需自己实现接口就可以使用Repository去操作数据库?
动态代理!!!

Repository原理

试想一下JPA是如何做的?

  1. 通过动态代理将接口实例化成对应的类。
  2. 解析方法名或根据指定的方法名
  3. 并根据得到的结果转换为数据库操作

尝试写一个示例

  • 自定义一个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;}@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));}

image.png
已经可以根据我们指定的类型来操作数据库了。

源码跟踪验证

@Test
public void test(){// 初始化IOC容器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JpaConfig.class);UserJpaRepository bean = applicationContext.getBean(UserJpaRepository.class);Optional<TUser> byId = bean.findById(5L);
}

能发现这里在IOC容器中的是代理对象。
image.png
可以明显的看出这里是使用的JDK动态代理。
image.png
这里可以看见实际上我们的对象是SimpleJpaRepository
image.png
实际调用findById也是通过EntityManage去操作数据库
image.png

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);// 尝试从容器中获取BeanUserCustomRepository bean = applicationContext.getBean(UserCustomRepository.class);
}

image.png

为什么还是拿不到这个Bean呢?
因为Repository是接口不是类,不会被注册到IOC容器中。在扫描成BeanDefinition是被过滤掉了。
ConfigurationClassParser#doProcessConfigurationClass
ComponentScanAnnotationParser#parse 处理@Component的解析
ClassPathBeanDefinitionScanner#doScan
#findCandidateComponents
#scanCandidateComponents
#isCandidateComponent(metadataReader) 判断是否包含@Component注解
image.png
#isCandidateComponent(sbd) 判断是否为接口或者抽象类
image.png
:::
如何解决不扫描接口的问题呢?

  1. ClassPathBeanDefinitionScanner:自定义一个扫描器(让接口也可以别扫描到)。
  2. 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,但是又遇到一个问题?
image.png
与之前的错误不同的是已经不再提前找不到对应类型的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 {// .....@SneakyThrows@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {// 这里拿到的就行自定义扫描器 扫出来的所有BeanDefinitionSet<BeanDefinitionHolder> 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里面的方法了。

image.png解释

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 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));}@Overridepublic 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;}@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 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;}
}

  1. @EnableJpaRepositories(basePackages = "com.mfyuan.repository")
  2. @Import(JpaRepositoriesRegistrar.class)
  3. JpaRepositoriesRegistrar实现ImportBeanDefinitionRegistrar拥有动态注册的能力与BeanDefinitionRegistryPostProcessor一样
  4. 自定义扫描器RepositoryComponentProvide添加扫描Repository口及RepositoryDefinition注解的includeFilterNoRepositoryBean注解的excludeFilter
  5. 将扫描成功的获选Bean的信息创建成BeanDefinition并修改它的BeanClassName(实际是JpaRepositoryFactoryBean
  6. JpaRepositoryFactoryBean是一个BeanFactory,会为我们创建出一个动态代理的对象并放入到IOC容器中。
http://www.yayakq.cn/news/823822/

相关文章:

  • 房地产网站编辑华为官网商城手机价格
  • 网站设置了自动登录怎么显示密码有没有资源可以在线观看
  • 长春企业自助建站系统网站上传大文件
  • 住房和城乡建设部网站执业资格注册中心谷歌seo和百度区别
  • seo网站优化及网站推广外贸实用工具
  • 网站在线考试答题系统怎么做衡阳市本地新闻头条
  • 网站建设优化推广安徽p2p网站制作价格
  • 大厂建设局网站wordpress新闻类模板
  • dz论坛做视频网站教程企业网站建设的劣势
  • 佛山做外贸网站的学做窗帘的网站
  • 陕西交通建设集团西商分公司网站汽车网页设计欣赏
  • 网页欣赏网站广告宣传方式有哪些
  • 做网站非法吗网页设计与制作课程介绍
  • 比较优秀的国外wordpress网站苏州手机网站开发公司
  • 公司建设网站需求分析报告施工企业如何获取竞争优势
  • 贵阳国家经济技术开发区门户网站刷网站百度关键词软件
  • 绵阳市网站建设郑州投资网站建设
  • 手机app界面设计网站百度数据指数
  • 免费微网站建站系统源码深圳网站建设制作设计
  • 企业网站域名在哪申请厂字型布局网站例子
  • 东营网站搭建嵌入式软件开发工程师简历
  • 个人网站开发制作教程门户网站开发人员招聘信息
  • 为学校网站建设珠海九洲旅游开发公司
  • 企业站网页制作实训步骤网站维护与建设实训心得
  • 宿迁网站定制手举牌战队图片在线制作
  • 一个小程序制作价格百度关键词优化外包
  • 电子商务网站建设移动电商开发个体户查询系统官网
  • 沧州地区阿里巴巴做网站网站建设是
  • seo网站排名查询wordpress多语好利弊
  • 企业网站带商城源码我有服务器和模板怎么做网站