Skip to content

Commit c9ce7aa

Browse files
author
Phillip Webb
committed
Merge pull request spring-projects#327 from olivergierke/SPR-10594
# By Oliver Gierke * SPR-10594: Add support for Objenesis proxy creation.
2 parents 41fffdc + 1f9e8f6 commit c9ce7aa

File tree

10 files changed

+289
-62
lines changed

10 files changed

+289
-62
lines changed

build.gradle

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,12 @@ project("spring-core") {
166166
// further transformed by the JarJar task to depend on org.springframework.asm; this
167167
// avoids including two different copies of asm unnecessarily.
168168
def cglibVersion = "3.0"
169+
def objenesisVersion = "2.0"
169170

170171
configurations {
171172
jarjar
172173
cglib
174+
objenesis
173175
}
174176

175177
task cglibRepackJar(type: Jar) { repackJar ->
@@ -195,8 +197,28 @@ project("spring-core") {
195197
}
196198
}
197199

200+
task objenesisRepackJar(type: Jar) { repackJar ->
201+
repackJar.baseName = "spring-objenesis-repack"
202+
repackJar.version = objenesisVersion
203+
204+
doLast() {
205+
project.ant {
206+
taskdef name: "jarjar", classname: "com.tonicsystems.jarjar.JarJarTask",
207+
classpath: configurations.jarjar.asPath
208+
jarjar(destfile: repackJar.archivePath) {
209+
configurations.objenesis.each { originalJar ->
210+
zipfileset(src: originalJar)
211+
}
212+
// repackage org.objenesis => org.springframework.objenesis
213+
rule(pattern: "org.objenesis.**", result: "org.springframework.objenesis.@1")
214+
}
215+
}
216+
}
217+
}
218+
198219
dependencies {
199220
cglib("cglib:cglib:${cglibVersion}@jar")
221+
objenesis("org.objenesis:objenesis:${objenesisVersion}@jar")
200222
jarjar("com.googlecode.jarjar:jarjar:1.3")
201223

202224
compile(files(cglibRepackJar))
@@ -216,6 +238,11 @@ project("spring-core") {
216238
from(zipTree(cglibRepackJar.archivePath)) {
217239
include "org/springframework/cglib/**"
218240
}
241+
242+
dependsOn objenesisRepackJar
243+
from(zipTree(objenesisRepackJar.archivePath)) {
244+
include "org/springframework/objenesis/**"
245+
}
219246
}
220247
}
221248

@@ -237,6 +264,7 @@ project("spring-aop") {
237264
dependencies {
238265
compile(project(":spring-core"))
239266
compile(files(project(":spring-core").cglibRepackJar))
267+
compile(files(project(":spring-core").objenesisRepackJar))
240268
compile(project(":spring-beans"))
241269
compile("aopalliance:aopalliance:1.0")
242270
optional("com.jamonapi:jamon:2.4")

spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
* @see DefaultAopProxyFactory
8080
*/
8181
@SuppressWarnings("serial")
82-
final class CglibAopProxy implements AopProxy, Serializable {
82+
class CglibAopProxy implements AopProxy, Serializable {
8383

8484
// Constants for CGLIB callback array indices
8585
private static final int AOP_PROXY = 0;
@@ -185,29 +185,20 @@ public Object getProxy(ClassLoader classLoader) {
185185
enhancer.setSuperclass(proxySuperClass);
186186
enhancer.setStrategy(new MemorySafeUndeclaredThrowableStrategy(UndeclaredThrowableException.class));
187187
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
188-
enhancer.setInterceptDuringConstruction(false);
189188

190189
Callback[] callbacks = getCallbacks(rootClass);
191-
enhancer.setCallbacks(callbacks);
192-
enhancer.setCallbackFilter(new ProxyCallbackFilter(
193-
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
194-
195190
Class<?>[] types = new Class[callbacks.length];
191+
196192
for (int x = 0; x < types.length; x++) {
197193
types[x] = callbacks[x].getClass();
198194
}
195+
199196
enhancer.setCallbackTypes(types);
197+
enhancer.setCallbackFilter(new ProxyCallbackFilter(
198+
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
200199

201200
// Generate the proxy class and create a proxy instance.
202-
Object proxy;
203-
if (this.constructorArgs != null) {
204-
proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
205-
}
206-
else {
207-
proxy = enhancer.create();
208-
}
209-
210-
return proxy;
201+
return createProxyClassAndInstance(enhancer, callbacks);
211202
}
212203
catch (CodeGenerationException ex) {
213204
throw new AopConfigException("Could not generate CGLIB subclass of class [" +
@@ -227,6 +218,15 @@ public Object getProxy(ClassLoader classLoader) {
227218
}
228219
}
229220

221+
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
222+
223+
enhancer.setInterceptDuringConstruction(false);
224+
enhancer.setCallbacks(callbacks);
225+
226+
return this.constructorArgs == null ? enhancer.create() : enhancer.create(
227+
this.constructorArgTypes, this.constructorArgs);
228+
}
229+
230230
/**
231231
* Creates the CGLIB {@link Enhancer}. Subclasses may wish to override this to return a custom
232232
* {@link Enhancer} implementation.

spring-aop/src/main/java/org/springframework/aop/framework/DefaultAopProxyFactory.java

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException
5959
if (targetClass.isInterface()) {
6060
return new JdkDynamicAopProxy(config);
6161
}
62-
return CglibProxyFactory.createCglibProxy(config);
62+
return new ObjenesisCglibAopProxy(config);
6363
}
6464
else {
6565
return new JdkDynamicAopProxy(config);
@@ -75,17 +75,4 @@ private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
7575
Class[] interfaces = config.getProxiedInterfaces();
7676
return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy.class.equals(interfaces[0])));
7777
}
78-
79-
80-
/**
81-
* Inner factory class used to just introduce a CGLIB dependency
82-
* when actually creating a CGLIB proxy.
83-
*/
84-
private static class CglibProxyFactory {
85-
86-
public static AopProxy createCglibProxy(AdvisedSupport advisedSupport) {
87-
return new CglibAopProxy(advisedSupport);
88-
}
89-
}
90-
9178
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.aop.framework;
18+
19+
20+
import org.apache.commons.logging.Log;
21+
import org.apache.commons.logging.LogFactory;
22+
import org.springframework.cglib.proxy.Callback;
23+
import org.springframework.cglib.proxy.Enhancer;
24+
import org.springframework.cglib.proxy.Factory;
25+
import org.springframework.objenesis.ObjenesisException;
26+
import org.springframework.objenesis.ObjenesisStd;
27+
28+
/**
29+
* Objenesis based extension of {@link CglibAopProxy} to create proxy instances without
30+
* invoking the constructor of the class.
31+
*
32+
* @author Oliver Gierke
33+
* @since 4.0
34+
*/
35+
class ObjenesisCglibAopProxy extends CglibAopProxy {
36+
37+
private static final Log logger = LogFactory.getLog(ObjenesisCglibAopProxy.class);
38+
39+
private final ObjenesisStd objenesis;
40+
41+
42+
/**
43+
* Creates a new {@link ObjenesisCglibAopProxy} using the given {@link AdvisedSupport}.
44+
* @param config must not be {@literal null}.
45+
*/
46+
public ObjenesisCglibAopProxy(AdvisedSupport config) {
47+
super(config);
48+
this.objenesis = new ObjenesisStd(true);
49+
}
50+
51+
52+
@Override
53+
@SuppressWarnings("unchecked")
54+
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
55+
try {
56+
Factory factory = (Factory) objenesis.newInstance(enhancer.createClass());
57+
factory.setCallbacks(callbacks);
58+
return factory;
59+
}
60+
catch (ObjenesisException ex) {
61+
// Fallback to Cglib on unsupported JVMs
62+
if (logger.isDebugEnabled()) {
63+
logger.debug("Unable to instantiate proxy using Objenesis, falling back "
64+
+ "to regular proxy construction", ex);
65+
}
66+
return super.createProxyClassAndInstance(enhancer, callbacks);
67+
}
68+
}
69+
70+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.aop.framework;
18+
19+
public class ClassWithConstructor {
20+
21+
public ClassWithConstructor(Object object) {
22+
23+
}
24+
25+
public void method() {
26+
27+
}
28+
}

spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,6 @@
1616

1717
package org.springframework.aop.framework;
1818

19-
import static org.hamcrest.CoreMatchers.instanceOf;
20-
import static org.junit.Assert.assertEquals;
21-
import static org.junit.Assert.assertNotNull;
22-
import static org.junit.Assert.assertThat;
23-
import static org.junit.Assert.assertTrue;
24-
import static org.junit.Assert.fail;
25-
2619
import java.io.Serializable;
2720

2821
import org.aopalliance.intercept.MethodInterceptor;
@@ -33,7 +26,6 @@
3326
import org.springframework.aop.Pointcut;
3427
import org.springframework.aop.support.AopUtils;
3528
import org.springframework.aop.support.DefaultPointcutAdvisor;
36-
import org.springframework.cglib.core.CodeGenerationException;
3729
import org.springframework.context.ApplicationContext;
3830
import org.springframework.context.ApplicationContextException;
3931
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -43,6 +35,8 @@
4335
import org.springframework.tests.sample.beans.TestBean;
4436

4537
import test.mixin.LockMixinAdvisor;
38+
import static org.hamcrest.CoreMatchers.*;
39+
import static org.junit.Assert.*;
4640

4741
/**
4842
* Additional and overridden tests for the CGLIB proxy.
@@ -135,31 +129,6 @@ public void testProxyCanBeClassNotInterface() throws Exception {
135129
assertEquals(32, tb.getAge());
136130
}
137131

138-
@Test
139-
public void testCglibProxyingGivesMeaningfulExceptionIfAskedToProxyNonvisibleClass() {
140-
141-
@SuppressWarnings("unused")
142-
class YouCantSeeThis {
143-
void hidden() {
144-
}
145-
}
146-
147-
YouCantSeeThis mine = new YouCantSeeThis();
148-
try {
149-
ProxyFactory pf = new ProxyFactory(mine);
150-
pf.getProxy();
151-
fail("Shouldn't be able to proxy non-visible class with CGLIB");
152-
}
153-
catch (AopConfigException ex) {
154-
// Check that stack trace is preserved
155-
assertTrue(ex.getCause() instanceof CodeGenerationException ||
156-
ex.getCause() instanceof IllegalArgumentException);
157-
// Check that error message is helpful
158-
assertTrue(ex.getMessage().indexOf("final") != -1);
159-
assertTrue(ex.getMessage().indexOf("visible") != -1);
160-
}
161-
}
162-
163132
@Test
164133
public void testMethodInvocationDuringConstructor() {
165134
CglibTestBean bean = new CglibTestBean();
@@ -348,6 +317,7 @@ public void testExceptionHandling() {
348317
}
349318

350319
@Test
320+
@SuppressWarnings("resource")
351321
public void testWithDependencyChecking() {
352322
ApplicationContext ctx =
353323
new ClassPathXmlApplicationContext(DEPENDENCY_CHECK_CONTEXT, getClass());
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.aop.framework;
18+
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.stereotype.Component;
21+
import org.springframework.util.Assert;
22+
23+
/**
24+
* @author Oliver Gierke
25+
*/
26+
@Component
27+
public class ClassWithComplexConstructor {
28+
29+
private final Dependency dependency;
30+
31+
@Autowired
32+
public ClassWithComplexConstructor(Dependency dependency) {
33+
Assert.notNull(dependency);
34+
this.dependency = dependency;
35+
}
36+
37+
public Dependency getDependency() {
38+
return dependency;
39+
}
40+
41+
public void method() {
42+
this.dependency.method();
43+
}
44+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2002-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.aop.framework;
18+
19+
import org.springframework.stereotype.Component;
20+
21+
@Component class Dependency {
22+
23+
private int value = 0;
24+
25+
public void method() {
26+
value++;
27+
}
28+
29+
public int getValue() {
30+
return value;
31+
}
32+
}

0 commit comments

Comments
 (0)