美文网首页商通贷技术分享
spring-jpa 源码学习(1)—加载流程简介

spring-jpa 源码学习(1)—加载流程简介

作者: 爱编程的凯哥 | 来源:发表于2019-04-21 20:25 被阅读0次

下一篇: spring jpa 源码学习(2)—调用流程简介

写在前边:

作为mybatis的拥护者,一直对于jpa、hibernate这种对象全映射的orm框架不甚理睬.但难免会有工作需要必须使用的时候,所以抽时间看了下相关实现,还是在帮助自己加深理解orm模型上有很大帮助的.

分析

  • 启动流程
  1. 先根据springboot的自动加载机制进行加载jps自动化配置


    image.png
  2. 看下JpaRepositoriesAutoConfiguration类

@Configuration
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class,
        JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration {

}
  1. 根据spring的import标签,我们找到JpaRepositoriesAutoConfigureRegistrar此注册类,进行respositor的注册.看下此类的bean结构


    image.png

我们看到了ImportBeanDefinitionRegistrar这个熟悉的spring中bean注册类,此类的作用和BeanDefinitionRegistryPostProcessor类似,都是可以自定义解析生成自己的bean定义,其核心方法是

  public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

看下jpa中的实现:

@Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
            BeanDefinitionRegistry registry) {
        new RepositoryConfigurationDelegate(getConfigurationSource(registry),
                this.resourceLoader, this.environment).registerRepositoriesIn(registry,
                        getRepositoryConfigurationExtension());
    }

看到RepositoryConfigurationDelegate类,'Delegate'明显的委派器模式,其中getConfigurationSource(registry)方法可以跟进去看到

return new AnnotationRepositoryConfigurationSource(metadata, getAnnotation(),
                this.resourceLoader, this.environment, beanDefinitionRegistry) {
            @Override
            public Streamable<String> getBasePackages() {
                return AbstractRepositoryConfigurationSourceSupport.this
                        .getBasePackages();
            }
        };

此处getBasePackages获取所有spring环境加载的包路径,看下最后调用BasePackages类的get方法,获取spring的扫描的包路径(spring的bean加载也依赖于此处),如我的demo为springboot环境,basepackage就是启动类的同级目录(常识,不了解的同学可脑补下哈)

static final class BasePackages {

        private final List<String> packages;

        private boolean loggedBasePackageInfo;

        BasePackages(String... names) {
            List<String> packages = new ArrayList<>();
            for (String name : names) {
                if (StringUtils.hasText(name)) {
                    packages.add(name);
                }
            }
            this.packages = packages;
        }

        public List<String> get() {
            if (!this.loggedBasePackageInfo) {
                if (this.packages.isEmpty()) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("@EnableAutoConfiguration was declared on a class "
                                + "in the default package. Automatic @Repository and "
                                + "@Entity scanning is not enabled.");
                    }
                }
                else {
                    if (logger.isDebugEnabled()) {
                        String packageNames = StringUtils
                                .collectionToCommaDelimitedString(this.packages);
                        logger.debug("@EnableAutoConfiguration was declared on a class "
                                + "in the package '" + packageNames
                                + "'. Automatic @Repository and @Entity scanning is "
                                + "enabled.");
                    }
                }
                this.loggedBasePackageInfo = true;
            }
            return this.packages;
        }

    }

还有一个比较重要点是getRepositoryConfigurationExtension()此处采用一种扩展方式,方便以后不同的扩展点实现,此处默认返回的是在JpaRepositoriesAutoConfigureRegistrar定义的JpaRepositoryConfigExtension,jpa扩展点

@Override
    protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
        return new JpaRepositoryConfigExtension();
    }

看下JpaRepositoryConfigExtension的部分代码

@Override
    public String getRepositoryFactoryBeanClassName() {
        return JpaRepositoryFactoryBean.class.getName();
    }

返回的是JpaRepositoryFactoryBean此工厂类,此类为生成所有dao层接口代理类的核心工厂.

  1. 下面进入核心的委派器RepositoryConfigurationDelegate,其核心方法:
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
            RepositoryConfigurationExtension extension) {

        extension.registerBeansForRoot(registry, configurationSource);

        RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader,
                environment);
        List<BeanComponentDefinition> definitions = new ArrayList<>();

        //1.核心点,获取所有需要注册点dao接口定义,创建bean定义
        for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : extension
                .getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)) {
            
            //2.类定义构造器
            BeanDefinitionBuilder definitionBuilder = builder.build(configuration);

            extension.postProcess(definitionBuilder, configurationSource);
            if (isXml) {
                extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
            } else {
                extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
            }

            AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
            String beanName = configurationSource.generateBeanName(beanDefinition);

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName,
                        configuration.getRepositoryInterface(), configuration.getRepositoryFactoryBeanClassName());
            }
            
          
            beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
            //3.制定beanFactory类,放入spring环境中
            registry.registerBeanDefinition(beanName, beanDefinition);
            definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
        }

        return definitions;
    }

如上,我标注了三部分核心代码,我们简单分析下:

==第一处:extension.getRepositoryConfigurations(configurationSource,resourceLoader, inMultiStoreMode)==
核心含义是找到所有候选的dao接口对象,其中有个for循环有必要说一下:

==for (BeanDefinition candidate : configSource.getCandidates(loader))==

这里循环的是什么呢?看下 configSource.getCandidates()-->获取所有需要加载的候选者定义,

public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {

        RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);
        scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
        scanner.setEnvironment(environment);
        scanner.setResourceLoader(loader);

        getExcludeFilters().forEach(it -> scanner.addExcludeFilter(it));

        return Streamable.of(() -> getBasePackages().stream()//
                .flatMap(it -> scanner.findCandidateComponents(it).stream()));
    }

看出RepositoryComponentProvider这个scanner负责扫描所有类,其内部添加了相关的filter

if (includeFilters.iterator().hasNext()) {
            for (TypeFilter filter : includeFilters) {
                addIncludeFilter(filter);
            }
        } else {
            super.addIncludeFilter(new InterfaceTypeFilter(Repository.class));
            super.addIncludeFilter(new AnnotationTypeFilter(RepositoryDefinition.class, true, true));
        }

核心逻辑是添加注解和Repository接口定义两个过滤器,然后从classpath环境中找到所有候选类.

==第二处:builder.build(configuration)==
此处将会将上一步获取到到接口类型包装成jpa工厂类到bean定义,如下:

public BeanDefinitionBuilder build(RepositoryConfiguration<?> configuration) {

    .....
        //此处获取到factorybeanName根据前面到扩展点中定义为JpaRepositoryFactoryBean
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .rootBeanDefinition(configuration.getRepositoryFactoryBeanClassName());

        builder.getRawBeanDefinition().setSource(configuration.getSource());
        //添加构造方法参数
        builder.addConstructorArgValue(configuration.getRepositoryInterface());
        builder.addPropertyValue("queryLookupStrategyKey", configuration.getQueryLookupStrategyKey());
        builder.addPropertyValue("lazyInit", configuration.isLazyInit());

这样上一步获取到到接口类信息,都转换为JpaRepositoryFactoryBean对应到工厂bean定义.

==第三处:将刚才生成到所有工厂bean定义放入spring注册工厂中,等待spring调用==

  1. 下面逻辑将会进去JpaRepositoryFactoryBean类中,闯进dao接口的真正代理类,先看下此类的依赖关系:


    image.png

可以看到熟悉的InitializingBean、FactoryBean、BeanFactoryAware..等spring等扩展类,根据InitializingBean等特性,我们重点看afterPropertiesSet方法

public void afterPropertiesSet() {
        //1.创建repositoryFactory,后续等实例bean由此工厂创造
        this.factory = createRepositoryFactory();
        this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
        this.factory.setNamedQueries(namedQueries);
        this.factory.setEvaluationContextProvider(evaluationContextProvider);
        this.factory.setBeanClassLoader(classLoader);
        this.factory.setBeanFactory(beanFactory);

        if (publisher != null) {
            this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
        }

        repositoryBaseClass.ifPresent(this.factory::setRepositoryBaseClass);

        RepositoryFragments customImplementationFragment = customImplementation //
                .map(RepositoryFragments::just) //
                .orElseGet(RepositoryFragments::empty);

        RepositoryFragments repositoryFragmentsToUse = this.repositoryFragments //
                .orElseGet(RepositoryFragments::empty) //
                .append(customImplementationFragment);

        this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
        //2.创建对应的respository
        this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));

        if (!lazyInit) {
            this.repository.get();
        }
    }

跟下这段代码

1. createRepositoryFactory()创建repository工厂,看下核心逻辑,最后返回JpaRepositoryFactory工厂:

protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new JpaRepositoryFactory(entityManager);
    }
返回工厂类,核心的实现类entityManager也在此时注入,看下这个的图谱
image.png

其中最底层的SessionImpl是我们比较直观的一个实现,但实际常用的我们会发现其实是通过SharedEntityManagerCreator创建对应的代理类实现

    public static EntityManager createSharedEntityManager(EntityManagerFactory emf, @Nullable Map<?, ?> properties,
            boolean synchronizedWithTransaction, Class<?>... entityManagerInterfaces) {

        ClassLoader cl = null;
        if (emf instanceof EntityManagerFactoryInfo) {
            cl = ((EntityManagerFactoryInfo) emf).getBeanClassLoader();
        }
        Class<?>[] ifcs = new Class<?>[entityManagerInterfaces.length + 1];
        System.arraycopy(entityManagerInterfaces, 0, ifcs, 0, entityManagerInterfaces.length);
        ifcs[entityManagerInterfaces.length] = EntityManagerProxy.class;
        return (EntityManager) Proxy.newProxyInstance(
                (cl != null ? cl : SharedEntityManagerCreator.class.getClassLoader()),
                ifcs, new SharedEntityManagerInvocationHandler(emf, properties, synchronizedWithTransaction));
    }

这块逻辑细扣的话就是hibernate对注解的@Entity(name="role")的相关实现,我们先看主线,此时代理类生成的就是invokeHandler是SharedEntityManagerInvocationHandler,所以实际sql执行的时候,最后都是从此handler进行执行的,具体在后面会做介绍,我们先把此afterPropertiesSet分析完

  1. this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse))-->去创建对应的实现方法(当然这里有Lazy类,对应this.repository.get()方法才会真正调用,相当于实现了懒加载逻辑),看下核心代码,代码主要做了
    1. 获取默认的SimpleJpaRepository类,然后对其加强,生成代理类,此类中jpa通用的方法都以包括在内.
    2. 生成对SimpleJpaRepository代理类,然后进行各种加强
    3. 重点QueryExecutorMethodInterceptor将dao的每一个方法转化为Query类,对用户自定义类要进行包装增强
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {

        Assert.notNull(repositoryInterface, "Repository interface must not be null!");
        Assert.notNull(fragments, "RepositoryFragments must not be null!");

        RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
        RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
        RepositoryInformation information = getRepositoryInformation(metadata, composition);

        validate(information, composition);

        //1.此处生成默认的jpa实现SimpleJpaRepository类
        Object target = getTargetRepository(information);

        // Create proxy
        ProxyFactory result = new ProxyFactory();
        result.setTarget(target);
        result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);

        if (MethodInvocationValidator.supports(repositoryInterface)) {
            result.addAdvice(new MethodInvocationValidator());
        }

        result.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
        result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
        
        //2.各种加强
        postProcessors.forEach(processor -> processor.postProcess(result, information));
    
        result.addAdvice(new DefaultMethodInvokingMethodInterceptor());

        ProjectionFactory projectionFactory = getProjectionFactory(classLoader, beanFactory);
        
        //3.重点逻辑,将dao的每一个方法转化为Query类
        result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory));

        composition = composition.append(RepositoryFragment.implemented(target));
        result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));

        //返回最终的代理类
        return (T) result.getProxy(classLoader);
    }

看下QueryExecutorMethodInterceptor这个类:

    public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation,
                ProjectionFactory projectionFactory) {

            this.resultHandler = new QueryExecutionResultHandler();

            Optional<QueryLookupStrategy> lookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
                    RepositoryFactorySupport.this.evaluationContextProvider);

            if (!lookupStrategy.isPresent() && repositoryInformation.hasQueryMethods()) {

                throw new IllegalStateException("You have defined query method in the repository but "
                        + "you don't have any query lookup strategy defined. The "
                        + "infrastructure apparently does not support query methods!");
            }
            
            //重点mapMethodsToQuery方法,进行方法对应Query类转化
            this.queries = lookupStrategy //
                    .map(it -> mapMethodsToQuery(repositoryInformation, it, projectionFactory)) //
                    .orElse(Collections.emptyMap());
        }

此处mapMethodsToQuery方法细节不再展开,有兴趣可以RepositoryFactorySupport在找到lookupQuery方法,跟下去,此方法将生成每个dao方法对应RepositoryQuery类,然后返回,其中有个重要逻辑在AbstractSharedSessionContract类的createQuery(String queryString)方法,将会构造最重要的QueryImpl类

    @Override
    public QueryImplementor createQuery(String queryString) {
        checkOpen();
        checkTransactionSynchStatus();
        delayedAfterCompletion();

        try {
            final QueryImpl query = new QueryImpl(
                    this,
                    getQueryPlan( queryString, false ).getParameterMetadata(),
                    queryString
            );
            query.setComment( queryString );
            applyQuerySettingsAndHints( query );
            return query;
        }
        catch (RuntimeException e) {
            throw exceptionConverter.convert( e );
        }
    }

好了,到这里jpa加载过程主要核心的逻辑就梳理大概梳理完了,总结下:

  1. JpaRepositoriesAutoConfiguration类通过配置文件自动加载
  2. import标签引入JpaRepositoriesAutoConfigureRegistrar类,加载所有的dao类定义
  3. 通过将JpaRepositoryFactoryBean工厂类注入spring上下文中,调用其afterPropertiesSet方法进行实际类产出.
  4. afterPropertiesSet方法中传教repositoryFactory工厂,注入实际数据库操作的EntityMananger类,此类实际为SharedEntityManagerCreator创造的代理类,核心逻辑在SharedEntityManagerInvocationHandler中
  5. 最后,通过上面的工厂类,创建dao中每个方法对应的Query对象,用来后续实际查询使用.

相关文章

网友评论

    本文标题:spring-jpa 源码学习(1)—加载流程简介

    本文链接:https://www.haomeiwen.com/subject/hkydgqtx.html