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:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user