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:
parent
cdaa247861
commit
61c3db0869
|
@ -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 {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
Loading…
Reference in New Issue