Browse Source

Don't use RestartClassLoader when proxying classes it did not load

Fixes gh-19010
Fixes gh-25367
pull/25709/head
Andy Wilkinson 5 years ago
parent
commit
952ac7b8d2
  1. 1
      spring-boot-project/spring-boot-devtools/build.gradle
  2. 5
      spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/RestartClassLoader.java
  3. 50
      spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/classloader/RestartClassLoaderTests.java

1
spring-boot-project/spring-boot-devtools/build.gradle

@ -70,6 +70,7 @@ dependencies { @@ -70,6 +70,7 @@ dependencies {
testImplementation("org.thymeleaf:thymeleaf-spring5")
testImplementation("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect")
testRuntimeOnly("org.aspectj:aspectjweaver")
testRuntimeOnly("org.yaml:snakeyaml")
}

5
spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/classloader/RestartClassLoader.java

@ -173,6 +173,11 @@ public class RestartClassLoader extends URLClassLoader implements SmartClassLoad @@ -173,6 +173,11 @@ public class RestartClassLoader extends URLClassLoader implements SmartClassLoad
return defineClass(name, b, 0, b.length, protectionDomain);
}
@Override
public ClassLoader getOriginalClassLoader() {
return getParent();
}
private URL createFileUrl(String name, ClassLoaderFile file) {
try {
return new URL("reloaded", null, -1, "/" + name, new ClassLoaderFileURLStreamHandler(file));

50
spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/restart/classloader/RestartClassLoaderTests.java

@ -20,6 +20,7 @@ import java.io.File; @@ -20,6 +20,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
@ -36,7 +37,14 @@ import org.junit.jupiter.api.Test; @@ -36,7 +37,14 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.boot.devtools.restart.classloader.ClassLoaderFile.Kind;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;
@ -210,6 +218,20 @@ class RestartClassLoaderTests { @@ -210,6 +218,20 @@ class RestartClassLoaderTests {
// Warning would happen outside the boundary of the test
}
@Test
void packagePrivateClassLoadedByParentClassLoaderCanBeProxied() throws MalformedURLException {
new ApplicationContextRunner()
.withClassLoader(new RestartClassLoader(ExampleTransactional.class.getClassLoader(),
new URL[] { this.sampleJarFile.toURI().toURL() }, this.updatedFiles))
.withUserConfiguration(ProxyConfiguration.class).run((context) -> {
assertThat(context).hasNotFailed();
ExampleTransactional transactional = context.getBean(ExampleTransactional.class);
assertThat(AopUtils.isCglibProxy(transactional)).isTrue();
assertThat(transactional.getClass().getClassLoader())
.isEqualTo(ExampleTransactional.class.getClassLoader());
});
}
private String readString(InputStream in) throws IOException {
return new String(FileCopyUtils.copyToByteArray(in));
}
@ -218,4 +240,32 @@ class RestartClassLoaderTests { @@ -218,4 +240,32 @@ class RestartClassLoaderTests {
return (enumeration != null) ? Collections.list(enumeration) : Collections.emptyList();
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@EnableTransactionManagement
static class ProxyConfiguration {
@Bean
ExampleTransactional exampleTransactional() {
return new ExampleTransactional();
}
}
static class ExampleTransactional implements ExampleInterface {
@Override
@Transactional
public String doIt() {
return "hello";
}
}
interface ExampleInterface {
String doIt();
}
}

Loading…
Cancel
Save