
org.hotswap.agent.plugin.spring.reload.SpringBeanReload Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2013-2024 the HotswapAgent authors.
*
* This file is part of HotswapAgent.
*
* HotswapAgent is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 2 of the License, or (at your
* option) any later version.
*
* HotswapAgent is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with HotswapAgent. If not, see http://www.gnu.org/licenses/.
*/
package org.hotswap.agent.plugin.spring.reload;
import org.hotswap.agent.logging.AgentLogger;
import org.hotswap.agent.plugin.spring.core.*;
import org.hotswap.agent.plugin.spring.files.PropertyReload;
import org.hotswap.agent.plugin.spring.files.XmlBeanDefinitionScannerAgent;
import org.hotswap.agent.plugin.spring.getbean.ProxyReplacer;
import org.hotswap.agent.plugin.spring.listener.SpringEventSource;
import org.hotswap.agent.plugin.spring.transformers.api.BeanFactoryLifecycle;
import org.hotswap.agent.plugin.spring.utils.ResourceUtils;
import org.hotswap.agent.util.AnnotationHelper;
import org.hotswap.agent.util.spring.util.ClassUtils;
import org.hotswap.agent.util.spring.util.ObjectUtils;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.*;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import static org.hotswap.agent.plugin.spring.utils.RegistryUtils.maybeRegistryToBeanFactory;
import static org.hotswap.agent.util.ReflectionHelper.get;
/**
* Reload spring beans.
*/
public class SpringBeanReload {
private static AgentLogger LOGGER = AgentLogger.getLogger(SpringBeanReload.class);
private AtomicBoolean isReloading = new AtomicBoolean(false);
// it is synchronized set because it is used synchronized block
private Set> classes = Collections.newSetFromMap(new ConcurrentHashMap<>());
// it is synchronized set because it is used synchronized block
private Set properties = Collections.newSetFromMap(new ConcurrentHashMap<>());
private Set yamls = Collections.newSetFromMap(new ConcurrentHashMap<>());
// it is synchronized set because it is used synchronized block
private Set xmls = Collections.newSetFromMap(new ConcurrentHashMap<>());
// it is synchronized set because it is used synchronized block
private Set newScanBeanDefinitions = new HashSet<>();
private Set changedBeanNames = new HashSet<>();
Set newBeanNames = new HashSet<>();
DefaultListableBeanFactory beanFactory;
private final Map> dependentBeanMap;
private Set processedBeans = new HashSet<>();
private Set destroyClasses = new HashSet<>();
private Set beansToProcess = new HashSet<>();
private final BeanNameGenerator beanNameGenerator;
private final BeanFactoryAssistant beanFactoryAssistant;
public SpringBeanReload(DefaultListableBeanFactory beanFactory) {
this.beanFactoryAssistant = new BeanFactoryAssistant(beanFactory);
beanNameGenerator = new AnnotationBeanNameGenerator();
this.beanFactory = beanFactory;
this.dependentBeanMap = (Map>) get(beanFactory, "dependentBeanMap");
}
public void addClass(Class clazz) {
if (clazz == null) {
return;
}
String simpleName = clazz.getSimpleName();
String userClassSimpleName = ClassUtils.getUserClass(clazz).getSimpleName();
boolean sameClass = simpleName.equals(userClassSimpleName);
synchronized (classes) {
if (classes.add(clazz)) {
if (sameClass) {
LOGGER.debug("try to add changed class '{}' into {}", clazz.getName(), ObjectUtils.identityToString(beanFactory));
} else {
LOGGER.debug("try to add changed class '{}({})' into {}", clazz.getName(), userClassSimpleName, ObjectUtils.identityToString(beanFactory));
}
} else {
if (sameClass) {
LOGGER.debug("try to add changed class '{}' into {}, but it is exist", clazz.getName(), ObjectUtils.identityToString(beanFactory));
} else {
LOGGER.debug("try to add changed class '{}({})' into {}, but it is exist", clazz.getName(), userClassSimpleName, ObjectUtils.identityToString(beanFactory));
}
}
}
}
public void addProperty(URL property) {
if (property == null) {
return;
}
synchronized (properties) {
if (properties.add(property)) {
LOGGER.info("try to add changed property '{}' into {}", property, ObjectUtils.identityToString(beanFactory));
} else {
LOGGER.debug("try to add changed property '{}' into {}", property, ObjectUtils.identityToString(beanFactory));
}
}
}
public void addYaml(URL property) {
if (property == null) {
return;
}
synchronized (yamls) {
if (yamls.add(property)) {
LOGGER.info("try to add changed yaml '{}' into {}", property, ObjectUtils.identityToString(beanFactory));
} else {
LOGGER.debug("try to add changed yaml '{}' into {}, but exist", property, ObjectUtils.identityToString(beanFactory));
}
}
}
public void addScanNewBean(BeanDefinitionRegistry registry, BeanDefinitionHolder beanDefinitionHolder) {
if (beanDefinitionHolder == null) {
return;
}
DefaultListableBeanFactory defaultListableBeanFactory = maybeRegistryToBeanFactory(registry);
if (defaultListableBeanFactory != null) {
if (defaultListableBeanFactory.equals(beanFactory)) {
synchronized (newScanBeanDefinitions) {
newScanBeanDefinitions.add(beanDefinitionHolder);
LOGGER.info("add new spring bean '{}' into {}", beanDefinitionHolder.getBeanName(), ObjectUtils.identityToString(beanFactory));
return;
}
}
}
LOGGER.debug("'{}' is not '{}' or the newBean is exist, ignore it", registry, defaultListableBeanFactory);
}
public void addXml(URL xml) {
if (xml == null) {
return;
}
synchronized (xmls) {
if (xmls.add(xml)) {
LOGGER.info("try to add xml '{}' into {}", xml, ObjectUtils.identityToString(beanFactory));
} else {
LOGGER.debug("try to add xml '{}' into {}", xml, ObjectUtils.identityToString(beanFactory));
}
}
}
public void addChangedBeanNames(String[] beanNames) {
if (beanNames == null) {
return;
}
synchronized (this.changedBeanNames) {
if (this.changedBeanNames.addAll(Arrays.asList(beanNames))) {
LOGGER.debug("try to add changed beanNames '{}' into {}", Arrays.asList(beanNames), ObjectUtils.identityToString(beanFactory));
} else {
LOGGER.trace("try to add changed beanNames '{}' into {}, but exist", Arrays.asList(beanNames), ObjectUtils.identityToString(beanFactory));
}
}
}
public void collectPlaceHolderProperties() {
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
doCollectPlaceHolderProperties(beanName, beanDefinition);
}
}
private void doCollectPlaceHolderProperties(String beanName, BeanDefinition beanDefinition) {
if (beanDefinition.getPropertyValues() != null) {
for (PropertyValue pv : beanDefinition.getPropertyValues().getPropertyValues()) {
String resourcePath = getPlaceHolderBeanResource(pv.getValue(), beanName, beanDefinition);
if (resourcePath != null) {
beanFactoryAssistant.placeHolderXmlMapping.put(beanName, resourcePath);
return;
}
}
}
if (beanDefinition.getConstructorArgumentValues().isEmpty()) {
return;
}
for (ConstructorArgumentValues.ValueHolder valueHolder : beanDefinition.getConstructorArgumentValues().getIndexedArgumentValues().values()) {
String resourcePath = getPlaceHolderBeanResource(valueHolder.getValue(), beanName, beanDefinition);
if (resourcePath != null) {
beanFactoryAssistant.placeHolderXmlMapping.put(beanName, resourcePath);
return;
}
}
for (ConstructorArgumentValues.ValueHolder valueHolder : beanDefinition.getConstructorArgumentValues().getGenericArgumentValues()) {
String resourcePath = getPlaceHolderBeanResource(valueHolder.getValue(), beanName, beanDefinition);
if (resourcePath != null) {
beanFactoryAssistant.placeHolderXmlMapping.put(beanName, resourcePath);
return;
}
}
}
private String getPlaceHolderBeanResource(Object object, String beanName, BeanDefinition beanDefinition) {
if (!isPlaceHolderBean(object)) {
return null;
}
if (beanDefinition instanceof AbstractBeanDefinition) {
return ResourceUtils.getPath(((AbstractBeanDefinition) beanDefinition).getResource());
}
return null;
}
private boolean isPlaceHolderBean(Object v) {
String value = null;
if (v instanceof TypedStringValue) {
value = ((TypedStringValue) v).getValue();
} else if (v instanceof String) {
value = (String) v;
}
if (value == null) {
return false;
}
if (value.startsWith(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX) &&
value.endsWith(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_SUFFIX)) {
return true;
}
return false;
}
public boolean reload(long changeTimeStamps) {
if (!preCheckReload()) {
return false;
}
boolean allowBeanDefinitionOverriding = BeanFactoryProcessor.isAllowBeanDefinitionOverriding(beanFactory);
long now = System.currentTimeMillis();
ClassLoader origContextClassLoader = null;
try {
origContextClassLoader = ClassUtils.overrideThreadContextClassLoader(beanFactory.getBeanClassLoader());
beanFactoryAssistant.setReload(true);
LOGGER.debug("##### start reloading '{}' with timestamp '{}'", ObjectUtils.identityToString(beanFactory), changeTimeStamps);
LOGGER.trace("SpringReload:{}, beanFactory:{}", this, beanFactory);
BeanFactoryProcessor.setAllowBeanDefinitionOverriding(beanFactory, true);
do {
doReload();
} while (checkHasChange() && printReloadLog());
return true;
} finally {
ClassUtils.overrideThreadContextClassLoader(origContextClassLoader);
beanFactoryAssistant.increaseReloadTimes();
BeanFactoryProcessor.setAllowBeanDefinitionOverriding(beanFactory, allowBeanDefinitionOverriding);
LOGGER.debug("##### [{}th] finish reloading '{}', it cost {}ms", beanFactoryAssistant.getReloadTimes(),
ObjectUtils.identityToString(beanFactory), System.currentTimeMillis() - now);
}
}
private void doReload() {
// when there are changes, it will rerun the while loop
while (true) {
// 1. clear cache
clearSpringCache();
// 2. properties reload
boolean propertiesChanged = refreshProperties();
// 3. reload xmls: the beans will be destroyed
reloadXmlBeanDefinitions(propertiesChanged);
// 4. add changed classes and changed beans into recreate beans
refreshChangedClassesAndBeans();
// 5. load new beans from scanning. The newBeanNames is used to print the suitable log.
refreshNewBean();
// 6. destroy bean including factory bean
destroyBeans();
// rerun the while loop if it has changes
if (checkHasChange() && printReloadLog()) {
continue;
}
//beanDefinition enhanced: BeanFactoryPostProcessor
ProxyReplacer.clearAllProxies();
// 7. invoke the Bean lifecycle steps
// 7.1 invoke BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
addBeanPostProcessors(beanFactory);
// 7.2 process @Value and @Autowired of singleton beans excluding destroyed beans
processAutowiredAnnotationBeans();
// 7.3 process @Configuration
processConfigBeanDefinitions();
// 8. skip the while loop if no change
if (!checkHasChange()) {
break;
}
}
// 9 invoke getBean to instantiate singleton beans
preInstantiateSingleton();
// 10 reset mvc initialized, it will update the mapping of url and handler
refreshRequestMapping();
// 11 clear all process cache
clearLocalCache();
}
private boolean preCheckReload() {
if (!checkHasChange()) {
return false;
}
// check the classes is bean of spring, if not remove it
synchronized (classes) {
if (!classes.isEmpty()) {
Iterator> iterator = classes.iterator();
while (iterator.hasNext()) {
Class> clazz = iterator.next();
String[] names = beanFactory.getBeanNamesForType(clazz);
// if the class is not spring bean or Factory Class, remove it
if ((names == null || names.length == 0) && !isFactoryMethod(clazz)) {
LOGGER.trace("the class '{}' is not spring bean or factory class", clazz.getName());
iterator.remove();
} else {
LOGGER.debug("the class '{}' is spring bean or factory class", clazz.getName());
}
}
}
}
return checkHasChange();
}
private boolean isFactoryMethod(Class> clazz) {
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (beanDefinition.getFactoryMethodName() != null && beanDefinition.getBeanClassName() != null &&
clazz.getName().equals(beanDefinition.getBeanClassName())) {
return true;
}
}
return false;
}
private boolean printReloadLog() {
LOGGER.debug("the class or the file at '{}' has changes, rerun the while loop.{}", ObjectUtils.identityToString(beanFactory), this);
return true;
}
private boolean checkHasChange() {
if (properties.isEmpty() && classes.isEmpty() && xmls.isEmpty() && newScanBeanDefinitions.isEmpty()
&& yamls.isEmpty() && changedBeanNames.isEmpty()) {
LOGGER.trace("no change, ignore reloading '{}'", ObjectUtils.identityToString(beanFactory));
return false;
}
LOGGER.trace("has change, start reloading '{}', {}", ObjectUtils.identityToString(beanFactory), this);
return true;
}
private boolean refreshProperties() {
boolean propertiesChanged = false;
synchronized (properties) {
if (!properties.isEmpty()) {
beansToProcess.addAll(beanFactoryAssistant.placeHolderXmlMapping.keySet());
// clear properties
properties.clear();
propertiesChanged = true;
}
}
synchronized (yamls) {
if (!yamls.isEmpty()) {
// clear
yamls.clear();
propertiesChanged = true;
}
}
if (propertiesChanged) {
LOGGER.reload("the properties of '{}' is changed", ObjectUtils.identityToString(beanFactory));
PropertyReload.reloadPropertySource(beanFactory);
beansToProcess.addAll(PropertyReload.getContainValueAnnotationBeans(beanFactory));
return true;
}
return false;
}
private void reloadXmlBeanDefinitions(boolean propertiesChanged) {
Set result = XmlBeanDefinitionScannerAgent.reloadXmlsAndGetBean(beanFactory, propertiesChanged,
beanFactoryAssistant.placeHolderXmlMapping, beansToProcess, xmls);
processedBeans.addAll(result);
}
private void refreshChangedClassesAndBeans() {
LOGGER.debug("refresh changed classes and beans of {}, classes:{}, changedBeans:{}",
ObjectUtils.identityToString(beanFactory), classes, changedBeanNames);
Set configurationBeansToReload = new HashSet<>();
// we should refresh changed classes before refresh changed beans.
// after refresh class, maybe we will add some changed beans into changedBeanNames
// 1. refresh changed classes
refreshChangedClass(configurationBeansToReload::addAll);
// 2. refresh changed beans
refreshChangedBeans(configurationBeansToReload::addAll);
// 3 when the bean is factory bean, it should be recreated.
resetConfigurationBeanDefinition(configurationBeansToReload);
LOGGER.trace("clear class cache of {}", ObjectUtils.identityToString(beanFactory));
}
private void refreshChangedClass(Consumer> reloadBeans) {
Set classToProcess;
synchronized (classes) {
classToProcess = new HashSet<>(classes);
classes.clear();
}
for (Class clazz : classToProcess) {
destroyClasses.add(ClassUtils.getUserClass(clazz).getName());
String[] names = beanFactory.getBeanNamesForType(clazz);
if (names != null && names.length > 0) {
LOGGER.trace("the bean of class {} has the bean names {}", clazz.getName(), Arrays.asList(names));
beansToProcess.addAll(Arrays.asList(names));
// 3.1 when the bean is @Configuration, it should be recreated.
reloadBeans.accept(reloadAnnotatedBeanDefinitions(clazz, names));
// notify the class is changed
SpringEventSource.INSTANCE.fireEvent(new ClassChangeEvent(clazz, beanFactory));
} else {
LOGGER.debug("the bean of class {} not found", clazz.getName());
}
}
}
private void refreshChangedBeans(Consumer> reloadBeans) {
Set beanNamesToProcess;
synchronized (changedBeanNames) {
beanNamesToProcess = new HashSet<>(changedBeanNames);
changedBeanNames.clear();
}
for (String beanName : beanNamesToProcess) {
beansToProcess.add(beanName);
// maybe this bean is not exist, or it belongs to other beanFactory
if (!beanFactory.containsBeanDefinition(beanName)) {
continue;
}
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (beanDefinition instanceof AbstractBeanDefinition) {
AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) beanDefinition;
if (abstractBeanDefinition.hasBeanClass()) {
Class> clazz = abstractBeanDefinition.getBeanClass();
reloadBeans.accept(reloadAnnotatedBeanDefinitions(clazz, new String[]{beanName}));
}
}
}
}
private void resetConfigurationBeanDefinition(Set configurationBeansToReload) {
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
String factoryBeanName = beanDefinition.getFactoryBeanName();
if (factoryBeanName != null && configurationBeansToReload.contains(factoryBeanName)) {
LOGGER.debug("the bean '{}' will be recreating because the factory bean '{}' is changed", beanName, factoryBeanName);
beanFactory.removeBeanDefinition(beanName);
}
}
}
private void refreshNewBean() {
Set newBeanNames = new HashSet<>();
synchronized (newScanBeanDefinitions) {
for (BeanDefinitionHolder beanDefinitionHolder : newScanBeanDefinitions) {
BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder, beanFactory);
newBeanNames.add(beanDefinitionHolder.getBeanName());
LOGGER.debug("Register new bean from scanning: {}", beanDefinitionHolder.getBeanName());
}
newScanBeanDefinitions.clear();
}
this.newBeanNames.addAll(newBeanNames);
}
private void preInstantiateSingleton() {
LOGGER.debug("preInstantiateSingleton of {}", ObjectUtils.identityToString(beanFactory));
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = null;
try {
beanDefinition = beanFactory.getBeanDefinition(beanName);
} catch (NoSuchBeanDefinitionException e) {
LOGGER.debug("bean not found: " + beanName);
continue;
}
if (beanDefinition.isSingleton()) {
try {
beanFactory.getBean(beanName);
} catch (Exception e) {
LOGGER.error("Failed to get bean: " + beanName, e);
}
}
}
}
private void refreshRequestMapping() {
// reset mvc initialized, it will update the mapping of url and handler
LOGGER.debug("refreshRequestMapping of {}", ObjectUtils.identityToString(beanFactory));
ResetRequestMappingCaches.reset(beanFactory);
}
private void processAutowiredAnnotationBeans() {
LOGGER.debug("process @Value and @Autowired of singleton beans of {}", ObjectUtils.identityToString(beanFactory));
AutowiredAnnotationProcessor.processSingletonBeanInjection(beanFactory);
}
private void processConfigBeanDefinitions() {
LOGGER.debug("process @Configuration of {}", ObjectUtils.identityToString(beanFactory));
ConfigurationClassPostProcessorEnhance.getInstance(beanFactory).postProcess(beanFactory);
}
private void clearSpringCache() {
// spring won't rebuild dependency map if injectionMetadataCache is not cleared
// which lead to singletons depend on beans in xml won't be destroy and recreate, may be a spring bug?
ResetSpringStaticCaches.reset();
ResetBeanPostProcessorCaches.reset(beanFactory);
ResetTransactionAttributeCaches.reset(beanFactory);
ResetBeanFactoryPostProcessorCaches.reset(beanFactory);
ProxyReplacer.clearAllProxies();
// fixme temperately disable it
// ResetBeanFactoryCaches.reset(beanFactory);
ConfigurationClassPostProcessorEnhance.getInstance(beanFactory).resetConfigurationClassPostProcessor(beanFactory);
ResetAnnotationCache.resetAnnotationScanner(beanFactory);
}
private void clearLocalCache() {
beansToProcess.clear();
newBeanNames.clear();
if (beanFactory instanceof BeanFactoryLifecycle) {
((BeanFactoryLifecycle) beanFactory).hotswapAgent$clearDestroyBean();
}
}
private List reloadAnnotatedBeanDefinitions(Class clazz, String[] beanNames) {
List configurationBeansToReload = new ArrayList<>();
Class realClass = ClassUtils.getUserClass(clazz);
for (String beanName : beanNames) {
if (beanName.startsWith("&")) {
beanName = beanName.substring(1);
}
BeanDefinition beanDefinition = BeanFactoryProcessor.getBeanDefinition(beanFactory, beanName);
if (AnnotationHelper.hasAnnotation(realClass, "org.springframework.context.annotation.Configuration")
&& beanDefinition.getAttribute("org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass") != null) {
configurationBeansToReload.add(beanName);
String generateBeanName = beanNameGenerator.generateBeanName(beanDefinition, beanFactory);
// the beanName is not the same as generateBeanName, it should register a new bean definition and remove the old one.
if (!beanName.equals(generateBeanName)) {
beanFactory.removeBeanDefinition(beanName);
BeanDefinition newBeanDefinition = new AnnotatedGenericBeanDefinition(realClass);
beanFactory.registerBeanDefinition(generateBeanName, newBeanDefinition);
} else {
// remove the attribute, and it will recreate the bean
((AbstractBeanDefinition) beanDefinition).setBeanClass(realClass);
beanDefinition.removeAttribute("org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass");
}
}
}
return configurationBeansToReload;
}
private void destroyBeans() {
for (String beanName : new ArrayList<>(beansToProcess)) {
destroyBean(beanName);
}
// destroy factory bean
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (isFactoryMethodAndNeedReload(beanName, beanDefinition)) {
destroyBean(beanName);
}
}
// clear destroy cache
processedBeans.clear();
destroyClasses.clear();
}
private boolean isFactoryMethodAndNeedReload(String beanName, BeanDefinition beanDefinition) {
if (beanDefinition.getFactoryMethodName() == null) {
return false;
}
if (beanDefinition.getBeanClassName() != null && destroyClasses.contains(beanDefinition.getBeanClassName())) {
LOGGER.debug("the bean '{}' of factory class '{}' is changed", beanName,
beanDefinition.getBeanClassName());
return true;
} else if (beanDefinition.getFactoryBeanName() != null && processedBeans.contains(beanDefinition.getFactoryBeanName())) {
LOGGER.debug("the bean '{}' of factory bean '{}' is changed", beanName,
beanDefinition.getFactoryBeanName());
return true;
}
return false;
}
private void destroyBean(String beanName) {
// FactoryBean case
if (beanName != null && beanName.startsWith("&") && !beanFactory.containsBeanDefinition(beanName)) {
beanName = beanName.substring(1);
}
if (processedBeans.contains(beanName)) {
return;
}
String[] dependentBeans = beanFactory.getDependentBeans(beanName);
LOGGER.debug("the bean '{}' is destroyed, and it is depended by {}", beanName, Arrays.toString(dependentBeans));
doDestroyBean(beanName);
}
private void doDestroyBean(String beanName) {
// dependentBeanMap.remove(beanName);
processedBeans.add(beanName);
Object singletonObject = beanFactory.getSingleton(beanName);
if (singletonObject != null) {
destroyClasses.add(ClassUtils.getUserClass(singletonObject).getName());
}
BeanFactoryProcessor.destroySingleton(beanFactory, beanName);
// dependentBeanMap.put(beanName, new HashSet<>(Arrays.asList(dependentBeans)));
}
private boolean containBeanDependencyAtConstruct(Constructor> constructor) {
Class>[] classes = constructor.getParameterTypes();
if (classes == null || classes.length == 0) {
return false;
}
for (Class> clazz : classes) {
if (clazz.isPrimitive()) {
continue;
}
if (clazz == String.class) {
continue;
}
String[] beanNames = beanFactory.getBeanNamesForType(clazz);
if (beanNames != null && beanNames.length > 0) {
return true;
}
}
return false;
}
private static void invokeBeanFactoryPostProcessors(DefaultListableBeanFactory factory) {
try {
LOGGER.debug("try to invoke PostProcessorRegistrationDelegate");
invokePostProcessorRegistrationDelegate(factory);
} catch (ClassNotFoundException t) {
LOGGER.debug("Failed to invoke PostProcessorRegistrationDelegate, possibly Spring version is 3.x or less, {}", t.getMessage());
invokeBeanFactoryPostProcessors0(factory);
} catch (NoSuchMethodException t) {
LOGGER.debug("Failed to invoke PostProcessorRegistrationDelegate, possibly Spring version is 3.x or less, {}", t.getMessage());
invokeBeanFactoryPostProcessors0(factory);
} catch (Exception e) {
LOGGER.error("Failed to invoke PostProcessorRegistrationDelegate", e);
throw new RuntimeException(e);
}
}
private static void invokePostProcessorRegistrationDelegate(DefaultListableBeanFactory factory) throws NoSuchMethodException,
ClassNotFoundException, InvocationTargetException, IllegalAccessException {
Class> clazz = Class.forName("org.springframework.context.support.PostProcessorRegistrationDelegate",
true, factory.getClass().getClassLoader());
Method method = clazz.getDeclaredMethod("invokeBeanFactoryPostProcessors",
ConfigurableListableBeanFactory.class, List.class);
method.setAccessible(true);
method.invoke(null, factory, Collections.emptyList());
}
private static void invokeBeanFactoryPostProcessors0(DefaultListableBeanFactory factory) {
String[] bdrppNames = factory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String name : bdrppNames) {
BeanDefinitionRegistryPostProcessor pp = factory.getBean(name, BeanDefinitionRegistryPostProcessor.class);
try {
pp.postProcessBeanDefinitionRegistry(factory);
} catch (Exception e) {
LOGGER.debug("Failed to invoke BeanDefinitionRegistryPostProcessor: {}, reason:{}",
pp.getClass().getName(), e.getMessage());
}
pp.postProcessBeanDefinitionRegistry(factory);
}
for (String name : bdrppNames) {
BeanDefinitionRegistryPostProcessor pp = factory.getBean(name, BeanDefinitionRegistryPostProcessor.class);
try {
pp.postProcessBeanFactory(factory);
} catch (Exception e) {
LOGGER.debug("Failed to invoke BeanDefinitionRegistryPostProcessor: {}, reason:{}",
pp.getClass().getName(), e.getMessage());
LOGGER.trace("Failed to invoke BeanDefinitionRegistryPostProcessor", e);
}
}
String[] bfppNames = factory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
for (String name : bfppNames) {
if (Arrays.asList(bdrppNames).contains(name)) {
continue;
}
BeanFactoryPostProcessor pp = factory.getBean(name, BeanFactoryPostProcessor.class);
try {
pp.postProcessBeanFactory(factory);
} catch (Exception e) {
LOGGER.debug("Failed to invoke BeanDefinitionRegistryPostProcessor: {}, reason:{}",
pp.getClass().getName(), e.getMessage());
LOGGER.trace("Failed to invoke BeanDefinitionRegistryPostProcessor", e);
}
}
}
private static void addBeanPostProcessors(DefaultListableBeanFactory factory) {
String[] names = factory.getBeanNamesForType(BeanPostProcessor.class, true, false);
LOGGER.debug("try to add BeanPostProcessor: {}", Arrays.asList(names));
for (String name : names) {
BeanPostProcessor pp = factory.getBean(name, BeanPostProcessor.class);
factory.addBeanPostProcessor(pp);
LOGGER.trace("Add BeanPostProcessor '{}' that mapping to {}", name, ObjectUtils.identityToString(pp));
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("SpringBeanReload{");
sb.append("classes=").append(classes);
sb.append(", properties=").append(properties);
sb.append(", yamls=").append(yamls);
sb.append(", xmls=").append(xmls);
sb.append(", newScanBeanDefinitions=").append(newScanBeanDefinitions);
sb.append(", changedBeanNames=").append(changedBeanNames);
sb.append('}');
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy