概述
PersistenceAnnotationBeanPostProcessor
是Spring
提供的用於處理註解@PersistenceUnit
和@PersistenceContext
的BeanPostProcessor
。用於註解相應的JPA
資源:EntityManagerFactory
和EntityManager
(或者它們的子類變量)。
注意 : 在目前的實現中,
PersistenceAnnotationBeanPostProcessor
僅僅支持@PersistenceUnit
和@PersistenceContext
上帶有屬性unitName
或者不帶任何屬性(比如使用缺省persistence unit
的情況)。如果這些註解使用在類上,並且使用了屬性name
,這些註解會被忽略,因爲它們此時僅僅作爲部署提示(參考Java EE
規範)。
該BeanPostProcessor
從如下渠道獲取EntityManagerFactory
對象 :
Spring
應用上下文定義的bean
對象(缺省情況)
這種情況下,
EntityManagerFactory
通常由org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
創建。
JNDI
這種情況下,通常會使用
jee:jndi-lookup
XML
配置元素,bean
名稱用於匹配被請求的persistence unit
名稱。
基於XML
配置時,使用context:annotation-config
或者context:component-scan
時會註冊一個缺省的PersistenceAnnotationBeanPostProcessor
。而基於註解的應用,比如Springboot
應用,如果使用了JPA
,應用也會缺省自動註冊一個PersistenceAnnotationBeanPostProcessor
。如果你想指定一個自定義的PersistenceAnnotationBeanPostProcessor
的,需要刪除或者關閉相應這樣的XML
配置或者註解。
Springboot JPA
應用中,PersistenceAnnotationBeanPostProcessor
的註冊參考工具類方法AnnotationConfigUtils#registerAnnotationConfigProcessors
:// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); }
源代碼分析
發現某個bean
的持久化注入元數據
開發人員,或者框架自身的某個部分,可能在某些bean
上通過如下方式注入了持久化有關的bean
:
- 基於成員屬性的注入
@PersistenceUnit
EntityManagerFactory entityManagerFactory;
@PersistenceContext
EntityManager entityManager;
- 基於成員屬性設置方法的注入
@PersistenceUnit
public void setEntityManagerFactory(EntityManager entityManagerFactory) {
this.entityManagerFactory= entityManagerFactory;
}
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
對於在bean
上的這些持久化注入的信息,就是由PersistenceAnnotationBeanPostProcessor
來處理的,但是,PersistenceAnnotationBeanPostProcessor
是怎樣發現這些信息的呢 ?這一小節,我們來回答這一問題。
首先,PersistenceAnnotationBeanPostProcessor
實現了接口MergedBeanDefinitionPostProcessor
,該接口約定了bean
創建時的生命週期回調方法postProcessMergedBeanDefinition
,該方法會在每個bean
創建過程中,依賴注入進行之前被調用。那麼對於PersistenceAnnotationBeanPostProcessor
而言,它的這個方法又做了哪些事情呢 ?
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType,
String beanName) {
// 獲取當前被創建的bean的持久化屬性元數據
InjectionMetadata metadata = findPersistenceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
上面方法postProcessMergedBeanDefinition
主要邏輯是通過方法findPersistenceMetadata
發現正在創建的bean
的持久化元數據。findPersistenceMetadata
實現如下 :
private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?> clazz,
@Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
// 從緩存this.injectionMetadataCache中查找針對該bean的持久化元數據,看是否已經獲得並緩存,
// 返回 null 的話表示尚未獲得和緩存過
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// metadata ==null 的話InjectionMetadata.needsRefresh()會返回true
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
// 如果 metadata ==null 表明尚未獲得和緩存過,
// 此方法在postProcessMergedBeanDefinition裏面執行的話顯然會走到這裏,
// 而在 postProcessProperties 裏面執行的話顯然不會走到這裏
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
// 根據 bean class 構建持久化元數據對象 InjectionMetadata ,
// 所使用的 InjectedElement 實現類是 PersistenceElement,
// 一個當前類的內嵌類定義
metadata = buildPersistenceMetadata(clazz);
// 緩存所構建的 InjectionMetadata 對象
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
以上方法findPersistenceMetadata
構建bean
的註解元數據所使用的方法實現如下 :
// 基於類 clazz,使用反射方法獲取持久化註解元數據 :
// @PersistenceContext
// @PersistenceUnit
private InjectionMetadata buildPersistenceMetadata(final Class<?> clazz) {
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final LinkedList<InjectionMetadata.InjectedElement> currElements =
new LinkedList<>();
// 使用反射遍歷 targetClass 自身聲明的各個實例成員屬性,看它們是否
// 使用了註解 @PersistenceContext 或者 @PersistenceUnit , 注意,需要忽略
// 類成員屬性(也就是使用了 static 修飾符的屬性定義)
// 如果遇到了相應的屬性,構造一個PersistenceElement對象,添加到currElements
ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (field.isAnnotationPresent(PersistenceContext.class) ||
field.isAnnotationPresent(PersistenceUnit.class)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException(
"Persistence annotations are not supported on static fields");
}
currElements.add(new PersistenceElement(field, field, null));
}
});
// 使用反射遍歷 targetClass 自身聲明的各個實例成員方法,看它們是否
// 使用了註解 @PersistenceContext 或者 @PersistenceUnit , 注意,
// 1. 需要忽略類成員方法(也就是使用了 static 修飾符的方法定義)
// 2. 使用了以上持久化註解的方法必須只有一個參數
// 3. 也處理因爲可能是用泛型導致的橋接方法
// 如果遇到了相應的方法,構造一個PersistenceElement對象,添加到currElements
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
if ((bridgedMethod.isAnnotationPresent(PersistenceContext.class) ||
bridgedMethod.isAnnotationPresent(PersistenceUnit.class)) &&
method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException(
"Persistence annotations are not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException(
"Persistence annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
// 注意,這裏使用的 InjectedElement 實現類是 PersistenceElement , 這是一個
// PersistenceAnnotationBeanPostProcessor 自定義的專門用於獲取和注入持久化屬性
// 的 InjectedElement,它是 PersistenceAnnotationBeanPostProcessor 進行持久化
// 屬性注入的核心
currElements.add(new PersistenceElement(method, bridgedMethod, pd));
}
});
elements.addAll(0, currElements);
// 處理完 targetClass,開始處理 targetClass 的 父類
targetClass = targetClass.getSuperclass();
}
// 直到 targetClass 變成 null 或者 Object,停止搜尋持久化元數據
while (targetClass != null && targetClass != Object.class);
// 將保存在 elements 中的 PersistenceElement 用於構建針對當前bean的注入元數據對象
// InjectionMetadata
return new InjectionMetadata(clazz, elements);
}
從上面的代碼可以看到,PersistenceAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
主要的作用就是提取當前創建的bean
中的持久化屬性元數據並保存在this.injectionMetadataCache
中。而對於每個bean
,持久化元數據獲取的主要邏輯實現在方法buildPersistenceMetadata
中。而buildPersistenceMetadata
使用了InjectionMetadata
包裝針對一個bean
的持久化屬性注入元數據,其中的每一條持久化屬性注入元數據元素對應一個PersistenceElement
對象。而PersistenceElement
是PersistenceAnnotationBeanPostProcessor
嵌套定義的一個內部類,繼承自InjectionMetadata.InjectedElement
。對目標bean
相應持久化屬性的注入,主要通過該實現類完成。
持久化屬性的注入
從上面的分析我們知道,PersistenceAnnotationBeanPostProcessor
能夠獲取任何一個bean
上的持久化屬性元數據了,也就是說,PersistenceAnnotationBeanPostProcessor
知道針對某個bean
的如下信息 :
- 是否需要持久化屬性注入
- 需要注入哪些持久化屬性
那麼,這些屬性又是如何注入的呢 ? 我們繼續分析。
PersistenceAnnotationBeanPostProcessor
又實現了接口InstantiationAwareBeanPostProcessor
約定的生命週期回調方法postProcessProperties
,該方法會在某個bean
創建過程中的屬性填充階段,也可以認爲是依賴注入階段,被應用到該bean
。那麼我們來看PersistenceAnnotationBeanPostProcessor
的這個方法又做了哪些事情:
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 獲取當前bean的持久化屬性注入元數據,根據前面的分析,該元數據已經在postProcessMergedBeanDefinition
// 應用階段獲取和緩存,所以這裏 metadata 會返回已經緩存的持久化屬性注入元數據
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;
}
InjectionMetadata#inject
屬性注入的過程,主要就是其中每個元素對應的屬性注入的執行。而從上面的分析我們已經知道,每個bean
的持久化屬性注入元數據對象InjectionMetadata
的每個元素都是一個PersistenceElement
:
/**
* Class representing injection information about an annotated field
* or setter method.
*/
private class PersistenceElement extends InjectionMetadata.InjectedElement {
private final String unitName;
@Nullable
private PersistenceContextType type;
private boolean synchronizedWithTransaction = false;
@Nullable
private Properties properties;
public PersistenceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
super(member, pd);
PersistenceContext pc = ae.getAnnotation(PersistenceContext.class);
PersistenceUnit pu = ae.getAnnotation(PersistenceUnit.class);
Class<?> resourceType = EntityManager.class;
if (pc != null) {
if (pu != null) {
throw new IllegalStateException("Member may only be annotated with either " +
"@PersistenceContext or @PersistenceUnit, not both: " + member);
}
Properties properties = null;
PersistenceProperty[] pps = pc.properties();
if (!ObjectUtils.isEmpty(pps)) {
properties = new Properties();
for (PersistenceProperty pp : pps) {
properties.setProperty(pp.name(), pp.value());
}
}
this.unitName = pc.unitName();
this.type = pc.type();
this.synchronizedWithTransaction =
SynchronizationType.SYNCHRONIZED.equals(pc.synchronization());
this.properties = properties;
}
else {
resourceType = EntityManagerFactory.class;
this.unitName = pu.unitName();
}
checkResourceType(resourceType);
}
/**
* Resolve the object against the application context.
* 從應用上下文中獲取要注入的bean實例: EntityManagerFactory 對象或者 EntityManager,
* 覆蓋了父類InjectedElement的缺省實現,這是在該類對象上調用#inject方法時,設置目標屬性
* 時用於解析對應屬性值的方法
*/
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
// Resolves to EntityManagerFactory or EntityManager.
if (this.type != null) {
return (this.type == PersistenceContextType.EXTENDED ?
resolveExtendedEntityManager(target, requestingBeanName) :
resolveEntityManager(requestingBeanName));
}
else {
// OK, so we need an EntityManagerFactory...
return resolveEntityManagerFactory(requestingBeanName);
}
}
// 獲取 EntityManagerFactory 的方法
private EntityManagerFactory resolveEntityManagerFactory(@Nullable String requestingBeanName) {
// Obtain EntityManagerFactory from JNDI?
// 從JNDI獲取 EntityManagerFactory
EntityManagerFactory emf = getPersistenceUnit(this.unitName);
if (emf == null) {
// Need to search for EntityManagerFactory beans.
// 從 bean 容器獲取 EntityManagerFactory
emf = findEntityManagerFactory(this.unitName, requestingBeanName);
}
return emf;
}
// 獲取 EntityManager 的方法
private EntityManager resolveEntityManager(@Nullable String requestingBeanName) {
// Obtain EntityManager reference from JNDI?
// 從JNDI獲取 EntityManager
EntityManager em = getPersistenceContext(this.unitName, false);
if (em == null) {
// No pre-built EntityManager found -> build one based on factory.
// Obtain EntityManagerFactory from JNDI?
// 從 bean 容器獲取 EntityManagerFactory ,然後創建 EntityManager
EntityManagerFactory emf = getPersistenceUnit(this.unitName);
if (emf == null) {
// Need to search for EntityManagerFactory beans.
emf = findEntityManagerFactory(this.unitName, requestingBeanName);
}
// Inject a shared transactional EntityManager proxy.
if (emf instanceof EntityManagerFactoryInfo &&
((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) {
// Create EntityManager based on the info's vendor-specific type
// (which might be more specific than the field's type).
em = SharedEntityManagerCreator.createSharedEntityManager(
emf, this.properties, this.synchronizedWithTransaction);
}
else {
// Create EntityManager based on the field's type.
em = SharedEntityManagerCreator.createSharedEntityManager(
emf, this.properties, this.synchronizedWithTransaction, getResourceType());
}
}
return em;
}
// 獲取 EntityManager擴展 的方法
private EntityManager resolveExtendedEntityManager(Object target,
@Nullable String requestingBeanName) {
// Obtain EntityManager reference from JNDI?
EntityManager em = getPersistenceContext(this.unitName, true);
if (em == null) {
// No pre-built EntityManager found -> build one based on factory.
// Obtain EntityManagerFactory from JNDI?
EntityManagerFactory emf = getPersistenceUnit(this.unitName);
if (emf == null) {
// Need to search for EntityManagerFactory beans.
emf = findEntityManagerFactory(this.unitName, requestingBeanName);
}
// Inject a container-managed extended EntityManager.
em = ExtendedEntityManagerCreator.createContainerManagedEntityManager(
emf, this.properties, this.synchronizedWithTransaction);
}
if (em instanceof EntityManagerProxy && beanFactory != null && requestingBeanName != null &&
beanFactory.containsBean(requestingBeanName) &&
!beanFactory.isPrototype(requestingBeanName)) {
extendedEntityManagersToClose.put(target, ((EntityManagerProxy)
em).getTargetEntityManager());
}
return em;
}
}
從上面的代碼可以看出,持久化屬性的注入主要是通過InjectionMetadata#inject
+PersistenceElement#getResourceToInject
完成的。而persistence unit
,persistence context
首先嚐試從JDNI
獲取,如果獲取不到,則嘗試從bean
容器獲取。下面我們繼續分析這些組件的獲取方法。
從bean
容器獲取persistence unit
/persistence context
的方法
從bean
容器獲取persistence unit
protected EntityManagerFactory findEntityManagerFactory(@Nullable String unitName,
@Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
String unitNameForLookup = (unitName != null ? unitName : "");
if (unitNameForLookup.isEmpty()) {
unitNameForLookup = this.defaultPersistenceUnitName;
}
if (!unitNameForLookup.isEmpty()) {
return findNamedEntityManagerFactory(unitNameForLookup, requestingBeanName);
}
else {
return findDefaultEntityManagerFactory(requestingBeanName);
}
}
/**
* Find an EntityManagerFactory with the given name in the current
* Spring application context.
* @param unitName the name of the persistence unit (never empty)
* @param requestingBeanName the name of the requesting bean
* @return the EntityManagerFactory
* @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
*/
protected EntityManagerFactory findNamedEntityManagerFactory(String unitName,
@Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
Assert.state(this.beanFactory != null,
"ListableBeanFactory required for EntityManagerFactory bean lookup");
EntityManagerFactory emf = EntityManagerFactoryUtils.findEntityManagerFactory(
this.beanFactory, unitName);
if (requestingBeanName != null && this.beanFactory instanceof ConfigurableBeanFactory) {
((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName,
requestingBeanName);
}
return emf;
}
/**
* Find a single default EntityManagerFactory in the Spring application context.
* @return the default EntityManagerFactory
* @throws NoSuchBeanDefinitionException if there is no single EntityManagerFactory in the context
*/
protected EntityManagerFactory findDefaultEntityManagerFactory(@Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
Assert.state(this.beanFactory != null,
"ListableBeanFactory required for EntityManagerFactory bean lookup");
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
// Fancy variant with dependency registration
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) this.beanFactory;
NamedBeanHolder<EntityManagerFactory> emfHolder =
clbf.resolveNamedBean(EntityManagerFactory.class);
if (requestingBeanName != null) {
clbf.registerDependentBean(emfHolder.getBeanName(), requestingBeanName);
}
return emfHolder.getBeanInstance();
}
else {
// Plain variant: just find a default bean
return this.beanFactory.getBean(EntityManagerFactory.class);
}
}
從bean
容器獲取persistence context
從上面的的代碼分析可以看出,如果persistence unit
,也就是組件EntityManagerFactory
bean
,是從容器中獲取得到的,則persistence context
,也就是EntityManager
實例,會基於該組件EntityManagerFactory
bean
,通過SharedEntityManagerCreator.createSharedEntityManager
方法創建得到。
從JNDI
獲取persistence unit
/persistence context
的方法
從JNDI
獲取persistence unit
從JNDI
獲取persistence unit
的實現由PersistenceAnnotationBeanPostProcessor
實現如下:
@Nullable
protected EntityManagerFactory getPersistenceUnit(@Nullable String unitName) {
if (this.persistenceUnits != null) {
String unitNameForLookup = (unitName != null ? unitName : "");
if (unitNameForLookup.isEmpty()) {
unitNameForLookup = this.defaultPersistenceUnitName;
}
String jndiName = this.persistenceUnits.get(unitNameForLookup);
if (jndiName == null && "".equals(unitNameForLookup) && this.persistenceUnits.size() == 1) {
jndiName = this.persistenceUnits.values().iterator().next();
}
if (jndiName != null) {
try {
return lookup(jndiName, EntityManagerFactory.class);
}
catch (Exception ex) {
throw new IllegalStateException("Could not obtain EntityManagerFactory ["
+ jndiName + "] from JNDI", ex);
}
}
}
return null;
}
從JNDI
獲取persistence context
從JNDI
獲取persistence context
的實現由PersistenceAnnotationBeanPostProcessor
實現如下:
@Nullable
protected EntityManager getPersistenceContext(@Nullable String unitName, boolean extended) {
Map<String, String> contexts = (extended ? this.extendedPersistenceContexts :
this.persistenceContexts);
if (contexts != null) {
String unitNameForLookup = (unitName != null ? unitName : "");
if (unitNameForLookup.isEmpty()) {
unitNameForLookup = this.defaultPersistenceUnitName;
}
String jndiName = contexts.get(unitNameForLookup);
if (jndiName == null && "".equals(unitNameForLookup) && contexts.size() == 1) {
jndiName = contexts.values().iterator().next();
}
if (jndiName != null) {
try {
return lookup(jndiName, EntityManager.class);
}
catch (Exception ex) {
throw new IllegalStateException("Could not obtain EntityManager [" +
jndiName + "] from JNDI", ex);
}
}
}
return null;
}
protected <T> T lookup(String jndiName, Class<T> requiredType) throws Exception {
return new LocatorDelegate().lookup(jndiName, requiredType);
}
內部類LocatorDelegate
– 從JNDI
查找EntityManagerFactory
/EntityManager
// 從JNDI中查找EntityManagerFactory或者EntityManager的內部類,基於JndiLocatorDelegate
// 的一個封裝
private class LocatorDelegate {
public <T> T lookup(String jndiName, Class<T> requiredType) throws Exception {
JndiLocatorDelegate locator = new JndiLocatorDelegate();
if (jndiEnvironment instanceof JndiTemplate) {
locator.setJndiTemplate((JndiTemplate) jndiEnvironment);
}
else if (jndiEnvironment instanceof Properties) {
locator.setJndiEnvironment((Properties) jndiEnvironment);
}
else if (jndiEnvironment != null) {
throw new IllegalStateException("Illegal 'jndiEnvironment' type: " +
jndiEnvironment.getClass());
}
locator.setResourceRef(resourceRef);
return locator.lookup(jndiName, requiredType);
}
}
總結
通過上述分析可見,在一個Spring JPA
應用中,PersistenceAnnotationBeanPostProcessor
用於發現每個bean
中的持久化註解並完成這些持久化屬性的注入。
這些持久化屬性指的是使用@PersistenceUnit
/@PersistenceContext
註解的bean
實例成員屬性EntityManagerFactory
/EntityManager
或者這些屬性的設置方法。
這個過程發生在每個bean
的創建過程中。具體來講,持久化屬性注入元數據在bean
創建#postProcessMergedBeanDefinition
階段被獲取並緩存在該BeanPostProcessor
對象,在bean
創建#postProcessProperties
階段被注入到bean
中。
持久化屬性的注入使用了框架內部工具InjectionMetadata
+InjectedElement子類PersistenceElement
。該子類PersistenceElement
是一個PersistenceAnnotationBeanPostProcessor
的嵌套子類。
而persistence unit
和persistence context
組件的獲取,由PersistenceElement
使用PersistenceAnnotationBeanPostProcessor
提供的方法getPersistenceUnit
/getPersistenceContext
/findEntityManagerFactory
從JNDI
或者bean
容器獲得。