SpringDataJpa源码分析

我们在定义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;
    }

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

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);
    // 尝试从容器中获取Bean
    UserCustomRepository 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
     */
    @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,但是又遇到一个问题?
image.png
与之前的错误不同的是已经不再提前找不到对应类型的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里面的方法了。

image.png解释

CustomRepository:相当于一个顶层接口,他去定义对应的统一的数据库操作。类似JpaRepositoryCrudRepository
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;
	}
}

  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容器中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

假女吖☌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值