diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanNameAutoProxyCreator.java b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanNameAutoProxyCreator.java index 27aa0547363..fd6acca5ea7 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanNameAutoProxyCreator.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/autoproxy/BeanNameAutoProxyCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-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. @@ -114,10 +114,10 @@ public class BeanNameAutoProxyCreator extends AbstractAutoProxyCreator { boolean isFactoryBean = FactoryBean.class.isAssignableFrom(beanClass); for (String mappedName : this.beanNames) { if (isFactoryBean) { - if (!mappedName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { + if (mappedName.isEmpty() || mappedName.charAt(0) != BeanFactory.FACTORY_BEAN_PREFIX_CHAR) { continue; } - mappedName = mappedName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); + mappedName = mappedName.substring(1); // length of '&' } if (isMatch(beanName, mappedName)) { return true; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index fb1f3ffd9df..1767442f802 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-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. @@ -124,9 +124,16 @@ public interface BeanFactory { * beans created by the FactoryBean. For example, if the bean named * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject} * will return the factory, not the instance returned by the factory. + * @see #FACTORY_BEAN_PREFIX_CHAR */ String FACTORY_BEAN_PREFIX = "&"; + /** + * Character variant of {@link #FACTORY_BEAN_PREFIX}. + * @since 6.2.6 + */ + char FACTORY_BEAN_PREFIX_CHAR = '&'; + /** * Return an instance, which may be shared or independent, of the specified bean. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java index 8d4b53c628b..b511489e13f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactoryUtils.java @@ -64,14 +64,6 @@ public abstract class BeanFactoryUtils { private static final Map transformedBeanNameCache = new ConcurrentHashMap<>(); - /** - * Used to dereference a {@link FactoryBean} instance and distinguish it from - * beans created by the FactoryBean. For example, if the bean named - * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject} - * will return the factory, not the instance returned by the factory. - */ - private static final char FACTORY_BEAN_PREFIX = BeanFactory.FACTORY_BEAN_PREFIX.charAt(0); - /** * Return whether the given name is a factory dereference * (beginning with the factory dereference prefix). @@ -92,14 +84,14 @@ public abstract class BeanFactoryUtils { */ public static String transformedBeanName(String name) { Assert.notNull(name, "'name' must not be null"); - if (!isFactoryDereference(name)) { + if (name.isEmpty() || name.charAt(0) != BeanFactory.FACTORY_BEAN_PREFIX_CHAR) { return name; } return transformedBeanNameCache.computeIfAbsent(name, beanName -> { do { - beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); + beanName = beanName.substring(1); // length of '&' } - while (isFactoryDereference(beanName)); + while (beanName.charAt(0) == BeanFactory.FACTORY_BEAN_PREFIX_CHAR); return beanName; }); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index e54c8a72b78..b9d66e1481c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -770,16 +770,16 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp public String[] getAliases(String name) { String beanName = transformedBeanName(name); List aliases = new ArrayList<>(); - boolean factoryPrefix = name.startsWith(FACTORY_BEAN_PREFIX); + boolean hasFactoryPrefix = (!name.isEmpty() && name.charAt(0) == BeanFactory.FACTORY_BEAN_PREFIX_CHAR); String fullBeanName = beanName; - if (factoryPrefix) { + if (hasFactoryPrefix) { fullBeanName = FACTORY_BEAN_PREFIX + beanName; } if (!fullBeanName.equals(name)) { aliases.add(fullBeanName); } String[] retrievedAliases = super.getAliases(beanName); - String prefix = (factoryPrefix ? FACTORY_BEAN_PREFIX : ""); + String prefix = (hasFactoryPrefix ? FACTORY_BEAN_PREFIX : ""); for (String retrievedAlias : retrievedAliases) { String alias = prefix + retrievedAlias; if (!alias.equals(name)) { @@ -1292,7 +1292,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp */ protected String originalBeanName(String name) { String beanName = transformedBeanName(name); - if (name.startsWith(FACTORY_BEAN_PREFIX)) { + if (!name.isEmpty() && name.charAt(0) == BeanFactory.FACTORY_BEAN_PREFIX_CHAR) { beanName = FACTORY_BEAN_PREFIX + beanName; } return beanName; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 5a085d8d41b..9db1a1253a2 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -364,9 +364,9 @@ class ConfigurationClassEnhancer { // proxy that intercepts calls to getObject() and returns any cached bean instance. // This ensures that the semantics of calling a FactoryBean from within @Bean methods // is the same as that of referring to a FactoryBean within XML. See SPR-6602. - if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) && - factoryContainsBean(beanFactory, beanName)) { - Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName); + String factoryBeanName = BeanFactory.FACTORY_BEAN_PREFIX + beanName; + if (factoryContainsBean(beanFactory, factoryBeanName) && factoryContainsBean(beanFactory, beanName)) { + Object factoryBean = beanFactory.getBean(factoryBeanName); if (factoryBean instanceof ScopedProxyFactoryBean) { // Scoped proxy factory beans are a special case and should not be further proxied } diff --git a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java index ec845f54d85..4db77128c49 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-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. @@ -932,8 +932,8 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo */ private boolean isExcluded(String beanName) { return (this.excludedBeans.contains(beanName) || - (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX) && - this.excludedBeans.contains(beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length())))); + (!beanName.isEmpty() && (beanName.charAt(0) == BeanFactory.FACTORY_BEAN_PREFIX_CHAR) && + this.excludedBeans.contains(beanName.substring(1)))); // length of '&' } /**