Support for pre-generated CGLIB proxy classes (in AOT scenarios)

Includes runtime storing of generated classes to a directory specified by the "cglib.generatedClasses" system property. Avoids lazy CGLIB fast-class generation and replaces generated Enhancer and MethodWrapper key classes with equivalent record types. Introduces support for early type determination in InstantiationStrategy, AopProxy and SmartInstantiationAwareBeanPostProcessor - in order to trigger CGLIB class generation in refreshForAotProcessing (through early determineBeanType calls for bean definitions).

Closes gh-28115
This commit is contained in:
Juergen Hoeller
2022-08-10 23:30:19 +02:00
parent 496b1879ab
commit b31a15851e
25 changed files with 364 additions and 173 deletions

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2003,2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cglib.core;
import java.lang.reflect.Method;
import java.util.*;
@SuppressWarnings({"rawtypes", "unchecked"})
public class MethodWrapper {
// SPRING PATCH BEGIN
private record MethodWrapperKey(String name, List<String> parameterTypes, String returnType) {
}
// SPRING PATCH END
private MethodWrapper() {
}
public static Object create(Method method) {
// SPRING PATCH BEGIN
return new MethodWrapperKey(method.getName(),
Arrays.asList(ReflectUtils.getNames(method.getParameterTypes())),
method.getReturnType().getName());
// SPRING PATCH END
}
public static Set createSet(Collection methods) {
Set set = new HashSet();
for (Iterator it = methods.iterator(); it.hasNext();) {
set.add(create((Method)it.next()));
}
return set;
}
}

View File

@@ -20,12 +20,16 @@ import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
@@ -444,6 +448,15 @@ public class ReflectUtils {
Class c = null;
Throwable t = THROWABLE;
String generatedClasses = System.getProperty("cglib.generatedClasses");
if (generatedClasses != null) {
Path path = Path.of(generatedClasses + "/" + className.replace(".", "/") + ".class");
Files.createDirectories(path.getParent());
try (OutputStream os = Files.newOutputStream(path)) {
new ByteArrayInputStream(b).transferTo(os);
}
}
// Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches
if (contextClass != null && contextClass.getClassLoader() == loader) {
try {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2022 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.
@@ -17,23 +17,36 @@
package org.springframework.cglib.core;
/**
* Custom extension of CGLIB's {@link DefaultNamingPolicy}, modifying
* the tag in generated class names from "ByCGLIB" to "BySpringCGLIB".
* Custom variant of CGLIB's {@link DefaultNamingPolicy}, modifying the tag
* in generated class names from "EnhancerByCGLIB" etc to a "SpringCGLIB" tag
* and using a plain counter suffix instead of a hash code suffix (as of 6.0).
*
* <p>This is primarily designed to avoid clashes between a regular CGLIB
* version (used by some other library) and Spring's embedded variant,
* in case the same class happens to get proxied for different purposes.
* <p>This allows for reliably discovering pre-generated Spring proxy classes
* in the classpath (as written at runtime when the "cglib.generatedClasses"
* system property points to a specific directory to store the proxy classes).
*
* @author Juergen Hoeller
* @since 3.2.8
* @since 3.2.8 / 6.0
*/
public class SpringNamingPolicy extends DefaultNamingPolicy {
public final class SpringNamingPolicy implements NamingPolicy {
public static final SpringNamingPolicy INSTANCE = new SpringNamingPolicy();
@Override
protected String getTag() {
return "BySpringCGLIB";
private SpringNamingPolicy() {
}
public String getClassName(String prefix, String source, Object key, Predicate names) {
if (prefix == null) {
prefix = "org.springframework.cglib.empty.Object";
} else if (prefix.startsWith("java")) {
prefix = "_" + prefix;
}
String base = prefix + "$$SpringCGLIB$$";
int index = 0;
String attempt = base + index;
while (names.evaluate(attempt))
attempt = base + index++;
return attempt;
}
}

View File

@@ -100,9 +100,6 @@ public class Enhancer extends AbstractClassGenerator {
private static final Source SOURCE = new Source(Enhancer.class.getName());
private static final EnhancerKey KEY_FACTORY =
(EnhancerKey) KeyFactory.create(EnhancerKey.class, KeyFactory.HASH_ASM_TYPE, null);
private static final String BOUND_FIELD = "CGLIB$BOUND";
private static final String FACTORY_DATA_FIELD = "CGLIB$FACTORY_DATA";
@@ -197,19 +194,16 @@ public class Enhancer extends AbstractClassGenerator {
private Object currentKey;
/**
* Internal interface, only public due to ClassLoader issues.
*/
public interface EnhancerKey {
public Object newInstance(String type,
String[] interfaces,
// SPRING PATCH BEGIN
private record EnhancerKey(String type,
List<String> interfaces,
WeakCacheKey<CallbackFilter> filter,
Type[] callbackTypes,
List<Type> callbackTypes,
boolean useFactory,
boolean interceptDuringConstruction,
Long serialVersionUID);
Long serialVersionUID) {
}
// SPRING PATCH END
private Class[] interfaces;
@@ -561,13 +555,15 @@ public class Enhancer extends AbstractClassGenerator {
private Object createHelper() {
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
// SPRING PATCH BEGIN
Object key = new EnhancerKey((superclass != null) ? superclass.getName() : null,
(interfaces != null ? Arrays.asList(ReflectUtils.getNames(interfaces)) :null),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
Arrays.asList(callbackTypes),
useFactory,
interceptDuringConstruction,
serialVersionUID);
// SPRING PATCH END
this.currentKey = key;
Object result = super.create(key);
return result;