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的建立過程,
- AbstractRepositoryConfigurationSourceSupport的registerBeanDefinitions為切入點
- RepositoryConfigurationDelegate定義建立步驟
- JpaRepositoryConfigExtension通過BeanDefinitionBuilder指定了EntityManager的具體建立方法
以上涉及到的相關類,用一張類圖展示
EntityManager是為誰建立的呢,創建出來後又是如何使用的呢,下一篇文章繼續分析。