BeanDefinitionUtils.java
/*
* Copyright 2014-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.util;
import static java.util.Arrays.*;
import static org.springframework.beans.factory.BeanFactoryUtils.*;
import jakarta.persistence.EntityManagerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Utility methods to work with {@link BeanDefinition} instances from {@link BeanFactoryPostProcessor}s.
*
* @author Oliver Gierke
* @author Mark Paluch
* @author Donghun Shin
*/
public final class BeanDefinitionUtils {
private static final String JNDI_OBJECT_FACTORY_BEAN = "org.springframework.jndi.JndiObjectFactoryBean";
private static final List<Class<?>> EMF_TYPES;
private BeanDefinitionUtils() {}
static {
List<Class<?>> types = new ArrayList<>();
types.add(EntityManagerFactory.class);
types.add(AbstractEntityManagerFactoryBean.class);
if (ClassUtils.isPresent(JNDI_OBJECT_FACTORY_BEAN, ClassUtils.getDefaultClassLoader())) {
types.add(JndiObjectFactoryBean.class);
}
EMF_TYPES = Collections.unmodifiableList(types);
}
/**
* Return all bean names for bean definitions that will result in an {@link EntityManagerFactory} eventually. We're
* checking for {@link EntityManagerFactory} and the well-known factory beans here to avoid eager initialization of
* the factory beans. The double lookup is necessary especially for JavaConfig scenarios as people might declare an
* {@link EntityManagerFactory} directly.
*
* @param beanFactory
* @return
*/
public static Iterable<String> getEntityManagerFactoryBeanNames(ListableBeanFactory beanFactory) {
String[] beanNames = beanNamesForTypeIncludingAncestors(beanFactory, EntityManagerFactory.class, true, false);
Set<String> names = new HashSet<>(asList(beanNames));
for (String factoryBeanName : beanNamesForTypeIncludingAncestors(beanFactory,
AbstractEntityManagerFactoryBean.class, true, false)) {
names.add(transformedBeanName(factoryBeanName));
}
return names;
}
/**
* Returns {@link EntityManagerFactoryBeanDefinition} instances for all {@link BeanDefinition} registered in the given
* {@link ConfigurableListableBeanFactory} hierarchy.
*
* @param beanFactory must not be {@literal null}.
* @return
*/
public static Collection<EntityManagerFactoryBeanDefinition> getEntityManagerFactoryBeanDefinitions(
ConfigurableListableBeanFactory beanFactory) {
return getEntityManagerFactoryBeanDefinitions(beanFactory, (beanName, beanDefinition) -> true);
}
/**
* Returns {@link EntityManagerFactoryBeanDefinition} instances for all {@link BeanDefinition} registered in the given
* {@link ConfigurableListableBeanFactory} hierarchy.
*
* @param beanFactory must not be {@literal null}.
* @param beanDefinitionBiPredicate predicate to determine whether a {@link EntityManagerFactory} bean should be
* decorated with a {@code SharedEntityManager} bean definition.
* @return
* @since 4.0
*/
public static Collection<EntityManagerFactoryBeanDefinition> getEntityManagerFactoryBeanDefinitions(
ConfigurableListableBeanFactory beanFactory, BiPredicate<String, BeanDefinition> beanDefinitionBiPredicate) {
Set<EntityManagerFactoryBeanDefinition> definitions = new HashSet<>();
for (Class<?> type : EMF_TYPES) {
for (String name : beanFactory.getBeanNamesForType(type, true, false)) {
registerEntityManagerFactoryBeanDefinition(transformedBeanName(name), beanFactory, definitions,
beanDefinitionBiPredicate);
}
}
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory instanceof ConfigurableListableBeanFactory parentConfigurableListableBeanFactory) {
definitions.addAll(
getEntityManagerFactoryBeanDefinitions(parentConfigurableListableBeanFactory, beanDefinitionBiPredicate));
}
return definitions;
}
/**
* Registers an {@link EntityManagerFactoryBeanDefinition} for the bean with the given name. Drops
* {@link JndiObjectFactoryBean} instances that don't point to an {@link EntityManagerFactory} bean as expected type.
*
* @param name
* @param beanFactory
* @param definitions
* @param decoratorPredicate
*/
private static void registerEntityManagerFactoryBeanDefinition(String name,
ConfigurableListableBeanFactory beanFactory, Collection<EntityManagerFactoryBeanDefinition> definitions,
BiPredicate<String, BeanDefinition> decoratorPredicate) {
BeanDefinition definition = beanFactory.getBeanDefinition(name);
if (JNDI_OBJECT_FACTORY_BEAN.equals(definition.getBeanClassName())) {
if (!EntityManagerFactory.class.getName().equals(definition.getPropertyValues().get("expectedType"))) {
return;
}
}
Class<?> type = beanFactory.getType(name);
if (type == null || !EntityManagerFactory.class.isAssignableFrom(type)) {
return;
}
if (decoratorPredicate.test(name, definition)) {
definitions.add(new EntityManagerFactoryBeanDefinition(name, beanFactory));
}
}
/**
* Returns the {@link BeanDefinition} with the given name, obtained from the given {@link BeanFactory} or one of its
* parents.
*
* @param name
* @param beanFactory
* @return
*/
public static BeanDefinition getBeanDefinition(String name, ConfigurableListableBeanFactory beanFactory) {
try {
return beanFactory.getBeanDefinition(name);
} catch (NoSuchBeanDefinitionException o_O) {
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory instanceof ConfigurableListableBeanFactory parentConfigurableListableBeanFactory) {
return getBeanDefinition(name, parentConfigurableListableBeanFactory);
}
throw o_O;
}
}
/**
* Value object to represent a {@link BeanDefinition} for an {@link EntityManagerFactory} with a dedicated bean name.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
public static class EntityManagerFactoryBeanDefinition {
private final String beanName;
private final ConfigurableListableBeanFactory beanFactory;
/**
* Creates a new {@link EntityManagerFactoryBeanDefinition}.
*
* @param beanName
* @param beanFactory
*/
public EntityManagerFactoryBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) {
this.beanName = beanName;
this.beanFactory = beanFactory;
}
/**
* Returns the bean name of the {@link BeanDefinition} for the {@link EntityManagerFactory}.
*
* @return
*/
public String getBeanName() {
return beanName;
}
/**
* Returns the underlying {@link BeanFactory}.
*
* @return
*/
public BeanFactory getBeanFactory() {
return beanFactory;
}
/**
* Returns the {@link BeanDefinition} for the {@link EntityManagerFactory}.
*
* @return
*/
public BeanDefinition getBeanDefinition() {
return beanFactory.getBeanDefinition(beanName);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof EntityManagerFactoryBeanDefinition that)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(beanName, that.beanName)) {
return false;
}
return ObjectUtils.nullSafeEquals(beanFactory, that.beanFactory);
}
@Override
public int hashCode() {
int result = ObjectUtils.nullSafeHashCode(beanName);
result = 31 * result + ObjectUtils.nullSafeHashCode(beanFactory);
return result;
}
}
}