Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid duplicate AOP proxy class definition with FilteredClassLoader #28531

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand All @@ -20,12 +20,14 @@
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.function.Predicate;

import org.springframework.core.SmartClassLoader;
import org.springframework.core.io.ClassPathResource;

/**
Expand All @@ -37,7 +39,7 @@
* @author Roy Jacobs
* @since 2.0.0
*/
public class FilteredClassLoader extends URLClassLoader {
public class FilteredClassLoader extends URLClassLoader implements SmartClassLoader {

private final Collection<Predicate<String>> classesFilters;

Expand Down Expand Up @@ -129,6 +131,16 @@ public InputStream getResourceAsStream(String 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.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand All @@ -26,6 +26,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

/**
* Tests for {@link FilteredClassLoader}.
Expand Down Expand Up @@ -111,4 +112,13 @@ void loadResourceAsStreamWhenNotFilteredShouldLoadResource() throws Exception {
}
}

@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));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
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.assertj.ApplicationContextAssertProvider;
import org.springframework.context.ConfigurableApplicationContext;
Expand All @@ -36,6 +38,7 @@
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
Expand Down Expand Up @@ -168,6 +171,15 @@ void runWithClassLoaderShouldSetClassLoaderOnConditionContext() {
.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
void thrownRuleWorksWithCheckedException() {
get().run((context) -> assertThatIOException().isThrownBy(() -> throwCheckedException("Expected message"))
Expand Down Expand Up @@ -260,6 +272,30 @@ static class ConditionalConfig {

}

@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 {

@Override
Expand Down