Spring Data之EntityManager創建及源碼分析

背景

前一篇文章介紹了EntityManagerFactory的創建過程,有了EntityManagerFactory接下來就是要獲取EntityManager了,但EntityManager的創建不再是通過@Conditional註解,而是使用的@PersistenceContext註解,那麼Spring Data是如何識別@PersistenceContext註解,並注入EntityManager實例的,又是怎樣通過上一步創建的EntityManagerFactory來獲取EntityManager的呢?這篇文章就來分析這兩個問題。

動態註冊Bean

日常開發中我們一般會在類上使用@Service、@Component或者在方法上使用@Bean註解,被註解的類或者方法的返回值的對象實例會被Spring維護到ApplicationContext中,在使用的時可以通過@Autowired、@Resource註解的類變量自動注入對象實例。

如果不使用註解,通過編程的方式也能創建一個對象實例交由ApplicationContext管理。

BeanDefinitionBuilder

 /**
 * Description:通過編碼的方式創建一個Bean實例
 */
public class BeanDefinitionBuilderTest {

    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        //使用builder構建一個MyBean的Bean定義
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(MyBean.class);

        //爲name屬性賦值,會默認調用setName方法,如果沒有setName方法會報錯
        builder.addPropertyValue("name","張三");

        //往BeanFactory註冊beanName爲myBean的Bean定義
        beanFactory.registerBeanDefinition("myBean", builder.getRawBeanDefinition());

        //獲取剛纔註冊的Bean
        MyBean myBean = beanFactory.getBean(MyBean.class);
        myBean.say();
    }

    private static class MyBean{
        private String name;

        public void setName(String name) {
            this.name = name;
        }

        public void say(){
            System.out.println("MyBean name is "+ name);
        }
    }
}

控制檯輸出

MyBean name is 張三

可以看到MyBean並未使用任何註解,而是通過BeanDefinitionBuilder創建了一個MyBean的實例並註冊到beanFactory中,再從beanFactory獲取出來調用say方法,爲name設置的值被正確輸出。

BeanFactoryPostProcessor

/**
 * Description:通過實現BeanFactoryPostProcessor接口創建對象實例
 *
 * @author fangliangsheng
 * @date 2018/11/18
 */
public class BeanFactoryPostProcessorTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        MyBean myBean = context.getBean(MyBean.class);

        myBean.say();
    }

    @Configuration
    public static class MyConfig{

        @Bean
        public MyConfigBean myConfigBean(){
            return new MyConfigBean();
        }
        
    }

    private static class MyConfigBean implements BeanFactoryPostProcessor{

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(MyBean.class);
            builder.addPropertyValue("name","張三");

            ((DefaultListableBeanFactory) beanFactory)
                    .registerBeanDefinition("myBean", builder.getRawBeanDefinition());
        }
    }

    private static class MyBean{
        private String name;

        public void setName(String name) {
            this.name = name;
        }

        public void say(){
            System.out.println("MyBean name is "+ name);
        }
    }
}

控制檯輸出

MyBean name is 張三

BeanFactoryPostProcessor接口只有一個方法postProcessBeanFactory,該方法的調用時機是Application Context的Bean Factory初始化完成後。

InstantiationAwareBeanPostProcessor

private static class MyConfigBean3 implements InstantiationAwareBeanPostProcessor{
        @Override
        public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
            if (bean instanceof MyBean){
                System.out.println("set property value:" + beanName +" "+ pvs.getPropertyValues()[0].getName() + " "+pvs.getPropertyValues()[0].getValue());
            }

            return pvs;
        }
    }

控制檯輸出

set property value:myBean name 張三

InstantiationAwareBeanPostProcessor接口能夠攔截對象實例化時對屬性的賦值操作,實現該接口並判斷想要監控的類及屬性,在注入值時可以實現自己想要的邏輯。例如根據一個配置再賦值時選擇不同的實例,或者通過編程的方式再動態創建一個實例。

這裏介紹BeanDefinitionBuilder、BeanFactoryPostProcessor、InstantiationAwareBeanPostProcessor主要是因爲在創建EntityManager時Spring Data就是通過這些接口的協作完成的。

創建EntityManager

先看自動配置的入口類

@Configuration
//當存在DataSource實例和JpaRepository類時生效
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
//當還沒有JpaRepositoryFactoryBean和JpaRepositoryConfigExtension實例時生效
//因爲後面會創建這兩個類的實例,意思就是不能重複創建
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class,
		JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)
//條件校驗通過後引入JpaRepositoriesAutoConfigureRegistrar
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
//在HibernateJpaAutoConfiguration之後自動配置,這點很關鍵,因爲要現有EntityManagerFactory,這步的EntityManager才能創建
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration {

}

Import的JpaRepositoriesAutoConfigureRegistrar繼承了AbstractRepositoryConfigurationSourceSupport,我們先看這個抽象類。
在這裏插入圖片描述
該抽象類有很多子類,看到類名是不是有點眼熟,這些就是Spring JPA支持的持久化實現。

public abstract class AbstractRepositoryConfigurationSourceSupport
		implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware,
		EnvironmentAware {
		
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
			BeanDefinitionRegistry registry) {
		new RepositoryConfigurationDelegate(getConfigurationSource(registry),
				this.resourceLoader, this.environment).registerRepositoriesIn(registry,
						getRepositoryConfigurationExtension());
	}
}

它繼承了ImportBeanDefinitionRegistrar接口實現了registerBeanDefinitions方法,這是一個重要的切入點。registerBeanDefinitions方法的第二個入參是BeanDefinitionRegistry,通過這個registry接口就可以往Bean Factory註冊各種實例了。具體的創建是交給RepositoryConfigurationDelegate待辦的。

/**
* 定義Repository的創建步驟
* 通過RepositoryConfigurationSource的配置來源和RepositoryConfigurationExtension的具體實現
*/
public class RepositoryConfigurationDelegate {

	private final RepositoryConfigurationSource configurationSource;

	/**
	 * 將找到的repositories註冊到BeanDefinitionRegistry中
	 * 類似於模版方法
	 */
	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<>();

		//找到Repository接口的子類並封裝爲RepositoryConfiguration
		//Repository的子類也就是我們平時開發所定義的例如UserRepository
		for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : extension
				.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)) {

			//將configuration轉化爲BeanDefinitionBuilder,用於創建具體的實例
			BeanDefinitionBuilder definitionBuilder = builder.build(configuration);

			//EntityManager如何創建的答案就在這個地方了
			//這裏extension的子類是JpaRepositoryConfigExtension
			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());

			registry.registerBeanDefinition(beanName, beanDefinition);
			definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
		}

		return definitions;
	}
}

接下來看下JpaRepositoryConfigExtension的實現

public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensionSupport
	//該方法是在上一個類的registerRepositoriesIn中調用的
	//給builder指定類entityManager屬性的賦值方法
	@Override
	public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) {

		Optional<String> transactionManagerRef = source.getAttribute("transactionManagerRef");
		builder.addPropertyValue("transactionManager", transactionManagerRef.orElse(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME));
		builder.addPropertyValue("entityManager", getEntityManagerBeanDefinitionFor(source, source.getSource()));
		builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME);
	}

	//通過BeanDefinitionBuilder定義了SharedEntityManagerCreator的createSharedEntityManager
	//爲entityManager的創建方法
	private static AbstractBeanDefinition getEntityManagerBeanDefinitionFor(RepositoryConfigurationSource config,
			@Nullable Object source) {

		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.rootBeanDefinition("org.springframework.orm.jpa.SharedEntityManagerCreator");
		builder.setFactoryMethod("createSharedEntityManager");
		builder.addConstructorArgReference(getEntityManagerBeanRef(config));

		AbstractBeanDefinition bean = builder.getRawBeanDefinition();
		bean.setSource(source);

		return bean;
	}
}

結合動態註冊Bean章節的內容,可以知道BeanDefinitionBuilder的作用。所以EntityManager的實際創建是通過SharedEntityManagerCreator的createSharedEntityManager方法。以上只是定義好了BeanDefinitionBuilder,builder中屬性的賦值操作是對象實際實例時調用的。

接下來我們再看PersistenceAnnotationBeanPostProcessor,該類繼承了InstantiationAwareBeanPostProcessor實現了postProcessPropertyValues方法,在動態註冊Bean章節中,介紹了postProcessPropertyValues的作用。

public class PersistenceAnnotationBeanPostProcessor
		implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor,
		MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable{

    //攔截實例化對象時的賦值操作
	@Override
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {

		//找到@PersistenceContext、@PersistenceUnit註解的方法
		InjectionMetadata metadata = findPersistenceMetadata(beanName, bean.getClass(), pvs);
		try {
			metadata.inject(bean, beanName, pvs);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of persistence dependencies failed", ex);
		}
		return pvs;
	}
}

PersistenceAnnotationBeanPostProcessor的作用是實現PersistenceContext和PersistenceUnit註解的作用,即JPA規範定義通過@PersistenceContext註解的方法或者屬性將會注入EntityManger實例,@PersistenceUnit註解的方法或者屬性將會注入EntityManagerFactory實例.

結束

這篇文章主要分析了EntityManger的創建過程,

  1. AbstractRepositoryConfigurationSourceSupport的registerBeanDefinitions爲切入點
  2. RepositoryConfigurationDelegate定義創建步驟
  3. JpaRepositoryConfigExtension通過BeanDefinitionBuilder指定了EntityManager的具體創建方法

以上涉及到的相關類,用一張類圖展示
在這裏插入圖片描述

EntityManager是爲誰創建的呢,創建出來後又是如何使用的呢,下一篇文章繼續分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章