Merge pull request #28531 from dreis2211

* pr/28531:
  Polish contribution
  Avoid duplicate AOP proxy class definition with FilteredClassLoader

Closes gh-28531
This commit is contained in:
Stephane Nicoll 2021-11-06 12:03:48 +01:00
commit 034cf6ac34
3 changed files with 62 additions and 3 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,12 +20,14 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.springframework.core.SmartClassLoader;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
/** /**
@ -37,7 +39,7 @@ import org.springframework.core.io.ClassPathResource;
* @author Roy Jacobs * @author Roy Jacobs
* @since 2.0.0 * @since 2.0.0
*/ */
public class FilteredClassLoader extends URLClassLoader { public class FilteredClassLoader extends URLClassLoader implements SmartClassLoader {
private final Collection<Predicate<String>> classesFilters; private final Collection<Predicate<String>> classesFilters;
@ -129,6 +131,16 @@ public class FilteredClassLoader extends URLClassLoader {
return super.getResourceAsStream(name); return super.getResourceAsStream(name);
} }
@Override
public Class<?> publicDefineClass(String name, byte[] b, ProtectionDomain protectionDomain) {
for (Predicate<String> filter : this.classesFilters) {
if (filter.test(name)) {
throw new IllegalArgumentException(String.format("Defining class with name %s is not supported", name));
}
}
return defineClass(name, b, 0, b.length, protectionDomain);
}
/** /**
* Filter to restrict the classes that can be loaded. * Filter to restrict the classes that can be loaded.
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,6 +26,7 @@ import org.springframework.core.io.ClassPathResource;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/** /**
* Tests for {@link FilteredClassLoader}. * Tests for {@link FilteredClassLoader}.
@ -111,4 +112,13 @@ class FilteredClassLoaderTests {
} }
} }
@Test
void publicDefineClassWhenFilteredThrowsException() throws Exception {
Class<FilteredClassLoaderTests> hiddenClass = FilteredClassLoaderTests.class;
try (FilteredClassLoader classLoader = new FilteredClassLoader(hiddenClass)) {
assertThatIllegalArgumentException()
.isThrownBy(() -> classLoader.publicDefineClass(hiddenClass.getName(), new byte[] {}, null));
}
}
} }

View File

@ -24,7 +24,10 @@ import com.google.gson.Gson;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.annotation.UserConfigurations; import org.springframework.boot.context.annotation.UserConfigurations;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.ApplicationContextAssertProvider; import org.springframework.boot.test.context.assertj.ApplicationContextAssertProvider;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
@ -33,6 +36,7 @@ import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -165,6 +169,15 @@ abstract class AbstractApplicationContextRunnerTests<T extends AbstractApplicati
.run((context) -> assertThat(context).hasSingleBean(ConditionalConfig.class)); .run((context) -> assertThat(context).hasSingleBean(ConditionalConfig.class));
} }
@Test
void consecutiveRunWithFilteredClassLoaderShouldHaveBeanWithLazyProperties() {
get().withClassLoader(new FilteredClassLoader(Gson.class)).withUserConfiguration(LazyConfig.class)
.run((context) -> assertThat(context).hasSingleBean(ExampleBeanWithLazyProperties.class));
get().withClassLoader(new FilteredClassLoader(Gson.class)).withUserConfiguration(LazyConfig.class)
.run((context) -> assertThat(context).hasSingleBean(ExampleBeanWithLazyProperties.class));
}
@Test @Test
void thrownRuleWorksWithCheckedException() { void thrownRuleWorksWithCheckedException() {
get().run((context) -> assertThatIOException().isThrownBy(() -> throwCheckedException("Expected message")) get().run((context) -> assertThatIOException().isThrownBy(() -> throwCheckedException("Expected message"))
@ -241,6 +254,30 @@ abstract class AbstractApplicationContextRunnerTests<T extends AbstractApplicati
} }
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ExampleProperties.class)
static class LazyConfig {
@Bean
ExampleBeanWithLazyProperties exampleBeanWithLazyProperties() {
return new ExampleBeanWithLazyProperties();
}
}
static class ExampleBeanWithLazyProperties {
@Autowired
@Lazy
ExampleProperties exampleProperties;
}
@ConfigurationProperties
public static class ExampleProperties {
}
static class FilteredClassLoaderCondition implements Condition { static class FilteredClassLoaderCondition implements Condition {
@Override @Override