mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-23 01:39:02 +00:00
Merge branch '3.5.x' into 4.0.x
Closes gh-50416
This commit is contained in:
+19
-6
@@ -23,10 +23,11 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.actuate.endpoint.OperationResponseBody;
|
||||
import org.springframework.boot.actuate.endpoint.SanitizableData;
|
||||
@@ -140,14 +141,14 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
||||
|
||||
private ContextConfigurationPropertiesDescriptor describeBeans(ApplicationContext context,
|
||||
Predicate<ConfigurationPropertiesBean> beanFilterPredicate, boolean showUnsanitized) {
|
||||
ApplicationContext parent = context.getParent();
|
||||
Map<String, ConfigurationPropertiesBean> beans = ConfigurationPropertiesBean.getAll(context);
|
||||
Map<String, ConfigurationPropertiesBeanDescriptor> descriptors = beans.values()
|
||||
Map<String, ConfigurationPropertiesBeanDescriptor> descriptors = new LinkedHashMap<>();
|
||||
beans.values()
|
||||
.stream()
|
||||
.filter(beanFilterPredicate)
|
||||
.collect(Collectors.toMap(ConfigurationPropertiesBean::getName,
|
||||
(bean) -> describeBean(bean, showUnsanitized)));
|
||||
return new ContextConfigurationPropertiesDescriptor(descriptors,
|
||||
(context.getParent() != null) ? context.getParent().getId() : null);
|
||||
.forEach((bean) -> descriptors.put(bean.getName(), describeBean(bean, showUnsanitized)));
|
||||
return new ContextConfigurationPropertiesDescriptor(descriptors, (parent != null) ? parent.getId() : null);
|
||||
}
|
||||
|
||||
private ConfigurationPropertiesBeanDescriptor describeBean(ConfigurationPropertiesBean bean,
|
||||
@@ -168,6 +169,18 @@ public class ConfigurationPropertiesReportEndpoint implements ApplicationContext
|
||||
*/
|
||||
private Map<String, @Nullable Object> safeSerialize(@Nullable Object bean, String prefix) {
|
||||
try {
|
||||
if (bean instanceof Advised advised) {
|
||||
TargetSource targetSource = advised.getTargetSource();
|
||||
Object target = targetSource.getTarget();
|
||||
try {
|
||||
return safeSerialize(target, prefix);
|
||||
}
|
||||
finally {
|
||||
if (target != null) {
|
||||
targetSource.releaseTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new HashMap<>(this.serializer.serialize(bean));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
||||
+116
@@ -30,6 +30,8 @@ import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesBeanDescriptor;
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ConfigurationPropertiesDescriptor;
|
||||
import org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint.ContextConfigurationPropertiesDescriptor;
|
||||
@@ -246,6 +248,58 @@ class ConfigurationPropertiesReportEndpointSerializationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void aopProxyDoesNotLeakAdvisedInternals() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(CglibProxiedFooConfig.class)
|
||||
.withPropertyValues("foo.name:test");
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
ConfigurationPropertiesDescriptor applicationProperties = endpoint.configurationProperties();
|
||||
ConfigurationPropertiesBeanDescriptor foo = getContextDescriptor(context, applicationProperties).getBeans()
|
||||
.get("foo");
|
||||
assertThat(foo).isNotNull();
|
||||
Map<String, @Nullable Object> map = foo.getProperties();
|
||||
assertThat(map).containsOnlyKeys("name", "bar");
|
||||
assertThat(map).containsEntry("name", "test");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void aopProxyTargetingAnotherProxyIsUnwrapped() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(NestedCglibProxiedFooConfig.class)
|
||||
.withPropertyValues("foo.name:nested");
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
ConfigurationPropertiesDescriptor applicationProperties = endpoint.configurationProperties();
|
||||
ConfigurationPropertiesBeanDescriptor foo = getContextDescriptor(context, applicationProperties).getBeans()
|
||||
.get("foo");
|
||||
assertThat(foo).isNotNull();
|
||||
assertThat(foo.getProperties()).containsOnlyKeys("name", "bar");
|
||||
assertThat(foo.getProperties()).containsEntry("name", "nested");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void aopProxyWithUnresolvableTarget() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(UnresolvableTargetFooConfig.class);
|
||||
contextRunner.run((context) -> {
|
||||
ConfigurationPropertiesReportEndpoint endpoint = context
|
||||
.getBean(ConfigurationPropertiesReportEndpoint.class);
|
||||
ConfigurationPropertiesDescriptor applicationProperties = endpoint.configurationProperties();
|
||||
assertThat(getContextDescriptor(context, applicationProperties).getBeans()).containsKey("foo")
|
||||
.satisfies((beans) -> {
|
||||
ConfigurationPropertiesBeanDescriptor bean = beans.get("foo");
|
||||
assertThat(bean).isNotNull();
|
||||
assertThat(bean.getProperties()).containsEntry("error", "Cannot serialize 'foo'");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void hikariDataSourceConfigurationPropertiesBeanCanBeSerialized() {
|
||||
ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
@@ -578,6 +632,68 @@ class ConfigurationPropertiesReportEndpointSerializationTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(Base.class)
|
||||
static class CglibProxiedFooConfig {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("foo")
|
||||
Foo foo() {
|
||||
ProxyFactory proxyFactory = new ProxyFactory(new Foo());
|
||||
proxyFactory.setProxyTargetClass(true);
|
||||
return (Foo) proxyFactory.getProxy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(Base.class)
|
||||
static class NestedCglibProxiedFooConfig {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("foo")
|
||||
Foo foo() {
|
||||
ProxyFactory inner = new ProxyFactory(new Foo());
|
||||
inner.setProxyTargetClass(true);
|
||||
ProxyFactory outer = new ProxyFactory(inner.getProxy());
|
||||
outer.setProxyTargetClass(true);
|
||||
return (Foo) outer.getProxy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import(Base.class)
|
||||
static class UnresolvableTargetFooConfig {
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("foo")
|
||||
Foo foo() {
|
||||
ProxyFactory proxyFactory = new ProxyFactory();
|
||||
proxyFactory.setProxyTargetClass(true);
|
||||
proxyFactory.setTargetSource(new TargetSource() {
|
||||
|
||||
@Override
|
||||
public Class<?> getTargetClass() {
|
||||
return Foo.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStatic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTarget() throws Exception {
|
||||
throw new IllegalStateException("no target");
|
||||
}
|
||||
|
||||
});
|
||||
return (Foo) proxyFactory.getProxy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
static class HikariDataSourceConfig {
|
||||
|
||||
Reference in New Issue
Block a user