MethodHandles.Lookup.defineClass for CGLIB class definition purposes

Spring's CGLIB fork is patched with local copies of affected files here, introducing the notion of a "contextClass" (e.g. the proxy superclass) which gets passed through to ReflectUtils.defineClass for delegating to MethodHandles.Lookup.defineClass eventually, against a privateLookupIn(contextClass) lookup context on JDK 9/10/11.

Issue: SPR-15859
This commit is contained in:
Juergen Hoeller 2018-04-11 12:47:55 +02:00
parent cdaa247861
commit 61c3db0869
15 changed files with 3153 additions and 206 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@ -205,9 +205,8 @@ class CglibAopProxy implements AopProxy, Serializable {
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
this.advised.getTargetClass() + "]: " +
"Common causes of this problem include using a final class or a non-visible class",
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
@ -743,7 +742,7 @@ class CglibAopProxy implements AopProxy, Serializable {
*/
@Override
protected Object invokeJoinpoint() throws Throwable {
if (this.publicMethod) {
if (this.publicMethod && getMethod().getDeclaringClass() != Object.class) {
return this.methodProxy.invoke(this.target, this.arguments);
}
else {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -82,22 +82,24 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
@Test
public void testRejectsPerCflowAspect() {
try {
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(),"someBean"));
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowAspect(), "someBean"));
fail("Cannot accept cflow");
}
catch (AopConfigException ex) {
assertTrue(ex.getMessage().indexOf("PERCFLOW") != -1);
assertTrue(ex.getMessage().contains("PERCFLOW"));
}
}
@Test
public void testRejectsPerCflowBelowAspect() {
try {
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(),"someBean"));
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new PerCflowBelowAspect(), "someBean"));
fail("Cannot accept cflowbelow");
}
catch (AopConfigException ex) {
assertTrue(ex.getMessage().indexOf("PERCFLOWBELOW") != -1);
assertTrue(ex.getMessage().contains("PERCFLOWBELOW"));
}
}
@ -227,9 +229,7 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
int realAge = 65;
target.setAge(realAge);
PerTypeWithinAspectInstanceFactory aif = new PerTypeWithinAspectInstanceFactory();
TestBean itb = (TestBean) createProxy(target,
getFixture().getAdvisors(aif),
TestBean.class);
TestBean itb = (TestBean) createProxy(target, getFixture().getAdvisors(aif), TestBean.class);
assertEquals("No method calls", 0, aif.getInstantiationCount());
assertEquals("Around advice must now apply", 0, itb.getAge());
@ -257,9 +257,7 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
assertEquals("Around advice must still apply", 1, itb.getAge());
assertEquals("Around advice must still apply", 2, itb.getAge());
TestBean itb2 = (TestBean) createProxy(target,
getFixture().getAdvisors(aif),
TestBean.class);
TestBean itb2 = (TestBean) createProxy(target, getFixture().getAdvisors(aif), TestBean.class);
assertEquals(1, aif.getInstantiationCount());
assertEquals("Around advice be independent for second instance", 0, itb2.getAge());
assertEquals(2, aif.getInstantiationCount());
@ -284,7 +282,8 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
public void testNamedPointcutFromAspectLibraryWithBinding() {
TestBean target = new TestBean();
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new NamedPointcutAspectFromLibraryWithBinding(),"someBean")),
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(
new NamedPointcutAspectFromLibraryWithBinding(), "someBean")),
ITestBean.class);
itb.setAge(10);
assertEquals("Around advice must apply", 20, itb.getAge());
@ -296,7 +295,7 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
int realAge = 65;
target.setAge(realAge);
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspectInstance,"someBean")),
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, "someBean")),
ITestBean.class);
assertEquals("Around advice must apply", -1, itb.getAge());
assertEquals(realAge, target.getAge());
@ -306,7 +305,8 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
public void testBindingWithSingleArg() {
TestBean target = new TestBean();
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new BindingAspectWithSingleArg(),"someBean")),
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new BindingAspectWithSingleArg(), "someBean")),
ITestBean.class);
itb.setAge(10);
assertEquals("Around advice must apply", 20, itb.getAge());
@ -317,7 +317,8 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
public void testBindingWithMultipleArgsDifferentlyOrdered() {
ManyValuedArgs target = new ManyValuedArgs();
ManyValuedArgs mva = (ManyValuedArgs) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new ManyValuedArgs(),"someBean")),
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new ManyValuedArgs(), "someBean")),
ManyValuedArgs.class);
String a = "a";
@ -338,7 +339,7 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
assertFalse(notLockableTarget instanceof Lockable);
NotLockable notLockable1 = (NotLockable) createProxy(notLockableTarget,
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(),"someBean")),
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
NotLockable.class);
assertTrue(notLockable1 instanceof Lockable);
Lockable lockable = (Lockable) notLockable1;
@ -349,7 +350,7 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
NotLockable notLockable2Target = new NotLockable();
NotLockable notLockable2 = (NotLockable) createProxy(notLockable2Target,
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(),"someBean")),
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
NotLockable.class);
assertTrue(notLockable2 instanceof Lockable);
Lockable lockable2 = (Lockable) notLockable2;
@ -369,10 +370,10 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
public void testIntroductionAdvisorExcludedFromTargetImplementingInterface() {
assertTrue(AopUtils.findAdvisorsThatCanApply(
getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(
new MakeLockable(),"someBean")),
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
CannotBeUnlocked.class).isEmpty());
assertEquals(2, AopUtils.findAdvisorsThatCanApply(getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class).size());
assertEquals(2, AopUtils.findAdvisorsThatCanApply(getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(),"someBean")), NotLockable.class).size());
}
@Test
@ -408,42 +409,34 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")),
List.class
),
CannotBeUnlocked.class);
List.class);
assertFalse("Type pattern must have excluded mixin", proxy instanceof Lockable);
}
/* prereq AspectJ 1.6.7
@Test
public void testIntroductionBasedOnAnnotationMatch_Spr5307() {
public void testIntroductionBasedOnAnnotationMatch_SPR5307() {
AnnotatedTarget target = new AnnotatedTargetImpl();
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeAnnotatedTypeModifiable(),"someBean"));
Object proxy = createProxy(target,
advisors,
AnnotatedTarget.class);
new SingletonMetadataAwareAspectInstanceFactory(new MakeAnnotatedTypeModifiable(), "someBean"));
Object proxy = createProxy(target, advisors, AnnotatedTarget.class);
System.out.println(advisors.get(1));
assertTrue(proxy instanceof Lockable);
Lockable lockable = (Lockable)proxy;
lockable.locked();
}
*/
// TODO: Why does this test fail? It hasn't been run before, so it maybe never actually passed...
@Test
@Ignore
public void testIntroductionWithArgumentBinding() {
TestBean target = new TestBean();
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeITestBeanModifiable(),"someBean"));
new SingletonMetadataAwareAspectInstanceFactory(new MakeITestBeanModifiable(), "someBean"));
advisors.addAll(getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(),"someBean")));
new SingletonMetadataAwareAspectInstanceFactory(new MakeLockable(), "someBean")));
Modifiable modifiable = (Modifiable) createProxy(target,
advisors,
ITestBean.class);
Modifiable modifiable = (Modifiable) createProxy(target, advisors, ITestBean.class);
assertThat(modifiable, instanceOf(Modifiable.class));
Lockable lockable = (Lockable) modifiable;
assertFalse(lockable.locked());
@ -477,11 +470,11 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
public void testAspectMethodThrowsExceptionLegalOnSignature() {
TestBean target = new TestBean();
UnsupportedOperationException expectedException = new UnsupportedOperationException();
List<Advisor> advisors = getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new ExceptionAspect(expectedException),"someBean"));
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new ExceptionAspect(expectedException), "someBean"));
assertEquals("One advice method was found", 1, advisors.size());
ITestBean itb = (ITestBean) createProxy(target,
advisors,
ITestBean.class);
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
try {
itb.getAge();
fail();
@ -497,11 +490,11 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
public void testAspectMethodThrowsExceptionIllegalOnSignature() {
TestBean target = new TestBean();
RemoteException expectedException = new RemoteException();
List<Advisor> advisors = getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new ExceptionAspect(expectedException),"someBean"));
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(new ExceptionAspect(expectedException), "someBean"));
assertEquals("One advice method was found", 1, advisors.size());
ITestBean itb = (ITestBean) createProxy(target,
advisors,
ITestBean.class);
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
try {
itb.getAge();
fail();
@ -522,10 +515,7 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
// Required everywhere we use AspectJ proxies
pf.addAdvice(ExposeInvocationInterceptor.INSTANCE);
for (Object a : advisors) {
pf.addAdvisor((Advisor) a);
}
pf.addAdvisors(advisors);
pf.setExposeProxy(true);
return pf.getProxy();
@ -534,13 +524,11 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
@Test
public void testTwoAdvicesOnOneAspect() {
TestBean target = new TestBean();
TwoAdviceAspect twoAdviceAspect = new TwoAdviceAspect();
List<Advisor> advisors = getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(twoAdviceAspect,"someBean"));
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(twoAdviceAspect, "someBean"));
assertEquals("Two advice methods found", 2, advisors.size());
ITestBean itb = (ITestBean) createProxy(target,
advisors,
ITestBean.class);
ITestBean itb = (ITestBean) createProxy(target, advisors, ITestBean.class);
itb.setName("");
assertEquals(0, itb.getAge());
int newAge = 32;
@ -551,16 +539,15 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
@Test
public void testAfterAdviceTypes() throws Exception {
Echo target = new Echo();
ExceptionHandling afterReturningAspect = new ExceptionHandling();
List<Advisor> advisors = getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(afterReturningAspect,"someBean"));
Echo echo = (Echo) createProxy(target,
advisors,
Echo.class);
List<Advisor> advisors = getFixture().getAdvisors(
new SingletonMetadataAwareAspectInstanceFactory(afterReturningAspect, "someBean"));
Echo echo = (Echo) createProxy(target, advisors, Echo.class);
assertEquals(0, afterReturningAspect.successCount);
assertEquals("", echo.echo(""));
assertEquals(1, afterReturningAspect.successCount);
assertEquals(0, afterReturningAspect.failureCount);
try {
echo.echo(new FileNotFoundException());
fail();
@ -580,9 +567,9 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
public void testFailureWithoutExplicitDeclarePrecedence() {
TestBean target = new TestBean();
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
new NoDeclarePrecedenceShouldFail(), "someBean");
new NoDeclarePrecedenceShouldFail(), "someBean");
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
itb.getAge();
}
@ -590,21 +577,10 @@ public abstract class AbstractAspectJAdvisorFactoryTests {
public void testDeclarePrecedenceNotSupported() {
TestBean target = new TestBean();
MetadataAwareAspectInstanceFactory aspectInstanceFactory = new SingletonMetadataAwareAspectInstanceFactory(
new DeclarePrecedenceShouldSucceed(), "someBean");
createProxy(target, getFixture().getAdvisors(aspectInstanceFactory),
ITestBean.class);
new DeclarePrecedenceShouldSucceed(), "someBean");
createProxy(target, getFixture().getAdvisors(aspectInstanceFactory), ITestBean.class);
}
/** Not supported in 2.0!
public void testExplicitDeclarePrecedencePreventsFailure() {
TestBean target = new TestBean();
ITestBean itb = (ITestBean) createProxy(target,
getFixture().getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(new DeclarePrecedenceShouldSucceed(), "someBean")),
ITestBean.class);
assertEquals(666, itb.getAge());
}
*/
@Aspect("percflow(execution(* *(..)))")
public static class PerCflowAspect {
@ -1019,9 +995,7 @@ class MakeLockable {
public static Lockable mixin;
@Before(value="execution(void set*(*)) && this(mixin)", argNames="mixin")
public void checkNotLocked(
Lockable mixin) // Bind to arg
{
public void checkNotLocked( Lockable mixin) {
// Can also obtain the mixin (this) this way
//Lockable mixin = (Lockable) jp.getThis();
if (mixin.locked()) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -16,9 +16,6 @@
package org.springframework.beans.factory.config;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import org.junit.After;
@ -56,7 +53,7 @@ public class PropertyPlaceholderConfigurerTests {
@Before
public void setUp() {
public void setup() {
p1BeanDef = rootBeanDefinition(TestBean.class)
.addPropertyValue("name", "${" + P1 + "}")
.getBeanDefinition();
@ -66,16 +63,14 @@ public class PropertyPlaceholderConfigurerTests {
ppcProperties = new Properties();
ppcProperties.setProperty(P1, P1_LOCAL_PROPS_VAL);
System.setProperty(P1, P1_SYSTEM_PROPS_VAL);
getModifiableSystemEnvironment().put(P1, P1_SYSTEM_ENV_VAL);
ppc = new PropertyPlaceholderConfigurer();
ppc.setProperties(ppcProperties);
}
@After
public void tearDown() {
public void cleanup() {
System.clearProperty(P1);
getModifiableSystemEnvironment().remove(P1);
}
@ -95,7 +90,7 @@ public class PropertyPlaceholderConfigurerTests {
@Test
public void resolveFromSystemProperties() {
getModifiableSystemEnvironment().put("otherKey", "systemValue");
System.setProperty("otherKey", "systemValue");
p1BeanDef = rootBeanDefinition(TestBean.class)
.addPropertyValue("name", "${" + P1 + "}")
.addPropertyValue("sex", "${otherKey}")
@ -105,12 +100,12 @@ public class PropertyPlaceholderConfigurerTests {
TestBean bean = bf.getBean(TestBean.class);
assertThat(bean.getName(), equalTo(P1_LOCAL_PROPS_VAL));
assertThat(bean.getSex(), equalTo("systemValue"));
getModifiableSystemEnvironment().remove("otherKey");
System.clearProperty("otherKey");
}
@Test
public void resolveFromLocalProperties() {
tearDown(); // eliminate entries from system props/environment
System.clearProperty(P1);
registerWithGeneratedName(p1BeanDef, bf);
ppc.postProcessBeanFactory(bf);
TestBean bean = bf.getBean(TestBean.class);
@ -134,16 +129,6 @@ public class PropertyPlaceholderConfigurerTests {
assertThat(bean.getName(), equalTo(P1_SYSTEM_PROPS_VAL));
}
@Test
public void setSystemSystemPropertiesMode_toOverride_andResolveFromSystemEnvironment() {
registerWithGeneratedName(p1BeanDef, bf);
System.clearProperty(P1); // will now fall all the way back to system environment
ppc.setSystemPropertiesMode(PropertyPlaceholderConfigurer.SYSTEM_PROPERTIES_MODE_OVERRIDE);
ppc.postProcessBeanFactory(bf);
TestBean bean = bf.getBean(TestBean.class);
assertThat(bean.getName(), equalTo(P1_SYSTEM_ENV_VAL));
}
@Test
public void setSystemSystemPropertiesMode_toOverride_andSetSearchSystemEnvironment_toFalse() {
registerWithGeneratedName(p1BeanDef, bf);
@ -160,7 +145,7 @@ public class PropertyPlaceholderConfigurerTests {
* settings regarding resolving properties from the environment.
*/
@Test
public void twoPlacholderConfigurers_withConflictingSettings() {
public void twoPlaceholderConfigurers_withConflictingSettings() {
String P2 = "p2";
String P2_LOCAL_PROPS_VAL = "p2LocalPropsVal";
String P2_SYSTEM_PROPS_VAL = "p2SystemPropsVal";
@ -178,7 +163,6 @@ public class PropertyPlaceholderConfigurerTests {
ppc.postProcessBeanFactory(bf);
System.setProperty(P2, P2_SYSTEM_PROPS_VAL);
getModifiableSystemEnvironment().put(P2, P2_SYSTEM_ENV_VAL);
Properties ppc2Properties = new Properties();
ppc2Properties.put(P2, P2_LOCAL_PROPS_VAL);
@ -198,7 +182,6 @@ public class PropertyPlaceholderConfigurerTests {
assertThat(p2Bean.getCountry(), equalTo(P2_SYSTEM_PROPS_VAL));
System.clearProperty(P2);
getModifiableSystemEnvironment().remove(P2);
}
@Test
@ -228,100 +211,41 @@ public class PropertyPlaceholderConfigurerTests {
public void nullValueIsPreserved() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setNullValue("customNull");
getModifiableSystemEnvironment().put("my.name", "customNull");
System.setProperty("my.name", "customNull");
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean", rootBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), nullValue());
getModifiableSystemEnvironment().remove("my.name");
System.clearProperty("my.name");
}
@Test
public void trimValuesIsOffByDefault() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
getModifiableSystemEnvironment().put("my.name", " myValue ");
System.setProperty("my.name", " myValue ");
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean", rootBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo(" myValue "));
getModifiableSystemEnvironment().remove("my.name");
System.clearProperty("my.name");
}
@Test
public void trimValuesIsApplied() {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setTrimValues(true);
getModifiableSystemEnvironment().put("my.name", " myValue ");
System.setProperty("my.name", " myValue ");
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.registerBeanDefinition("testBean", rootBeanDefinition(TestBean.class)
.addPropertyValue("name", "${my.name}")
.getBeanDefinition());
ppc.postProcessBeanFactory(bf);
assertThat(bf.getBean(TestBean.class).getName(), equalTo("myValue"));
getModifiableSystemEnvironment().remove("my.name");
System.clearProperty("my.name");
}
@SuppressWarnings("unchecked")
private static Map<String, String> getModifiableSystemEnvironment() {
// for os x / linux
Class<?>[] classes = Collections.class.getDeclaredClasses();
Map<String, String> env = System.getenv();
for (Class<?> cl : classes) {
if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
try {
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Object obj = field.get(env);
if (obj != null && obj.getClass().getName().equals("java.lang.ProcessEnvironment$StringEnvironment")) {
return (Map<String, String>) obj;
}
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
// for windows
Class<?> processEnvironmentClass;
try {
processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
try {
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
theCaseInsensitiveEnvironmentField.setAccessible(true);
Object obj = theCaseInsensitiveEnvironmentField.get(null);
return (Map<String, String>) obj;
}
catch (NoSuchFieldException ex) {
// do nothing
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
try {
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
theEnvironmentField.setAccessible(true);
Object obj = theEnvironmentField.get(null);
return (Map<String, String>) obj;
}
catch (NoSuchFieldException ex) {
// do nothing
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
throw new IllegalStateException();
}
}

View File

@ -118,9 +118,9 @@ class ConfigurationClassEnhancer {
/**
* Creates a new CGLIB {@link Enhancer} instance.
*/
private Enhancer newEnhancer(Class<?> superclass, @Nullable ClassLoader classLoader) {
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(superclass);
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -212,7 +212,7 @@ public abstract class AbstractAopProxyTests {
}
@Test
public void testSerializationSerializableTargetAndAdvice() throws Throwable {
public void testSerializableTargetAndAdvice() throws Throwable {
SerializablePerson personTarget = new SerializablePerson();
personTarget.setName("jim");
personTarget.setAge(26);
@ -435,7 +435,7 @@ public abstract class AbstractAopProxyTests {
TestBean raw = new OwnSpouse();
ProxyCreatorSupport pc = new ProxyCreatorSupport();
pc.setInterfaces(new Class<?>[] {ITestBean.class});
pc.setInterfaces(ITestBean.class);
pc.setTarget(raw);
ITestBean tb = (ITestBean) createProxy(pc);
@ -457,7 +457,7 @@ public abstract class AbstractAopProxyTests {
pc.addAdvice(mi);
// We don't care about the object
mockTargetSource.setTarget(new Object());
mockTargetSource.setTarget(new TestBean());
pc.setTargetSource(mockTargetSource);
AopProxy aop = createAopProxy(pc);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@ -122,27 +122,7 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
}
@Test
public void testPackageMethodInvocationWithDifferentClassLoader() {
ClassLoader child = new ClassLoader(getClass().getClassLoader()) {
};
PackageMethodTestBean bean = new PackageMethodTestBean();
bean.value = "foo";
mockTargetSource.setTarget(bean);
AdvisedSupport as = new AdvisedSupport();
as.setTargetSource(mockTargetSource);
as.addAdvice(new NopInterceptor());
AopProxy aop = new CglibAopProxy(as);
PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy(child);
assertTrue(AopUtils.isCglibProxy(proxy));
assertNotEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
assertNull(proxy.getString()); // we're stuck in the proxy instance
}
@Test
public void testProxyCanBeClassNotInterface() throws Exception {
public void testProxyCanBeClassNotInterface() {
TestBean raw = new TestBean();
raw.setAge(32);
mockTargetSource.setTarget(raw);
@ -174,7 +154,7 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
}
@Test
public void testUnadvisedProxyCreationWithCallDuringConstructor() throws Exception {
public void testUnadvisedProxyCreationWithCallDuringConstructor() {
CglibTestBean target = new CglibTestBean();
target.setName("Rob Harrop");
@ -370,7 +350,7 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
}
@Test
public void testProxyProtectedMethod() throws Exception {
public void testProxyProtectedMethod() {
CountingBeforeAdvice advice = new CountingBeforeAdvice();
ProxyFactory proxyFactory = new ProxyFactory(new MyBean());
proxyFactory.addAdvice(advice);
@ -382,14 +362,14 @@ public class CglibProxyTests extends AbstractAopProxyTests implements Serializab
}
@Test
public void testProxyTargetClassInCaseOfNoInterfaces() throws Exception {
public void testProxyTargetClassInCaseOfNoInterfaces() {
ProxyFactory proxyFactory = new ProxyFactory(new MyBean());
MyBean proxy = (MyBean) proxyFactory.getProxy();
assertEquals(4, proxy.add(1, 3));
}
@Test // SPR-13328
public void testVarargsWithEnumArray() throws Exception {
public void testVarargsWithEnumArray() {
ProxyFactory proxyFactory = new ProxyFactory(new MyBean());
MyBean proxy = (MyBean) proxyFactory.getProxy();
assertTrue(proxy.doWithVarargs(MyEnum.A, MyOtherEnum.C));

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2018 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.
@ -35,13 +35,13 @@ import static org.junit.Assert.*;
public class BeanNameAutoProxyCreatorInitTests {
@Test(expected = IllegalArgumentException.class)
public void testIgnoreAdvisorThatIsCurrentlyCreation() {
public void testIgnoreAdvisorThatIsCurrentlyInCreation() {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-context.xml", getClass());
new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-context.xml", getClass());
TestBean bean = (TestBean) ctx.getBean("bean");
bean.setName("foo");
assertEquals("foo", bean.getName());
bean.setName(null); // should throw
bean.setName(null); // should throw
}
}

View File

@ -40,7 +40,7 @@ task cglibRepackJar(type: Jar) { repackJar ->
}
// Repackage net.sf.cglib => org.springframework.cglib
rule(pattern: "net.sf.cglib.**", result: "org.springframework.cglib.@1")
// As mentioned above, transform cglib"s internal asm dependencies from
// As mentioned above, transform cglib's internal asm dependencies from
// org.objectweb.asm => org.springframework.asm. Doing this counts on the
// the fact that Spring and cglib depend on the same version of asm!
rule(pattern: "org.objectweb.asm.**", result: "org.springframework.asm.@1")
@ -98,6 +98,11 @@ jar {
dependsOn cglibRepackJar
from(zipTree(cglibRepackJar.archivePath)) {
include "org/springframework/cglib/**"
exclude "org/springframework/cglib/core/AbstractClassGenerator*.class"
exclude "org/springframework/cglib/core/KeyFactory*.class"
exclude "org/springframework/cglib/core/ReflectUtils*.class"
exclude "org/springframework/cglib/proxy/Enhancer*.class"
exclude "org/springframework/cglib/proxy/MethodProxy*.class"
}
dependsOn objenesisRepackJar

View File

@ -0,0 +1,379 @@
/*
* 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
*
* http://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.ref.WeakReference;
import java.security.ProtectionDomain;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.springframework.asm.ClassReader;
import org.springframework.cglib.core.internal.Function;
import org.springframework.cglib.core.internal.LoadingCache;
/**
* Abstract class for all code-generating CGLIB utilities.
* In addition to caching generated classes for performance, it provides hooks for
* customizing the <code>ClassLoader</code>, name of the generated class, and transformations
* applied before generation.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
abstract public class AbstractClassGenerator<T> implements ClassGenerator {
private static final ThreadLocal CURRENT = new ThreadLocal();
private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>();
private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE;
private Source source;
private ClassLoader classLoader;
private Class contextClass;
private String namePrefix;
private Object key;
private boolean useCache = true;
private String className;
private boolean attemptLoad;
protected static class ClassLoaderData {
private final Set<String> reservedClassNames = new HashSet<String>();
/**
* {@link AbstractClassGenerator} here holds "cache key" (e.g. {@link org.springframework.cglib.proxy.Enhancer}
* configuration), and the value is the generated class plus some additional values
* (see {@link #unwrapCachedValue(Object)}.
* <p>The generated classes can be reused as long as their classloader is reachable.</p>
* <p>Note: the only way to access a class is to find it through generatedClasses cache, thus
* the key should not expire as long as the class itself is alive (its classloader is alive).</p>
*/
private final LoadingCache<AbstractClassGenerator, Object, Object> generatedClasses;
/**
* Note: ClassLoaderData object is stored as a value of {@code WeakHashMap<ClassLoader, ...>} thus
* this classLoader reference should be weak otherwise it would make classLoader strongly reachable
* and alive forever.
* Reference queue is not required since the cleanup is handled by {@link WeakHashMap}.
*/
private final WeakReference<ClassLoader> classLoader;
private final Predicate uniqueNamePredicate = new Predicate() {
public boolean evaluate(Object name) {
return reservedClassNames.contains(name);
}
};
private static final Function<AbstractClassGenerator, Object> GET_KEY = new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
return gen.key;
}
};
public ClassLoaderData(ClassLoader classLoader) {
if (classLoader == null) {
throw new IllegalArgumentException("classLoader == null is not yet supported");
}
this.classLoader = new WeakReference<ClassLoader>(classLoader);
Function<AbstractClassGenerator, Object> load =
new Function<AbstractClassGenerator, Object>() {
public Object apply(AbstractClassGenerator gen) {
Class klass = gen.generate(ClassLoaderData.this);
return gen.wrapCachedClass(klass);
}
};
generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);
}
public ClassLoader getClassLoader() {
return classLoader.get();
}
public void reserveName(String name) {
reservedClassNames.add(name);
}
public Predicate getUniqueNamePredicate() {
return uniqueNamePredicate;
}
public Object get(AbstractClassGenerator gen, boolean useCache) {
if (!useCache) {
return gen.generate(ClassLoaderData.this);
}
else {
Object cachedValue = generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}
}
protected T wrapCachedClass(Class klass) {
return (T) new WeakReference(klass);
}
protected Object unwrapCachedValue(T cached) {
return ((WeakReference) cached).get();
}
protected static class Source {
String name;
public Source(String name) {
this.name = name;
}
}
protected AbstractClassGenerator(Source source) {
this.source = source;
}
protected void setNamePrefix(String namePrefix) {
this.namePrefix = namePrefix;
}
final protected String getClassName() {
return className;
}
private void setClassName(String className) {
this.className = className;
}
private String generateClassName(Predicate nameTestPredicate) {
return namingPolicy.getClassName(namePrefix, source.name, key, nameTestPredicate);
}
/**
* Set the <code>ClassLoader</code> in which the class will be generated.
* Concrete subclasses of <code>AbstractClassGenerator</code> (such as <code>Enhancer</code>)
* will try to choose an appropriate default if this is unset.
* <p>
* Classes are cached per-<code>ClassLoader</code> using a <code>WeakHashMap</code>, to allow
* the generated classes to be removed when the associated loader is garbage collected.
* @param classLoader the loader to generate the new class with, or null to use the default
*/
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
// SPRING PATCH BEGIN
public void setContextClass(Class contextClass) {
this.contextClass = contextClass;
}
// SPRING PATCH END
/**
* Override the default naming policy.
* @param namingPolicy the custom policy, or null to use the default
* @see DefaultNamingPolicy
*/
public void setNamingPolicy(NamingPolicy namingPolicy) {
if (namingPolicy == null)
namingPolicy = DefaultNamingPolicy.INSTANCE;
this.namingPolicy = namingPolicy;
}
/**
* @see #setNamingPolicy
*/
public NamingPolicy getNamingPolicy() {
return namingPolicy;
}
/**
* Whether use and update the static cache of generated classes
* for a class with the same properties. Default is <code>true</code>.
*/
public void setUseCache(boolean useCache) {
this.useCache = useCache;
}
/**
* @see #setUseCache
*/
public boolean getUseCache() {
return useCache;
}
/**
* If set, CGLIB will attempt to load classes from the specified
* <code>ClassLoader</code> before generating them. Because generated
* class names are not guaranteed to be unique, the default is <code>false</code>.
*/
public void setAttemptLoad(boolean attemptLoad) {
this.attemptLoad = attemptLoad;
}
public boolean getAttemptLoad() {
return attemptLoad;
}
/**
* Set the strategy to use to create the bytecode from this generator.
* By default an instance of {@see DefaultGeneratorStrategy} is used.
*/
public void setStrategy(GeneratorStrategy strategy) {
if (strategy == null)
strategy = DefaultGeneratorStrategy.INSTANCE;
this.strategy = strategy;
}
/**
* @see #setStrategy
*/
public GeneratorStrategy getStrategy() {
return strategy;
}
/**
* Used internally by CGLIB. Returns the <code>AbstractClassGenerator</code>
* that is being used to generate a class in the current thread.
*/
public static AbstractClassGenerator getCurrent() {
return (AbstractClassGenerator) CURRENT.get();
}
public ClassLoader getClassLoader() {
ClassLoader t = classLoader;
if (t == null) {
t = getDefaultClassLoader();
}
if (t == null) {
t = getClass().getClassLoader();
}
if (t == null) {
t = Thread.currentThread().getContextClassLoader();
}
if (t == null) {
throw new IllegalStateException("Cannot determine classloader");
}
return t;
}
abstract protected ClassLoader getDefaultClassLoader();
/**
* Returns the protection domain to use when defining the class.
* <p>
* Default implementation returns <code>null</code> for using a default protection domain. Sub-classes may
* override to use a more specific protection domain.
* </p>
* @return the protection domain (<code>null</code> for using a default)
*/
protected ProtectionDomain getProtectionDomain() {
return null;
}
protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, getUseCache());
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
}
catch (RuntimeException | Error ex) {
throw ex;
}
catch (Exception ex) {
throw new CodeGenerationException(ex);
}
}
protected Class generate(ClassLoaderData data) {
Class gen;
Object save = CURRENT.get();
CURRENT.set(this);
try {
ClassLoader classLoader = data.getClassLoader();
if (classLoader == null) {
throw new IllegalStateException("ClassLoader is null while trying to define class " +
getClassName() + ". It seems that the loader has been expired from a weak reference somehow. " +
"Please file an issue at cglib's issue tracker.");
}
synchronized (classLoader) {
String name = generateClassName(data.getUniqueNamePredicate());
data.reserveName(name);
this.setClassName(name);
}
if (attemptLoad) {
try {
gen = classLoader.loadClass(getClassName());
return gen;
}
catch (ClassNotFoundException e) {
// ignore
}
}
byte[] b = strategy.generate(this);
String className = ClassNameReader.getClassName(new ClassReader(b));
ProtectionDomain protectionDomain = getProtectionDomain();
synchronized (classLoader) { // just in case
// SPRING PATCH BEGIN
gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain, contextClass);
// SPRING PATCH END
}
return gen;
}
catch (RuntimeException | Error ex) {
throw ex;
}
catch (Exception ex) {
throw new CodeGenerationException(ex);
}
finally {
CURRENT.set(save);
}
}
abstract protected Object firstInstance(Class type) throws Exception;
abstract protected Object nextInstance(Object instance) throws Exception;
}

View File

@ -0,0 +1,363 @@
/*
* 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
*
* http://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.security.ProtectionDomain;
import java.util.Collections;
import java.util.List;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Label;
import org.springframework.asm.Type;
import org.springframework.cglib.core.internal.CustomizerRegistry;
/**
* Generates classes to handle multi-valued keys, for use in things such as Maps and Sets.
* Code for <code>equals</code> and <code>hashCode</code> methods follow the
* the rules laid out in <i>Effective Java</i> by Joshua Bloch.
* <p>
* To generate a <code>KeyFactory</code>, you need to supply an interface which
* describes the structure of the key. The interface should have a
* single method named <code>newInstance</code>, which returns an
* <code>Object</code>. The arguments array can be
* <i>anything</i>--Objects, primitive values, or single or
* multi-dimension arrays of either. For example:
* <p><pre>
* private interface IntStringKey {
* public Object newInstance(int i, String s);
* }
* </pre><p>
* Once you have made a <code>KeyFactory</code>, you generate a new key by calling
* the <code>newInstance</code> method defined by your interface.
* <p><pre>
* IntStringKey factory = (IntStringKey)KeyFactory.create(IntStringKey.class);
* Object key1 = factory.newInstance(4, "Hello");
* Object key2 = factory.newInstance(4, "World");
* </pre><p>
* <b>Note:</b>
* <code>hashCode</code> equality between two keys <code>key1</code> and <code>key2</code> is only guaranteed if
* <code>key1.equals(key2)</code> <i>and</i> the keys were produced by the same factory.
* @version $Id: KeyFactory.java,v 1.26 2006/03/05 02:43:19 herbyderby Exp $
*/
@SuppressWarnings({"rawtypes", "unchecked"})
abstract public class KeyFactory {
private static final Signature GET_NAME =
TypeUtils.parseSignature("String getName()");
private static final Signature GET_CLASS =
TypeUtils.parseSignature("Class getClass()");
private static final Signature HASH_CODE =
TypeUtils.parseSignature("int hashCode()");
private static final Signature EQUALS =
TypeUtils.parseSignature("boolean equals(Object)");
private static final Signature TO_STRING =
TypeUtils.parseSignature("String toString()");
private static final Signature APPEND_STRING =
TypeUtils.parseSignature("StringBuffer append(String)");
private static final Type KEY_FACTORY =
TypeUtils.parseType("org.springframework.cglib.core.KeyFactory");
private static final Signature GET_SORT =
TypeUtils.parseSignature("int getSort()");
//generated numbers:
private final static int PRIMES[] = {
11, 73, 179, 331,
521, 787, 1213, 1823,
2609, 3691, 5189, 7247,
10037, 13931, 19289, 26627,
36683, 50441, 69403, 95401,
131129, 180179, 247501, 340057,
467063, 641371, 880603, 1209107,
1660097, 2279161, 3129011, 4295723,
5897291, 8095873, 11114263, 15257791,
20946017, 28754629, 39474179, 54189869,
74391461, 102123817, 140194277, 192456917,
264202273, 362693231, 497900099, 683510293,
938313161, 1288102441, 1768288259};
public static final Customizer CLASS_BY_NAME = new Customizer() {
public void customize(CodeEmitter e, Type type) {
if (type.equals(Constants.TYPE_CLASS)) {
e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
}
}
};
public static final FieldTypeCustomizer STORE_CLASS_AS_STRING = new FieldTypeCustomizer() {
public void customize(CodeEmitter e, int index, Type type) {
if (type.equals(Constants.TYPE_CLASS)) {
e.invoke_virtual(Constants.TYPE_CLASS, GET_NAME);
}
}
public Type getOutType(int index, Type type) {
if (type.equals(Constants.TYPE_CLASS)) {
return Constants.TYPE_STRING;
}
return type;
}
};
/**
* {@link Type#hashCode()} is very expensive as it traverses full descriptor to calculate hash code.
* This customizer uses {@link Type#getSort()} as a hash code.
*/
public static final HashCodeCustomizer HASH_ASM_TYPE = new HashCodeCustomizer() {
public boolean customize(CodeEmitter e, Type type) {
if (Constants.TYPE_TYPE.equals(type)) {
e.invoke_virtual(type, GET_SORT);
return true;
}
return false;
}
};
/**
* @deprecated this customizer might result in unexpected class leak since key object still holds a strong reference to the Object and class.
* It is recommended to have pre-processing method that would strip Objects and represent Classes as Strings
*/
@Deprecated
public static final Customizer OBJECT_BY_CLASS = new Customizer() {
public void customize(CodeEmitter e, Type type) {
e.invoke_virtual(Constants.TYPE_OBJECT, GET_CLASS);
}
};
protected KeyFactory() {
}
public static KeyFactory create(Class keyInterface) {
return create(keyInterface, null);
}
public static KeyFactory create(Class keyInterface, Customizer customizer) {
return create(keyInterface.getClassLoader(), keyInterface, customizer);
}
public static KeyFactory create(Class keyInterface, KeyFactoryCustomizer first, List<KeyFactoryCustomizer> next) {
return create(keyInterface.getClassLoader(), keyInterface, first, next);
}
public static KeyFactory create(ClassLoader loader, Class keyInterface, Customizer customizer) {
return create(loader, keyInterface, customizer, Collections.<KeyFactoryCustomizer>emptyList());
}
public static KeyFactory create(ClassLoader loader, Class keyInterface, KeyFactoryCustomizer customizer,
List<KeyFactoryCustomizer> next) {
Generator gen = new Generator();
gen.setInterface(keyInterface);
// SPRING PATCH BEGIN
gen.setContextClass(keyInterface);
// SPRING PATCH END
if (customizer != null) {
gen.addCustomizer(customizer);
}
if (next != null && !next.isEmpty()) {
for (KeyFactoryCustomizer keyFactoryCustomizer : next) {
gen.addCustomizer(keyFactoryCustomizer);
}
}
gen.setClassLoader(loader);
return gen.create();
}
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(KeyFactory.class.getName());
private static final Class[] KNOWN_CUSTOMIZER_TYPES = new Class[]{Customizer.class, FieldTypeCustomizer.class};
private Class keyInterface;
// TODO: Make me final when deprecated methods are removed
private CustomizerRegistry customizers = new CustomizerRegistry(KNOWN_CUSTOMIZER_TYPES);
private int constant;
private int multiplier;
public Generator() {
super(SOURCE);
}
protected ClassLoader getDefaultClassLoader() {
return keyInterface.getClassLoader();
}
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(keyInterface);
}
/**
* @deprecated Use {@link #addCustomizer(KeyFactoryCustomizer)} instead.
*/
@Deprecated
public void setCustomizer(Customizer customizer) {
customizers = CustomizerRegistry.singleton(customizer);
}
public void addCustomizer(KeyFactoryCustomizer customizer) {
customizers.add(customizer);
}
public <T> List<T> getCustomizers(Class<T> klass) {
return customizers.get(klass);
}
public void setInterface(Class keyInterface) {
this.keyInterface = keyInterface;
}
public KeyFactory create() {
setNamePrefix(keyInterface.getName());
return (KeyFactory) super.create(keyInterface.getName());
}
public void setHashConstant(int constant) {
this.constant = constant;
}
public void setHashMultiplier(int multiplier) {
this.multiplier = multiplier;
}
protected Object firstInstance(Class type) {
return ReflectUtils.newInstance(type);
}
protected Object nextInstance(Object instance) {
return instance;
}
public void generateClass(ClassVisitor v) {
ClassEmitter ce = new ClassEmitter(v);
Method newInstance = ReflectUtils.findNewInstance(keyInterface);
if (!newInstance.getReturnType().equals(Object.class)) {
throw new IllegalArgumentException("newInstance method must return Object");
}
Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes());
ce.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
getClassName(),
KEY_FACTORY,
new Type[]{Type.getType(keyInterface)},
Constants.SOURCE_FILE);
EmitUtils.null_constructor(ce);
EmitUtils.factory_method(ce, ReflectUtils.getSignature(newInstance));
int seed = 0;
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC,
TypeUtils.parseConstructor(parameterTypes),
null);
e.load_this();
e.super_invoke_constructor();
e.load_this();
List<FieldTypeCustomizer> fieldTypeCustomizers = getCustomizers(FieldTypeCustomizer.class);
for (int i = 0; i < parameterTypes.length; i++) {
Type parameterType = parameterTypes[i];
Type fieldType = parameterType;
for (FieldTypeCustomizer customizer : fieldTypeCustomizers) {
fieldType = customizer.getOutType(i, fieldType);
}
seed += fieldType.hashCode();
ce.declare_field(Constants.ACC_PRIVATE | Constants.ACC_FINAL,
getFieldName(i),
fieldType,
null);
e.dup();
e.load_arg(i);
for (FieldTypeCustomizer customizer : fieldTypeCustomizers) {
customizer.customize(e, i, parameterType);
}
e.putfield(getFieldName(i));
}
e.return_value();
e.end_method();
// hash code
e = ce.begin_method(Constants.ACC_PUBLIC, HASH_CODE, null);
int hc = (constant != 0) ? constant : PRIMES[(Math.abs(seed) % PRIMES.length)];
int hm = (multiplier != 0) ? multiplier : PRIMES[(Math.abs(seed * 13) % PRIMES.length)];
e.push(hc);
for (int i = 0; i < parameterTypes.length; i++) {
e.load_this();
e.getfield(getFieldName(i));
EmitUtils.hash_code(e, parameterTypes[i], hm, customizers);
}
e.return_value();
e.end_method();
// equals
e = ce.begin_method(Constants.ACC_PUBLIC, EQUALS, null);
Label fail = e.make_label();
e.load_arg(0);
e.instance_of_this();
e.if_jump(CodeEmitter.EQ, fail);
for (int i = 0; i < parameterTypes.length; i++) {
e.load_this();
e.getfield(getFieldName(i));
e.load_arg(0);
e.checkcast_this();
e.getfield(getFieldName(i));
EmitUtils.not_equals(e, parameterTypes[i], fail, customizers);
}
e.push(1);
e.return_value();
e.mark(fail);
e.push(0);
e.return_value();
e.end_method();
// toString
e = ce.begin_method(Constants.ACC_PUBLIC, TO_STRING, null);
e.new_instance(Constants.TYPE_STRING_BUFFER);
e.dup();
e.invoke_constructor(Constants.TYPE_STRING_BUFFER);
for (int i = 0; i < parameterTypes.length; i++) {
if (i > 0) {
e.push(", ");
e.invoke_virtual(Constants.TYPE_STRING_BUFFER, APPEND_STRING);
}
e.load_this();
e.getfield(getFieldName(i));
EmitUtils.append_string(e, parameterTypes[i], EmitUtils.DEFAULT_DELIMITERS, customizers);
}
e.invoke_virtual(Constants.TYPE_STRING_BUFFER, TO_STRING);
e.return_value();
e.end_method();
ce.end_class();
}
private String getFieldName(int arg) {
return "FIELD_" + arg;
}
}
}

View File

@ -0,0 +1,617 @@
/*
* 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
*
* http://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.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
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.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.asm.Attribute;
import org.springframework.asm.Type;
/**
* @version $Id: ReflectUtils.java,v 1.30 2009/01/11 19:47:49 herbyderby Exp $
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class ReflectUtils {
private ReflectUtils() {
}
private static final Map primitives = new HashMap(8);
private static final Map transforms = new HashMap(8);
private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader();
// SPRING PATCH BEGIN
private static final Method privateLookupInMethod;
private static final Method lookupDefineClassMethod;
private static final Method classLoaderDefineClassMethod;
private static final ProtectionDomain PROTECTION_DOMAIN;
private static final Throwable THROWABLE;
private static final List<Method> OBJECT_METHODS = new ArrayList<Method>();
static {
Method privateLookupIn;
Method lookupDefineClass;
Method classLoaderDefineClass;
ProtectionDomain protectionDomain;
Throwable throwable = null;
try {
privateLookupIn = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
try {
return MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
}
catch (NoSuchMethodException ex) {
return null;
}
}
});
lookupDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
try {
return MethodHandles.Lookup.class.getMethod("defineClass", byte[].class);
}
catch (NoSuchMethodException ex) {
return null;
}
}
});
classLoaderDefineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
return ClassLoader.class.getDeclaredMethod("defineClass",
String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
}
});
protectionDomain = getProtectionDomain(ReflectUtils.class);
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception {
Method[] methods = Object.class.getDeclaredMethods();
for (Method method : methods) {
if ("finalize".equals(method.getName())
|| (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) {
continue;
}
OBJECT_METHODS.add(method);
}
return null;
}
});
}
catch (Throwable t) {
privateLookupIn = null;
lookupDefineClass = null;
classLoaderDefineClass = null;
protectionDomain = null;
throwable = t;
}
privateLookupInMethod = privateLookupIn;
lookupDefineClassMethod = lookupDefineClass;
classLoaderDefineClassMethod = classLoaderDefineClass;
PROTECTION_DOMAIN = protectionDomain;
THROWABLE = throwable;
}
// SPRING PATCH END
private static final String[] CGLIB_PACKAGES = {
"java.lang",
};
static {
primitives.put("byte", Byte.TYPE);
primitives.put("char", Character.TYPE);
primitives.put("double", Double.TYPE);
primitives.put("float", Float.TYPE);
primitives.put("int", Integer.TYPE);
primitives.put("long", Long.TYPE);
primitives.put("short", Short.TYPE);
primitives.put("boolean", Boolean.TYPE);
transforms.put("byte", "B");
transforms.put("char", "C");
transforms.put("double", "D");
transforms.put("float", "F");
transforms.put("int", "I");
transforms.put("long", "J");
transforms.put("short", "S");
transforms.put("boolean", "Z");
}
public static ProtectionDomain getProtectionDomain(final Class source) {
if (source == null) {
return null;
}
return (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return source.getProtectionDomain();
}
});
}
public static Type[] getExceptionTypes(Member member) {
if (member instanceof Method) {
return TypeUtils.getTypes(((Method) member).getExceptionTypes());
}
else if (member instanceof Constructor) {
return TypeUtils.getTypes(((Constructor) member).getExceptionTypes());
}
else {
throw new IllegalArgumentException("Cannot get exception types of a field");
}
}
public static Signature getSignature(Member member) {
if (member instanceof Method) {
return new Signature(member.getName(), Type.getMethodDescriptor((Method) member));
}
else if (member instanceof Constructor) {
Type[] types = TypeUtils.getTypes(((Constructor) member).getParameterTypes());
return new Signature(Constants.CONSTRUCTOR_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE, types));
}
else {
throw new IllegalArgumentException("Cannot get signature of a field");
}
}
public static Constructor findConstructor(String desc) {
return findConstructor(desc, defaultLoader);
}
public static Constructor findConstructor(String desc, ClassLoader loader) {
try {
int lparen = desc.indexOf('(');
String className = desc.substring(0, lparen).trim();
return getClass(className, loader).getConstructor(parseTypes(desc, loader));
}
catch (ClassNotFoundException | NoSuchMethodException ex) {
throw new CodeGenerationException(ex);
}
}
public static Method findMethod(String desc) {
return findMethod(desc, defaultLoader);
}
public static Method findMethod(String desc, ClassLoader loader) {
try {
int lparen = desc.indexOf('(');
int dot = desc.lastIndexOf('.', lparen);
String className = desc.substring(0, dot).trim();
String methodName = desc.substring(dot + 1, lparen).trim();
return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader));
}
catch (ClassNotFoundException | NoSuchMethodException ex) {
throw new CodeGenerationException(ex);
}
}
private static Class[] parseTypes(String desc, ClassLoader loader) throws ClassNotFoundException {
int lparen = desc.indexOf('(');
int rparen = desc.indexOf(')', lparen);
List params = new ArrayList();
int start = lparen + 1;
for (; ; ) {
int comma = desc.indexOf(',', start);
if (comma < 0) {
break;
}
params.add(desc.substring(start, comma).trim());
start = comma + 1;
}
if (start < rparen) {
params.add(desc.substring(start, rparen).trim());
}
Class[] types = new Class[params.size()];
for (int i = 0; i < types.length; i++) {
types[i] = getClass((String) params.get(i), loader);
}
return types;
}
private static Class getClass(String className, ClassLoader loader) throws ClassNotFoundException {
return getClass(className, loader, CGLIB_PACKAGES);
}
private static Class getClass(String className, ClassLoader loader, String[] packages) throws ClassNotFoundException {
String save = className;
int dimensions = 0;
int index = 0;
while ((index = className.indexOf("[]", index) + 1) > 0) {
dimensions++;
}
StringBuffer brackets = new StringBuffer(className.length() - dimensions);
for (int i = 0; i < dimensions; i++) {
brackets.append('[');
}
className = className.substring(0, className.length() - 2 * dimensions);
String prefix = (dimensions > 0) ? brackets + "L" : "";
String suffix = (dimensions > 0) ? ";" : "";
try {
return Class.forName(prefix + className + suffix, false, loader);
}
catch (ClassNotFoundException ignore) {
}
for (int i = 0; i < packages.length; i++) {
try {
return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader);
}
catch (ClassNotFoundException ignore) {
}
}
if (dimensions == 0) {
Class c = (Class) primitives.get(className);
if (c != null) {
return c;
}
}
else {
String transform = (String) transforms.get(className);
if (transform != null) {
try {
return Class.forName(brackets + transform, false, loader);
}
catch (ClassNotFoundException ignore) {
}
}
}
throw new ClassNotFoundException(save);
}
public static Object newInstance(Class type) {
return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null);
}
public static Object newInstance(Class type, Class[] parameterTypes, Object[] args) {
return newInstance(getConstructor(type, parameterTypes), args);
}
@SuppressWarnings("deprecation") // on JDK 9
public static Object newInstance(final Constructor cstruct, final Object[] args) {
boolean flag = cstruct.isAccessible();
try {
if (!flag) {
cstruct.setAccessible(true);
}
Object result = cstruct.newInstance(args);
return result;
}
catch (InstantiationException e) {
throw new CodeGenerationException(e);
}
catch (IllegalAccessException e) {
throw new CodeGenerationException(e);
}
catch (InvocationTargetException e) {
throw new CodeGenerationException(e.getTargetException());
}
finally {
if (!flag) {
cstruct.setAccessible(flag);
}
}
}
public static Constructor getConstructor(Class type, Class[] parameterTypes) {
try {
Constructor constructor = type.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
return constructor;
}
catch (NoSuchMethodException e) {
throw new CodeGenerationException(e);
}
}
public static String[] getNames(Class[] classes) {
if (classes == null)
return null;
String[] names = new String[classes.length];
for (int i = 0; i < names.length; i++) {
names[i] = classes[i].getName();
}
return names;
}
public static Class[] getClasses(Object[] objects) {
Class[] classes = new Class[objects.length];
for (int i = 0; i < objects.length; i++) {
classes[i] = objects[i].getClass();
}
return classes;
}
public static Method findNewInstance(Class iface) {
Method m = findInterfaceMethod(iface);
if (!m.getName().equals("newInstance")) {
throw new IllegalArgumentException(iface + " missing newInstance method");
}
return m;
}
public static Method[] getPropertyMethods(PropertyDescriptor[] properties, boolean read, boolean write) {
Set methods = new HashSet();
for (int i = 0; i < properties.length; i++) {
PropertyDescriptor pd = properties[i];
if (read) {
methods.add(pd.getReadMethod());
}
if (write) {
methods.add(pd.getWriteMethod());
}
}
methods.remove(null);
return (Method[]) methods.toArray(new Method[methods.size()]);
}
public static PropertyDescriptor[] getBeanProperties(Class type) {
return getPropertiesHelper(type, true, true);
}
public static PropertyDescriptor[] getBeanGetters(Class type) {
return getPropertiesHelper(type, true, false);
}
public static PropertyDescriptor[] getBeanSetters(Class type) {
return getPropertiesHelper(type, false, true);
}
private static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) {
try {
BeanInfo info = Introspector.getBeanInfo(type, Object.class);
PropertyDescriptor[] all = info.getPropertyDescriptors();
if (read && write) {
return all;
}
List properties = new ArrayList(all.length);
for (int i = 0; i < all.length; i++) {
PropertyDescriptor pd = all[i];
if ((read && pd.getReadMethod() != null) ||
(write && pd.getWriteMethod() != null)) {
properties.add(pd);
}
}
return (PropertyDescriptor[]) properties.toArray(new PropertyDescriptor[properties.size()]);
}
catch (IntrospectionException e) {
throw new CodeGenerationException(e);
}
}
public static Method findDeclaredMethod(final Class type,
final String methodName, final Class[] parameterTypes)
throws NoSuchMethodException {
Class cl = type;
while (cl != null) {
try {
return cl.getDeclaredMethod(methodName, parameterTypes);
}
catch (NoSuchMethodException e) {
cl = cl.getSuperclass();
}
}
throw new NoSuchMethodException(methodName);
}
public static List addAllMethods(final Class type, final List list) {
if (type == Object.class) {
list.addAll(OBJECT_METHODS);
}
else
list.addAll(java.util.Arrays.asList(type.getDeclaredMethods()));
Class superclass = type.getSuperclass();
if (superclass != null) {
addAllMethods(superclass, list);
}
Class[] interfaces = type.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
addAllMethods(interfaces[i], list);
}
return list;
}
public static List addAllInterfaces(Class type, List list) {
Class superclass = type.getSuperclass();
if (superclass != null) {
list.addAll(Arrays.asList(type.getInterfaces()));
addAllInterfaces(superclass, list);
}
return list;
}
public static Method findInterfaceMethod(Class iface) {
if (!iface.isInterface()) {
throw new IllegalArgumentException(iface + " is not an interface");
}
Method[] methods = iface.getDeclaredMethods();
if (methods.length != 1) {
throw new IllegalArgumentException("expecting exactly 1 method in " + iface);
}
return methods[0];
}
// SPRING PATCH BEGIN
public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception {
return defineClass(className, b, loader, null, null);
}
public static Class defineClass(String className, byte[] b, ClassLoader loader,
ProtectionDomain protectionDomain) throws Exception {
return defineClass(className, b, loader, protectionDomain, null);
}
@SuppressWarnings("deprecation") // on JDK 9
public static Class defineClass(String className, byte[] b, ClassLoader loader,
ProtectionDomain protectionDomain, Class<?> contextClass) throws Exception {
Class c = null;
if (contextClass != null && privateLookupInMethod != null && lookupDefineClassMethod != null) {
try {
MethodHandles.Lookup lookup =
(MethodHandles.Lookup) privateLookupInMethod.invoke(null, contextClass, MethodHandles.lookup());
c = (Class) lookupDefineClassMethod.invoke(lookup, b);
}
catch (InvocationTargetException ex) {
if (!(ex.getTargetException() instanceof IllegalArgumentException)) {
throw new CodeGenerationException(ex.getTargetException());
}
// in case of IllegalArgumentException: fall through to defineClass
}
catch (Throwable ex) {
throw new CodeGenerationException(ex);
}
}
if (protectionDomain == null) {
protectionDomain = PROTECTION_DOMAIN;
}
if (c == null) {
if (classLoaderDefineClassMethod != null) {
Object[] args = new Object[]{className, b, 0, b.length, protectionDomain};
try {
if (!classLoaderDefineClassMethod.isAccessible()) {
classLoaderDefineClassMethod.setAccessible(true);
}
c = (Class) classLoaderDefineClassMethod.invoke(loader, args);
}
catch (InvocationTargetException ex) {
throw new CodeGenerationException(ex.getTargetException());
}
catch (Throwable ex) {
throw new CodeGenerationException(ex);
}
}
else {
throw new CodeGenerationException(THROWABLE);
}
}
// Force static initializers to run.
Class.forName(className, true, loader);
return c;
}
// SPRING PATCH END
public static int findPackageProtected(Class[] classes) {
for (int i = 0; i < classes.length; i++) {
if (!Modifier.isPublic(classes[i].getModifiers())) {
return i;
}
}
return 0;
}
public static MethodInfo getMethodInfo(final Member member, final int modifiers) {
final Signature sig = getSignature(member);
return new MethodInfo() {
private ClassInfo ci;
public ClassInfo getClassInfo() {
if (ci == null)
ci = ReflectUtils.getClassInfo(member.getDeclaringClass());
return ci;
}
public int getModifiers() {
return modifiers;
}
public Signature getSignature() {
return sig;
}
public Type[] getExceptionTypes() {
return ReflectUtils.getExceptionTypes(member);
}
public Attribute getAttribute() {
return null;
}
};
}
public static MethodInfo getMethodInfo(Member member) {
return getMethodInfo(member, member.getModifiers());
}
public static ClassInfo getClassInfo(final Class clazz) {
final Type type = Type.getType(clazz);
final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass());
return new ClassInfo() {
public Type getType() {
return type;
}
public Type getSuperType() {
return sc;
}
public Type[] getInterfaces() {
return TypeUtils.getTypes(clazz.getInterfaces());
}
public int getModifiers() {
return clazz.getModifiers();
}
};
}
// used by MethodInterceptorGenerated generated code
public static Method[] findMethods(String[] namesAndDescriptors, Method[] methods) {
Map map = new HashMap();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
map.put(method.getName() + Type.getMethodDescriptor(method), method);
}
Method[] result = new Method[namesAndDescriptors.length / 2];
for (int i = 0; i < result.length; i++) {
result[i] = (Method) map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]);
if (result[i] == null) {
// TODO: error?
}
}
return result;
}
}

View File

@ -0,0 +1,10 @@
/**
* Spring's repackaging of the
* <a href="http://cglib.sourceforge.net">CGLIB</a> core package
* (for internal use only).
*
* <p>As this repackaging happens at the class file level, sources
* and javadocs are not available here... except for a few files
* that have been patched for Spring's purposes on JDK 9/10/11.
*/
package org.springframework.cglib.core;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,251 @@
/*
* 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
*
* http://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.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.cglib.core.AbstractClassGenerator;
import org.springframework.cglib.core.CodeGenerationException;
import org.springframework.cglib.core.GeneratorStrategy;
import org.springframework.cglib.core.NamingPolicy;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.reflect.FastClass;
/**
* Classes generated by {@link Enhancer} pass this object to the
* registered {@link MethodInterceptor} objects when an intercepted method is invoked. It can
* be used to either invoke the original method, or call the same method on a different
* object of the same type.
* @version $Id: MethodProxy.java,v 1.16 2009/01/11 20:09:48 herbyderby Exp $
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class MethodProxy {
private Signature sig1;
private Signature sig2;
private CreateInfo createInfo;
private final Object initLock = new Object();
private volatile FastClassInfo fastClassInfo;
/**
* For internal use by {@link Enhancer} only; see the {@link org.springframework.cglib.reflect.FastMethod} class
* for similar functionality.
*/
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
private void init() {
/*
* Using a volatile invariant allows us to initialize the FastClass and
* method index pairs atomically.
*
* Double-checked locking is safe with volatile in Java 5. Before 1.5 this
* code could allow fastClassInfo to be instantiated more than once, which
* appears to be benign.
*/
if (fastClassInfo == null) {
synchronized (initLock) {
if (fastClassInfo == null) {
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
}
}
}
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
}
private static class CreateInfo {
Class c1;
Class c2;
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad;
public CreateInfo(Class c1, Class c2) {
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if (fromEnhancer != null) {
namingPolicy = fromEnhancer.getNamingPolicy();
strategy = fromEnhancer.getStrategy();
attemptLoad = fromEnhancer.getAttemptLoad();
}
}
}
private static FastClass helper(CreateInfo ci, Class type) {
FastClass.Generator g = new FastClass.Generator();
g.setType(type);
// SPRING PATCH BEGIN
g.setContextClass(type);
// SPRING PATCH END
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
private MethodProxy() {
}
/**
* Return the signature of the proxied method.
*/
public Signature getSignature() {
return sig1;
}
/**
* Return the name of the synthetic method created by CGLIB which is
* used by {@link #invokeSuper} to invoke the superclass
* (non-intercepted) method implementation. The parameter types are
* the same as the proxied method.
*/
public String getSuperName() {
return sig2.getName();
}
/**
* Return the {@link org.springframework.cglib.reflect.FastClass} method index
* for the method used by {@link #invokeSuper}. This index uniquely
* identifies the method within the generated proxy, and therefore
* can be useful to reference external metadata.
* @see #getSuperName
*/
public int getSuperIndex() {
init();
return fastClassInfo.i2;
}
// For testing
FastClass getFastClass() {
init();
return fastClassInfo.f1;
}
// For testing
FastClass getSuperFastClass() {
init();
return fastClassInfo.f2;
}
/**
* Return the <code>MethodProxy</code> used when intercepting the method
* matching the given signature.
* @param type the class generated by Enhancer
* @param sig the signature to match
* @return the MethodProxy instance, or null if no applicable matching method is found
* @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor
*/
public static MethodProxy find(Class type, Signature sig) {
try {
Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
MethodInterceptorGenerator.FIND_PROXY_TYPES);
return (MethodProxy) m.invoke(null, new Object[]{sig});
}
catch (NoSuchMethodException ex) {
throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
}
catch (IllegalAccessException | InvocationTargetException ex) {
throw new CodeGenerationException(ex);
}
}
/**
* Invoke the original method, on a different object of the same type.
* @param obj the compatible object; recursion will result if you use the object passed as the first
* argument to the MethodInterceptor (usually not what you want)
* @param args the arguments passed to the intercepted method; you may substitute a different
* argument array as long as the types are compatible
* @throws Throwable the bare exceptions thrown by the called method are passed through
* without wrapping in an <code>InvocationTargetException</code>
* @see MethodInterceptor#intercept
*/
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw ex;
}
}
/**
* Invoke the original (super) method on the specified object.
* @param obj the enhanced object, must be the object passed as the first
* argument to the MethodInterceptor
* @param args the arguments passed to the intercepted method; you may substitute a different
* argument array as long as the types are compatible
* @throws Throwable the bare exceptions thrown by the called method are passed through
* without wrapping in an <code>InvocationTargetException</code>
* @see MethodInterceptor#intercept
*/
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
}
catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
}

View File

@ -0,0 +1,10 @@
/**
* Spring's repackaging of the
* <a href="http://cglib.sourceforge.net">CGLIB</a> proxy package
* (for internal use only).
*
* <p>As this repackaging happens at the class file level, sources
* and javadocs are not available here... except for a few files
* that have been patched for Spring's purposes on JDK 9/10/11.
*/
package org.springframework.cglib.proxy;