You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2021/08/23 13:12:12 UTC

[wicket] 01/01: WICKET-6913 bytebuddy for wicket-9.x

This is an automated email from the ASF dual-hosted git repository.

svenmeier pushed a commit to branch WICKET-6913-wicket-9.x
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit 8287aa9f762a08574fe4ff433524aa9f374687fc
Author: Sven Meier <sv...@apache.org>
AuthorDate: Mon Aug 23 15:02:31 2021 +0200

    WICKET-6913 bytebuddy for wicket-9.x
    
    optional via system parameter wicket.ioc.proxyfactory
---
 NOTICE                                             |   7 +-
 README.md                                          |   3 +-
 pom.xml                                            |   6 +
 wicket-examples/src/main/resources/META-INF/NOTICE |   9 +-
 wicket-ioc/pom.xml                                 |   6 +-
 wicket-ioc/src/main/java/module-info.java          |   1 +
 .../apache/wicket/proxy/IProxyFactory.java}        |  25 +-
 .../apache/wicket/proxy/IProxyTargetLocator.java   |   3 +-
 .../apache/wicket/proxy/LazyInitProxyFactory.java  | 434 ++++++---------------
 .../proxy/bytebuddy/ByteBuddyProxyFactory.java     | 302 ++++++++++++++
 .../ObjenesisByteBuddyInterceptor.java}            |  11 +-
 .../ObjenesisProxyFactory.java}                    |  25 +-
 .../ObjenesisProxyReplacement.java                 |   5 +-
 .../wicket/proxy/cglib/CglibProxyFactory.java      | 155 ++++++++
 .../apache/wicket/proxy/jdk/JdkProxyFactory.java   | 196 ++++++++++
 .../proxy/objenesis/ObjenesisCGLibInterceptor.java |   4 +-
 .../proxy/objenesis/ObjenesisProxyReplacement.java |   4 +-
 .../wicket/injection/util/MockDependency.java      |   5 +-
 .../annot/SpringBeanWithGenericsTest.java          |   2 +-
 19 files changed, 832 insertions(+), 371 deletions(-)

diff --git a/NOTICE b/NOTICE
index 3e462db..bc97306 100644
--- a/NOTICE
+++ b/NOTICE
@@ -56,8 +56,11 @@ src/./wicket-examples
    This product includes software developed by the CGLib Project
    (http://cglib.sourceforge.net).
 
-   This product includes ASM, released under a BSD style license (http://asm.objectweb.org).
-   Copyright (c) 2000-2005 INRIA, France Telecom
+   This product includes ASM, released under a BSD style license (https://asm.ow2.io/).
+   Copyright (c) 2000-2011 INRIA, France Telecom
+
+   This product includes software developed by the ByteBuddy Project
+   (https://bytebuddy.net/).
 
    This product includes jhighlight (https://jhighlight.dev.java.net/)
    which is released under CDDL 1.0 license (http://www.opensource.org/licenses/cddl1.php).
diff --git a/README.md b/README.md
index 01e9982..8faa041 100644
--- a/README.md
+++ b/README.md
@@ -159,7 +159,8 @@ the src/ folder.
  - wicket-ioc:
 
     cglib 3.1 (http://cglib.sourceforge.net/) and 
-    asm-util 5.0.3 (http://asm.objectweb.org/)
+    asm-util 9.1 (https://asm.ow2.io/)
+    byte-buddy 1.11.12 (https://bytebuddy.net/) and 
 
  - wicket-spring:
 
diff --git a/pom.xml b/pom.xml
index b4cbc76..589c2a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -139,6 +139,7 @@
 		<assertj-core.version>3.19.0</assertj-core.version>
 		<cdi-unit.version>4.1.0</cdi-unit.version>
 		<cglib.version>3.3.0</cglib.version>
+		<byte-buddy.version>1.11.12</byte-buddy.version>
 		<commons-collections.version>3.2.2</commons-collections.version>
 		<commons-collections4.version>4.4</commons-collections4.version>
 		<commons-fileupload.version>1.4</commons-fileupload.version>
@@ -321,6 +322,11 @@
 				<optional>true</optional>
 			</dependency>
 			<dependency>
+				<groupId>net.bytebuddy</groupId>
+				<artifactId>byte-buddy</artifactId>
+				<version>${byte-buddy.version}</version>
+			</dependency>
+			<dependency>
 				<groupId>org.apache.commons</groupId>
 				<artifactId>commons-collections4</artifactId>
 				<version>${commons-collections4.version}</version>
diff --git a/wicket-examples/src/main/resources/META-INF/NOTICE b/wicket-examples/src/main/resources/META-INF/NOTICE
index 619dd91..3598cb3 100644
--- a/wicket-examples/src/main/resources/META-INF/NOTICE
+++ b/wicket-examples/src/main/resources/META-INF/NOTICE
@@ -29,8 +29,11 @@
    This product includes software developed by the CGLib Project
    (http://cglib.sourceforge.net).
 
-   This product includes ASM, released under a BSD style license (http://asm.objectweb.org).
-   Copyright (c) 2000-2005 INRIA, France Telecom
+   This product includes ASM, released under a BSD style license (https://asm.ow2.io/).
+   Copyright (c) 2000-2011 INRIA, France Telecom
+
+   This product includes software developed by the ByteBuddy Project
+   (https://bytebuddy.net/).
 
    This product includes jhighlight (https://jhighlight.dev.java.net/)
    which is released under CDDL 1.0 license (http://www.opensource.org/licenses/cddl1.php).
@@ -42,4 +45,4 @@
    jQuery Foundation, Inc, http://jquery.org/license
 
    Contains qunit.css released under a MIT style license.
-   jQuery Foundation, Inc, http://jquery.org/license
\ No newline at end of file
+   jQuery Foundation, Inc, http://jquery.org/license
diff --git a/wicket-ioc/pom.xml b/wicket-ioc/pom.xml
index d34c07f..8ccb684 100644
--- a/wicket-ioc/pom.xml
+++ b/wicket-ioc/pom.xml
@@ -41,9 +41,13 @@
 			<artifactId>javax.inject</artifactId>
 		</dependency>
 		<dependency>
+			<groupId>net.bytebuddy</groupId>
+			<artifactId>byte-buddy</artifactId>
+			<optional>true</optional>
+		</dependency>
+		<dependency>
 			<groupId>org.apache.wicket</groupId>
 			<artifactId>wicket-core</artifactId>
-			<version>${project.version}</version>
 		</dependency>
 		<dependency>
 			<groupId>org.objenesis</groupId>
diff --git a/wicket-ioc/src/main/java/module-info.java b/wicket-ioc/src/main/java/module-info.java
index 6f7f7e1..d175804 100644
--- a/wicket-ioc/src/main/java/module-info.java
+++ b/wicket-ioc/src/main/java/module-info.java
@@ -19,6 +19,7 @@ module org.apache.wicket.ioc {
     requires org.apache.wicket.util;
     requires org.apache.wicket.core;
     requires cglib;
+    requires net.bytebuddy;
     requires org.objenesis;
 
     exports org.apache.wicket.injection;
diff --git a/wicket-ioc/src/main/java/module-info.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyFactory.java
similarity index 70%
copy from wicket-ioc/src/main/java/module-info.java
copy to wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyFactory.java
index 6f7f7e1..ef5f638 100644
--- a/wicket-ioc/src/main/java/module-info.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyFactory.java
@@ -14,14 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.apache.wicket.proxy;
 
-module org.apache.wicket.ioc {
-    requires org.apache.wicket.util;
-    requires org.apache.wicket.core;
-    requires cglib;
-    requires org.objenesis;
-
-    exports org.apache.wicket.injection;
-    exports org.apache.wicket.proxy;
-    exports org.apache.wicket.proxy.objenesis;
+/**
+ * A factory of proxies.
+ */
+public interface IProxyFactory
+{
+	/**
+	 * Create a proxy.
+	 * 
+	 * @param type
+	 *            the target type
+	 * @param locator
+	 *            the locator of the target
+	 * @return a proxy
+	 */
+	public Object createProxy(final Class<?> type, final IProxyTargetLocator locator);
 }
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyTargetLocator.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyTargetLocator.java
index 2b8eb25..6df597e 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyTargetLocator.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/IProxyTargetLocator.java
@@ -16,6 +16,7 @@
  */
 package org.apache.wicket.proxy;
 
+import org.apache.wicket.proxy.cglib.CglibProxyFactory;
 import org.apache.wicket.util.io.IClusterable;
 
 /**
@@ -43,7 +44,7 @@ import org.apache.wicket.util.io.IClusterable;
  * }
  * </pre>
  * 
- * @see LazyInitProxyFactory#createProxy(Class, IProxyTargetLocator)
+ * @see CglibProxyFactory#createProxy(Class, IProxyTargetLocator)
  * 
  * @author Igor Vaynberg (ivaynberg)
  * 
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java
index ccc7769..dc1ae95 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/LazyInitProxyFactory.java
@@ -18,32 +18,23 @@ package org.apache.wicket.proxy;
 
 import java.io.ObjectStreamException;
 import java.io.Serializable;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Proxy;
 import java.util.Arrays;
 import java.util.List;
 
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.core.util.lang.WicketObjects;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.proxy.cglib.CglibProxyFactory;
+import org.apache.wicket.proxy.jdk.JdkProxyFactory;
+import org.apache.wicket.util.io.IClusterable;
+
 import net.sf.cglib.core.DefaultNamingPolicy;
 import net.sf.cglib.core.Predicate;
-import net.sf.cglib.proxy.Callback;
-import net.sf.cglib.proxy.CallbackFilter;
-import net.sf.cglib.proxy.Enhancer;
 import net.sf.cglib.proxy.MethodInterceptor;
 import net.sf.cglib.proxy.MethodProxy;
 import net.sf.cglib.proxy.NoOp;
 
-import org.apache.wicket.Application;
-import org.apache.wicket.WicketRuntimeException;
-import org.apache.wicket.core.util.lang.WicketObjects;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.proxy.objenesis.ObjenesisProxyFactory;
-import org.apache.wicket.util.io.IClusterable;
-import org.apache.wicket.util.string.Strings;
-
 /**
  * A factory class that creates lazy init proxies given a type and a {@link IProxyTargetLocator}
  * used to retrieve the object the proxy will represent.
@@ -53,7 +44,7 @@ import org.apache.wicket.util.string.Strings;
  * forwarded.
  * <p>
  * This factory creates two kinds of proxies: A standard dynamic proxy when the specified type is an
- * interface, and a CGLib proxy when the specified type is a concrete class.
+ * interface, and a ByteBuddy proxy when the specified type is a concrete class.
  * <p>
  * The general use case for such a proxy is to represent a dependency that should not be serialized
  * with a wicket page or {@link IModel}. The solution is to serialize the proxy and the
@@ -112,16 +103,29 @@ public class LazyInitProxyFactory
 	/**
 	 * Primitive java types and their object wrappers
 	 */
-	@SuppressWarnings({ "unchecked", "rawtypes" })
-	private static final List PRIMITIVES = Arrays.asList(String.class, byte.class, Byte.class,
+	private static final List<Class<?>> PRIMITIVES = Arrays.asList(String.class, byte.class, Byte.class,
 		short.class, Short.class, int.class, Integer.class, long.class, Long.class, float.class,
 		Float.class, double.class, Double.class, char.class, Character.class, boolean.class,
 		Boolean.class);
-
-	private static final int CGLIB_CALLBACK_NO_OVERRIDE = 0;
-	private static final int CGLIB_CALLBACK_HANDLER = 1;
-
-	private static final boolean IS_OBJENESIS_AVAILABLE = isObjenesisAvailable();
+	
+	private static final IProxyFactory proxyFactory = initProxyFactory();
+	
+    private static IProxyFactory initProxyFactory() {
+		IProxyFactory proxyFactory = null; 
+		
+		String factoryName = System.getProperty("wicket.ioc.proxyfactory");
+		if (factoryName == null) {
+			proxyFactory = new CglibProxyFactory();
+		} else {
+			try {
+				proxyFactory = (IProxyFactory) Class.forName(factoryName).getConstructor().newInstance();
+			} catch (Exception e) {
+				throw new Error(String.format("wicket.ioc.proxyFactory=%s", factoryName), e);
+			}
+		}
+		
+		return proxyFactory;
+	}
 
 	/**
 	 * Create a lazy init proxy for the specified type. The target object will be located using the
@@ -145,70 +149,14 @@ public class LazyInitProxyFactory
 		}
 		else if (type.isInterface())
 		{
-			JdkHandler handler = new JdkHandler(type, locator);
-
-			try
-			{
-				return Proxy.newProxyInstance(resolveClassLoader(),
-					new Class[] { type, Serializable.class, ILazyInitProxy.class,
-							IWriteReplace.class }, handler);
-			}
-			catch (IllegalArgumentException e)
-			{
-				/*
-				 * STW: In some clustering environments it appears the context classloader fails to
-				 * load the proxied interface (currently seen in BEA WLS 9.x clusters). If this
-				 * happens, we can try and fall back to the classloader (current) that actually
-				 * loaded this class.
-				 */
-				return Proxy.newProxyInstance(LazyInitProxyFactory.class.getClassLoader(),
-					new Class[] { type, Serializable.class, ILazyInitProxy.class,
-							IWriteReplace.class }, handler);
-			}
-
-		}
-		else if (IS_OBJENESIS_AVAILABLE && !hasNoArgConstructor(type))
-		{
-			return ObjenesisProxyFactory.createProxy(type, locator, WicketNamingPolicy.INSTANCE);
-		}
-		else
-		{
-			CGLibInterceptor handler = new CGLibInterceptor(type, locator);
-
-			Callback[] callbacks = new Callback[2];
-			callbacks[CGLIB_CALLBACK_NO_OVERRIDE] = SerializableNoOpCallback.INSTANCE;
-			callbacks[CGLIB_CALLBACK_HANDLER] = handler;
-
-			Enhancer e = new Enhancer();
-			e.setClassLoader(resolveClassLoader());
-			e.setInterfaces(new Class[] { Serializable.class, ILazyInitProxy.class,
-					IWriteReplace.class });
-			e.setSuperclass(type);
-			e.setCallbackFilter(NoOpForProtectedMethodsCGLibCallbackFilter.INSTANCE);
-			e.setCallbacks(callbacks);
-			e.setNamingPolicy(WicketNamingPolicy.INSTANCE);
-
-			return e.create();
-		}
-	}
-
-	private static ClassLoader resolveClassLoader()
-	{
-		ClassLoader classLoader = null;
-		if (Application.exists())
-		{
-			classLoader = Application.get().getApplicationSettings()
-					.getClassResolver().getClassLoader();
+			return new JdkProxyFactory().createProxy(type, locator);
 		}
-
-		if (classLoader == null) {
-			classLoader = Thread.currentThread().getContextClassLoader();
+		else {
+			return proxyFactory.createProxy(type, locator);
 		}
-
-		return classLoader;
 	}
-
-    /**
+	
+	/**
 	 * This interface is used to make the proxy forward writeReplace() call to the handler instead
 	 * of invoking it on itself. This allows us to serialize the replacement object instead of the
 	 * proxy itself in case the proxy subclass is deserialized on a VM that does not have it
@@ -229,7 +177,7 @@ public class LazyInitProxyFactory
 		 */
 		Object writeReplace() throws ObjectStreamException;
 	}
-
+	
 	/**
 	 * Object that replaces the proxy when it is serialized. Upon deserialization this object will
 	 * create a new proxy with the same locator.
@@ -237,7 +185,7 @@ public class LazyInitProxyFactory
 	 * @author Igor Vaynberg (ivaynberg)
 	 * 
 	 */
-	static class ProxyReplacement implements IClusterable
+	public static class ProxyReplacement implements IClusterable
 	{
 		private static final long serialVersionUID = 1L;
 
@@ -286,6 +234,72 @@ public class LazyInitProxyFactory
 	}
 
 	/**
+	 * Checks if the method is derived from Object.equals()
+	 * 
+	 * @param method
+	 *            method being tested
+	 * @return true if the method is derived from Object.equals(), false otherwise
+	 */
+	public static boolean isEqualsMethod(final Method method)
+	{
+		return (method.getReturnType() == boolean.class) &&
+			(method.getParameterTypes().length == 1) &&
+			(method.getParameterTypes()[0] == Object.class) && method.getName().equals("equals");
+	}
+
+	/**
+	 * Checks if the method is derived from Object.hashCode()
+	 * 
+	 * @param method
+	 *            method being tested
+	 * @return true if the method is defined from Object.hashCode(), false otherwise
+	 */
+	public static boolean isHashCodeMethod(final Method method)
+	{
+		return (method.getReturnType() == int.class) && (method.getParameterTypes().length == 0) &&
+			method.getName().equals("hashCode");
+	}
+
+	/**
+	 * Checks if the method is derived from Object.toString()
+	 * 
+	 * @param method
+	 *            method being tested
+	 * @return true if the method is defined from Object.toString(), false otherwise
+	 */
+	public static boolean isToStringMethod(final Method method)
+	{
+		return (method.getReturnType() == String.class) &&
+			(method.getParameterTypes().length == 0) && method.getName().equals("toString");
+	}
+
+	/**
+	 * Checks if the method is derived from Object.finalize()
+	 * 
+	 * @param method
+	 *            method being tested
+	 * @return true if the method is defined from Object.finalize(), false otherwise
+	 */
+	public static boolean isFinalizeMethod(final Method method)
+	{
+		return (method.getReturnType() == void.class) && (method.getParameterTypes().length == 0) &&
+			method.getName().equals("finalize");
+	}
+
+	/**
+	 * Checks if the method is the writeReplace method
+	 * 
+	 * @param method
+	 *            method being tested
+	 * @return true if the method is the writeReplace method, false otherwise
+	 */
+	public static boolean isWriteReplaceMethod(final Method method)
+	{
+		return (method.getReturnType() == Object.class) &&
+			(method.getParameterTypes().length == 0) && method.getName().equals("writeReplace");
+	}
+
+	/**
 	 * Method interceptor for proxies representing concrete object not backed by an interface. These
 	 * proxies are represented by cglib proxies.
 	 * 
@@ -331,24 +345,24 @@ public class LazyInitProxyFactory
 		public Object intercept(final Object object, final Method method, final Object[] args,
 			final MethodProxy proxy) throws Throwable
 		{
-			if (isFinalizeMethod(method))
+			if (LazyInitProxyFactory.isFinalizeMethod(method))
 			{
 				// swallow finalize call
 				return null;
 			}
-			else if (isEqualsMethod(method))
+			else if (LazyInitProxyFactory.isEqualsMethod(method))
 			{
 				return (equals(args[0])) ? Boolean.TRUE : Boolean.FALSE;
 			}
-			else if (isHashCodeMethod(method))
+			else if (LazyInitProxyFactory.isHashCodeMethod(method))
 			{
 				return hashCode();
 			}
-			else if (isToStringMethod(method))
+			else if (LazyInitProxyFactory.isToStringMethod(method))
 			{
 				return toString();
 			}
-			else if (isWriteReplaceMethod(method))
+			else if (LazyInitProxyFactory.isWriteReplaceMethod(method))
 			{
 				return writeReplace();
 			}
@@ -380,16 +394,13 @@ public class LazyInitProxyFactory
 	 *
 	 * @author Igor Vaynberg (ivaynberg)
 	 */
-	protected static class CGLibInterceptor extends AbstractCGLibInterceptor
+	public static class CGLibInterceptor extends AbstractCGLibInterceptor
 	{
 		public CGLibInterceptor(Class<?> type, IProxyTargetLocator locator)
 		{
 			super(type, locator);
 		}
 
-		/**
-		 * @see org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace#writeReplace()
-		 */
 		@Override
 		public Object writeReplace() throws ObjectStreamException
 		{
@@ -404,214 +415,10 @@ public class LazyInitProxyFactory
 	{
 		private static final long serialVersionUID = 1L;
 
-		private static final NoOp INSTANCE = new SerializableNoOpCallback();
-	}
-
-	/**
-	 * CGLib callback filter which does not intercept protected methods.
-	 * 
-	 * Protected methods need to be called with invokeSuper() instead of invoke().
-	 * When invoke() is called on a protected method, it throws an "IllegalArgumentException:
-	 * Protected method" exception.
-	 * That being said, we do not need to intercept the protected methods so this callback filter
-	 * is designed to use a NoOp callback for protected methods.
-	 * 
-	 * @see <a href="http://comments.gmane.org/gmane.comp.java.cglib.devel/720">Discussion about
-	 * this very issue in Spring AOP</a>
-	 * @see <a href="https://github.com/wicketstuff/core/wiki/SpringReference">The WicketStuff
-	 * SpringReference project which worked around this issue</a>
-	 */
-	private static class NoOpForProtectedMethodsCGLibCallbackFilter implements CallbackFilter
-	{
-		private static final CallbackFilter INSTANCE = new NoOpForProtectedMethodsCGLibCallbackFilter();
-
-		@Override
-		public int accept(Method method) {
-			if (Modifier.isProtected(method.getModifiers()))
-			{
-				return CGLIB_CALLBACK_NO_OVERRIDE;
-			}
-			else
-			{
-				return CGLIB_CALLBACK_HANDLER;
-			}
-		}
-	}
-
-	/**
-	 * Invocation handler for proxies representing interface based object. For interface backed
-	 * objects dynamic jdk proxies are used.
-	 * 
-	 * @author Igor Vaynberg (ivaynberg)
-	 * 
-	 */
-	private static class JdkHandler
-		implements
-			InvocationHandler,
-			ILazyInitProxy,
-			Serializable,
-			IWriteReplace
-	{
-		private static final long serialVersionUID = 1L;
-
-		private final IProxyTargetLocator locator;
-
-		private final String typeName;
-
-		private transient Object target;
-
-		/**
-		 * Constructor
-		 * 
-		 * @param type
-		 *            class of object this handler will represent
-		 * 
-		 * @param locator
-		 *            object locator used to locate the object this proxy represents
-		 */
-		public JdkHandler(final Class<?> type, final IProxyTargetLocator locator)
-		{
-			super();
-			this.locator = locator;
-			typeName = type.getName();
-		}
-
-		/**
-		 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
-		 *      java.lang.reflect.Method, java.lang.Object[])
-		 */
-		@Override
-		public Object invoke(final Object proxy, final Method method, final Object[] args)
-			throws Throwable
-		{
-			if (isFinalizeMethod(method))
-			{
-				// swallow finalize call
-				return null;
-			}
-			else if (isEqualsMethod(method))
-			{
-				return (equals(args[0])) ? Boolean.TRUE : Boolean.FALSE;
-			}
-			else if (isHashCodeMethod(method))
-			{
-				return hashCode();
-			}
-			else if (isToStringMethod(method))
-			{
-				return toString();
-			}
-			else if (method.getDeclaringClass().equals(ILazyInitProxy.class))
-			{
-				return getObjectLocator();
-			}
-			else if (isWriteReplaceMethod(method))
-			{
-				return writeReplace();
-			}
-
-			if (target == null)
-			{
-
-				target = locator.locateProxyTarget();
-			}
-			try
-			{
-				method.setAccessible(true);
-				return method.invoke(target, args);
-			}
-			catch (InvocationTargetException e)
-			{
-				throw e.getTargetException();
-			}
-		}
-
-		/**
-		 * @see org.apache.wicket.proxy.ILazyInitProxy#getObjectLocator()
-		 */
-		@Override
-		public IProxyTargetLocator getObjectLocator()
-		{
-			return locator;
-		}
-
-		/**
-		 * @see org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace#writeReplace()
-		 */
-		@Override
-		public Object writeReplace() throws ObjectStreamException
-		{
-			return new ProxyReplacement(typeName, locator);
-		}
+		public static final NoOp INSTANCE = new SerializableNoOpCallback();
 	}
-
-	/**
-	 * Checks if the method is derived from Object.equals()
-	 * 
-	 * @param method
-	 *            method being tested
-	 * @return true if the method is derived from Object.equals(), false otherwise
-	 */
-	public static boolean isEqualsMethod(final Method method)
-	{
-		return (method.getReturnType() == boolean.class) &&
-			(method.getParameterTypes().length == 1) &&
-			(method.getParameterTypes()[0] == Object.class) && method.getName().equals("equals");
-	}
-
-	/**
-	 * Checks if the method is derived from Object.hashCode()
-	 * 
-	 * @param method
-	 *            method being tested
-	 * @return true if the method is defined from Object.hashCode(), false otherwise
-	 */
-	public static boolean isHashCodeMethod(final Method method)
-	{
-		return (method.getReturnType() == int.class) && (method.getParameterTypes().length == 0) &&
-			method.getName().equals("hashCode");
-	}
-
-	/**
-	 * Checks if the method is derived from Object.toString()
-	 * 
-	 * @param method
-	 *            method being tested
-	 * @return true if the method is defined from Object.toString(), false otherwise
-	 */
-	public static boolean isToStringMethod(final Method method)
-	{
-		return (method.getReturnType() == String.class) &&
-			(method.getParameterTypes().length == 0) && method.getName().equals("toString");
-	}
-
-	/**
-	 * Checks if the method is derived from Object.finalize()
-	 * 
-	 * @param method
-	 *            method being tested
-	 * @return true if the method is defined from Object.finalize(), false otherwise
-	 */
-	public static boolean isFinalizeMethod(final Method method)
-	{
-		return (method.getReturnType() == void.class) && (method.getParameterTypes().length == 0) &&
-			method.getName().equals("finalize");
-	}
-
-	/**
-	 * Checks if the method is the writeReplace method
-	 * 
-	 * @param method
-	 *            method being tested
-	 * @return true if the method is the writeReplace method, false otherwise
-	 */
-	public static boolean isWriteReplaceMethod(final Method method)
-	{
-		return (method.getReturnType() == Object.class) &&
-			(method.getParameterTypes().length == 0) && method.getName().equals("writeReplace");
-	}
-
-	public static final class WicketNamingPolicy extends DefaultNamingPolicy
+	
+	public static class WicketNamingPolicy extends DefaultNamingPolicy
 	{
 		public static final WicketNamingPolicy INSTANCE = new WicketNamingPolicy();
 
@@ -632,25 +439,4 @@ public class LazyInitProxyFactory
 		}
 	}
 
-
-	private static boolean hasNoArgConstructor(Class<?> type)
-	{
-		for (Constructor<?> constructor : type.getDeclaredConstructors())
-		{
-			if (constructor.getParameterTypes().length == 0)
-				return true;
-		}
-
-		return false;
-	}
-
-	private static boolean isObjenesisAvailable()
-	{
-		try {
-			Class.forName("org.objenesis.ObjenesisStd");
-			return true;
-		} catch (Exception ignored) {
-			return false;
-		}
-	}
 }
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ByteBuddyProxyFactory.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ByteBuddyProxyFactory.java
new file mode 100644
index 0000000..3274d01
--- /dev/null
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ByteBuddyProxyFactory.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.wicket.proxy.bytebuddy;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.function.Function;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.proxy.ILazyInitProxy;
+import org.apache.wicket.proxy.IProxyFactory;
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace;
+import org.apache.wicket.proxy.LazyInitProxyFactory.ProxyReplacement;
+
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.NamingStrategy;
+import net.bytebuddy.TypeCache;
+import net.bytebuddy.description.modifier.Visibility;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
+import net.bytebuddy.implementation.FieldAccessor;
+import net.bytebuddy.implementation.MethodDelegation;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.Pipe;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.matcher.ElementMatchers;
+
+/**
+ * A factory class that creates bytebuddy proxies.
+ */
+public class ByteBuddyProxyFactory implements IProxyFactory
+{
+	/**
+	 * A cache used to store the dynamically generated classes by ByteBuddy.
+	 * Without this cache a new class will be generated for each proxy creation
+	 * and this will fill up the metaspace
+	 */
+	private static final TypeCache<TypeCache.SimpleKey> DYNAMIC_CLASS_CACHE = new TypeCache.WithInlineExpunction<>(TypeCache.Sort.SOFT);
+
+	private static final ByteBuddy BYTE_BUDDY = new ByteBuddy().with(WicketNamingStrategy.INSTANCE);
+
+	private static final boolean IS_OBJENESIS_AVAILABLE = isObjenesisAvailable();
+
+	/**
+	 * Create a lazy init proxy for the specified type. The target object will be located using the
+	 * provided locator upon first method invocation.
+	 * 
+	 * @param type
+	 *            type that proxy will represent
+	 * 
+	 * @param locator
+	 *            object locator that will locate the object the proxy represents
+	 * 
+	 * @return lazily initializable proxy
+	 */
+	@Override
+	public Object createProxy(final Class<?> type, final IProxyTargetLocator locator)
+	{
+		if (IS_OBJENESIS_AVAILABLE && !hasNoArgConstructor(type))
+		{
+			return ObjenesisProxyFactory.createProxy(type, locator);
+		}
+		else
+		{
+			Class<?> proxyClass = createOrGetProxyClass(type);
+
+			try
+			{
+				Object instance = proxyClass.getDeclaredConstructor().newInstance();
+				ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor(type, locator);
+				((InterceptorMutator) instance).setInterceptor(interceptor);
+				return instance;
+			}
+			catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e)
+			{
+				throw new WicketRuntimeException(e);
+			}
+		}
+	}
+
+	public static Class<?> createOrGetProxyClass(Class<?> type)
+	{
+		ClassLoader classLoader = resolveClassLoader();
+		return DYNAMIC_CLASS_CACHE.findOrInsert(classLoader,
+				new TypeCache.SimpleKey(type),
+				() -> BYTE_BUDDY
+						.subclass(type)
+						.method(ElementMatchers.isPublic())
+							.intercept(
+								MethodDelegation
+									.withDefaultConfiguration()
+									.withBinders(Pipe.Binder.install(Function.class))
+									.toField("interceptor"))
+						.defineField("interceptor", ByteBuddyInterceptor.class, Visibility.PRIVATE)
+						.implement(InterceptorMutator.class).intercept(FieldAccessor.ofBeanProperty())
+						.implement(Serializable.class, IWriteReplace.class, ILazyInitProxy.class).intercept(MethodDelegation.toField("interceptor"))
+						.make()
+						.load(classLoader, ClassLoadingStrategy.Default.INJECTION)
+						.getLoaded());
+	}
+
+	private static ClassLoader resolveClassLoader()
+	{
+		ClassLoader classLoader = null;
+		if (Application.exists())
+		{
+			classLoader = Application.get().getApplicationSettings()
+					.getClassResolver().getClassLoader();
+		}
+
+		if (classLoader == null) {
+			classLoader = Thread.currentThread().getContextClassLoader();
+		}
+
+		return classLoader;
+	}
+
+	/**
+	 * An interface used to set the Byte Buddy interceptor after creating an
+	 * instance of the dynamically created proxy class.
+	 * We need to set the interceptor as a field in the proxy class so that
+	 * we could use different interceptors for proxied classes with generics.
+	 * For example: a {@link org.apache.wicket.Component} may need to inject
+	 * two beans with the same raw type but different generic type(s) (<em>
+	 * ArrayList&lt;String&gt;</em> and <em>ArrayList&lt;Integer&gt;</em>).
+	 * Since the generic types are erased at runtime, and we use caching for the
+	 * dynamic proxy classes we need to be able to set different interceptors
+	 * after instantiating the proxy class.
+	 */
+	public interface InterceptorMutator
+	{
+		void setInterceptor(ByteBuddyInterceptor interceptor);
+	}
+
+	/**
+	 * Method interceptor for proxies representing concrete object not backed by an interface.
+	 * These proxies are represented by ByteBuddy proxies.
+	 * 
+	 * @author Igor Vaynberg (ivaynberg)
+	 */
+	public static class ByteBuddyInterceptor
+		implements
+			ILazyInitProxy,
+			Serializable,
+			IWriteReplace
+	{
+		private static final long serialVersionUID = 1L;
+
+		protected final IProxyTargetLocator locator;
+
+		protected final String typeName;
+
+		private transient Object target;
+
+		/**
+		 * Constructor
+		 * 
+		 * @param type
+		 *            class of the object this proxy was created for
+		 * 
+		 * @param locator
+		 *            object locator used to locate the object this proxy represents
+		 */
+		public ByteBuddyInterceptor(final Class<?> type, final IProxyTargetLocator locator)
+		{
+			super();
+			
+			this.typeName = type.getName();
+			this.locator = locator;
+		}
+
+		@RuntimeType
+		public Object intercept(@Origin Method method, @AllArguments Object[] args, @Pipe Function pipe) throws Exception
+		{
+			if (LazyInitProxyFactory.isFinalizeMethod(method))
+			{
+				// swallow finalize call
+				return null;
+			}
+			else if (LazyInitProxyFactory.isEqualsMethod(method))
+			{
+				return (equals(args[0])) ? Boolean.TRUE : Boolean.FALSE;
+			}
+			else if (LazyInitProxyFactory.isHashCodeMethod(method))
+			{
+				return hashCode();
+			}
+			else if (LazyInitProxyFactory.isToStringMethod(method))
+			{
+				return toString();
+			}
+			
+			if (target == null)
+			{
+				target = locator.locateProxyTarget();
+			}
+			
+			return pipe.apply(target);
+		}
+
+		@Override
+		public IProxyTargetLocator getObjectLocator()
+		{
+			return locator;
+		}
+
+		@Override
+		public Object writeReplace() throws ObjectStreamException
+		{
+			return new ProxyReplacement(typeName, locator);
+		}
+	}
+
+	/**
+	 * A strategy that decides what should be the fully qualified name of the generated
+	 * classes. Since it is not possible to create new classes in the <em>java.**</em>
+	 * package we modify the package name by prefixing it with <em>bytebuddy_generated_wicket_proxy.</em>.
+	 * For classes in any other packages we modify just the class name by prefixing
+	 * it with <em>WicketProxy_</em>. This way the generated proxy class could still
+	 * access package-private members of sibling classes.
+	 */
+	public static final class WicketNamingStrategy extends NamingStrategy.AbstractBase
+	{
+		public static final WicketNamingStrategy INSTANCE = new WicketNamingStrategy();
+
+		private WicketNamingStrategy()
+		{
+			super();
+		}
+
+		@Override
+		protected String name(TypeDescription superClass) {
+			String prefix = superClass.getName();
+			int lastIdxOfDot = prefix.lastIndexOf('.');
+			String packageName = prefix.substring(0, lastIdxOfDot);
+			String className = prefix.substring(lastIdxOfDot + 1);
+			String name = packageName + ".";
+			if (prefix.startsWith("java."))
+			{
+				name = "bytebuddy_generated_wicket_proxy." + name + className;
+			}
+			else
+			{
+				name += "WicketProxy_" + className;
+			}
+			return name;
+		}
+
+		@Override
+		public String redefine(TypeDescription typeDescription) {
+			return typeDescription.getName();
+		}
+
+		@Override
+		public String rebase(TypeDescription typeDescription) {
+			return typeDescription.getName();
+		}
+	}
+
+
+	private static boolean hasNoArgConstructor(Class<?> type)
+	{
+		for (Constructor<?> constructor : type.getDeclaredConstructors())
+		{
+			if (constructor.getParameterTypes().length == 0)
+				return true;
+		}
+
+		return false;
+	}
+
+	private static boolean isObjenesisAvailable()
+	{
+		try {
+			Class.forName("org.objenesis.ObjenesisStd");
+			return true;
+		} catch (Exception ignored) {
+			return false;
+		}
+	}
+}
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisByteBuddyInterceptor.java
similarity index 76%
copy from wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
copy to wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisByteBuddyInterceptor.java
index 63975a0..bac1b64 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisByteBuddyInterceptor.java
@@ -14,20 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.wicket.proxy.objenesis;
+package org.apache.wicket.proxy.bytebuddy;
 
 import java.io.ObjectStreamException;
 
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
 
 /**
- * Method interceptor for proxies representing concrete object not backed by an interface. These
- * proxies are representing by cglib proxies.
+ * Method interceptor for proxies representing concrete object not backed by an interface.
+ * These proxies are representing by ByteBuddy proxies.
  */
-public class ObjenesisCGLibInterceptor extends LazyInitProxyFactory.AbstractCGLibInterceptor
+public class ObjenesisByteBuddyInterceptor extends ByteBuddyProxyFactory.ByteBuddyInterceptor
 {
-	public ObjenesisCGLibInterceptor(Class<?> type, IProxyTargetLocator locator) {
+	public ObjenesisByteBuddyInterceptor(Class<?> type, IProxyTargetLocator locator) {
 		super(type, locator);
 	}
 
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.java
similarity index 58%
copy from wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
copy to wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.java
index 63975a0..45cf623 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyFactory.java
@@ -14,26 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.wicket.proxy.objenesis;
-
-import java.io.ObjectStreamException;
+package org.apache.wicket.proxy.bytebuddy;
 
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.objenesis.ObjenesisStd;
 
-/**
- * Method interceptor for proxies representing concrete object not backed by an interface. These
- * proxies are representing by cglib proxies.
- */
-public class ObjenesisCGLibInterceptor extends LazyInitProxyFactory.AbstractCGLibInterceptor
+public class ObjenesisProxyFactory
 {
-	public ObjenesisCGLibInterceptor(Class<?> type, IProxyTargetLocator locator) {
-		super(type, locator);
-	}
+	private static final ObjenesisStd OBJENESIS = new ObjenesisStd(false);
 
-	@Override
-	public Object writeReplace() throws ObjectStreamException
+	public static Object createProxy(final Class<?> type, final IProxyTargetLocator locator)
 	{
-		return new ObjenesisProxyReplacement(typeName, locator);
+		Class<?> proxyClass = ByteBuddyProxyFactory.createOrGetProxyClass(type);
+		Object instance = OBJENESIS.newInstance(proxyClass);
+		ObjenesisByteBuddyInterceptor interceptor = new ObjenesisByteBuddyInterceptor(type, locator);
+		((ByteBuddyProxyFactory.InterceptorMutator) instance).setInterceptor(interceptor);
+		return instance;
 	}
 }
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyReplacement.java
similarity index 90%
copy from wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
copy to wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyReplacement.java
index cbffee8..4430182 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/bytebuddy/ObjenesisProxyReplacement.java
@@ -14,14 +14,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.wicket.proxy.objenesis;
+package org.apache.wicket.proxy.bytebuddy;
 
 import java.io.ObjectStreamException;
 
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.core.util.lang.WicketObjects;
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
 import org.apache.wicket.util.io.IClusterable;
 
 /**
@@ -50,6 +49,6 @@ class ObjenesisProxyReplacement implements IClusterable
 							"] with the currently configured org.apache.wicket.application.IClassResolver");
 			throw new WicketRuntimeException(cause);
 		}
-		return ObjenesisProxyFactory.createProxy(clazz, locator, LazyInitProxyFactory.WicketNamingPolicy.INSTANCE);
+		return ObjenesisProxyFactory.createProxy(clazz, locator);
 	}
 }
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/cglib/CglibProxyFactory.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/cglib/CglibProxyFactory.java
new file mode 100644
index 0000000..2cc8be7
--- /dev/null
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/cglib/CglibProxyFactory.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.wicket.proxy.cglib;
+
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.proxy.ILazyInitProxy;
+import org.apache.wicket.proxy.IProxyFactory;
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.apache.wicket.proxy.LazyInitProxyFactory.CGLibInterceptor;
+import org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace;
+import org.apache.wicket.proxy.LazyInitProxyFactory.SerializableNoOpCallback;
+import org.apache.wicket.proxy.LazyInitProxyFactory.WicketNamingPolicy;
+import org.apache.wicket.proxy.objenesis.ObjenesisProxyFactory;
+
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.CallbackFilter;
+import net.sf.cglib.proxy.Enhancer;
+
+/**
+ * A factory class that creates cglib proxies.
+ */
+public class CglibProxyFactory implements IProxyFactory
+{
+	private static final int CGLIB_CALLBACK_NO_OVERRIDE = 0;
+	private static final int CGLIB_CALLBACK_HANDLER = 1;
+
+	private static final boolean IS_OBJENESIS_AVAILABLE = isObjenesisAvailable();
+
+	/**
+	 * Create a lazy init proxy for the specified type. The target object will be located using the
+	 * provided locator upon first method invocation.
+	 * 
+	 * @param type
+	 *            type that proxy will represent
+	 * 
+	 * @param locator
+	 *            object locator that will locate the object the proxy represents
+	 * 
+	 * @return lazily initializable proxy
+	 */
+	@Override
+	public Object createProxy(final Class<?> type, final IProxyTargetLocator locator)
+	{
+		if (IS_OBJENESIS_AVAILABLE && !hasNoArgConstructor(type))
+		{
+			return ObjenesisProxyFactory.createProxy(type, locator, WicketNamingPolicy.INSTANCE);
+		}
+		else
+		{
+			CGLibInterceptor handler = new CGLibInterceptor(type, locator);
+
+			Callback[] callbacks = new Callback[2];
+			callbacks[CGLIB_CALLBACK_NO_OVERRIDE] = SerializableNoOpCallback.INSTANCE;
+			callbacks[CGLIB_CALLBACK_HANDLER] = handler;
+
+			Enhancer e = new Enhancer();
+			e.setClassLoader(resolveClassLoader());
+			e.setInterfaces(new Class[] { Serializable.class, ILazyInitProxy.class,
+					IWriteReplace.class });
+			e.setSuperclass(type);
+			e.setCallbackFilter(NoOpForProtectedMethodsCGLibCallbackFilter.INSTANCE);
+			e.setCallbacks(callbacks);
+			e.setNamingPolicy(WicketNamingPolicy.INSTANCE);
+
+			return e.create();
+		}
+	}
+
+	private static ClassLoader resolveClassLoader()
+	{
+		ClassLoader classLoader = null;
+		if (Application.exists())
+		{
+			classLoader = Application.get().getApplicationSettings()
+					.getClassResolver().getClassLoader();
+		}
+
+		if (classLoader == null) {
+			classLoader = Thread.currentThread().getContextClassLoader();
+		}
+
+		return classLoader;
+	}
+
+	/**
+	 * CGLib callback filter which does not intercept protected methods.
+	 * 
+	 * Protected methods need to be called with invokeSuper() instead of invoke().
+	 * When invoke() is called on a protected method, it throws an "IllegalArgumentException:
+	 * Protected method" exception.
+	 * That being said, we do not need to intercept the protected methods so this callback filter
+	 * is designed to use a NoOp callback for protected methods.
+	 * 
+	 * @see <a href="http://comments.gmane.org/gmane.comp.java.cglib.devel/720">Discussion about
+	 * this very issue in Spring AOP</a>
+	 * @see <a href="https://github.com/wicketstuff/core/wiki/SpringReference">The WicketStuff
+	 * SpringReference project which worked around this issue</a>
+	 */
+	private static class NoOpForProtectedMethodsCGLibCallbackFilter implements CallbackFilter
+	{
+		private static final CallbackFilter INSTANCE = new NoOpForProtectedMethodsCGLibCallbackFilter();
+
+		@Override
+		public int accept(Method method) {
+			if (Modifier.isProtected(method.getModifiers()))
+			{
+				return CGLIB_CALLBACK_NO_OVERRIDE;
+			}
+			else
+			{
+				return CGLIB_CALLBACK_HANDLER;
+			}
+		}
+	}
+
+	private static boolean hasNoArgConstructor(Class<?> type)
+	{
+		for (Constructor<?> constructor : type.getDeclaredConstructors())
+		{
+			if (constructor.getParameterTypes().length == 0)
+				return true;
+		}
+
+		return false;
+	}
+
+	private static boolean isObjenesisAvailable()
+	{
+		try {
+			Class.forName("org.objenesis.ObjenesisStd");
+			return true;
+		} catch (Exception ignored) {
+			return false;
+		}
+	}
+}
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/jdk/JdkProxyFactory.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/jdk/JdkProxyFactory.java
new file mode 100644
index 0000000..66ecda0
--- /dev/null
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/jdk/JdkProxyFactory.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.wicket.proxy.jdk;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.proxy.ILazyInitProxy;
+import org.apache.wicket.proxy.IProxyTargetLocator;
+import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace;
+import org.apache.wicket.proxy.LazyInitProxyFactory.ProxyReplacement;
+
+/**
+ * A factory class that creates jdk dynamic proxies.
+ */
+public class JdkProxyFactory
+{
+	/**
+	 * Create a lazy init proxy for the specified type. The target object will be located using the
+	 * provided locator upon first method invocation.
+	 * 
+	 * @param type
+	 *            type that proxy will represent
+	 * 
+	 * @param locator
+	 *            object locator that will locate the object the proxy represents
+	 * 
+	 * @return lazily initializable proxy
+	 */
+	public Object createProxy(final Class<?> type, final IProxyTargetLocator locator)
+	{
+		JdkHandler handler = new JdkHandler(type, locator);
+
+		try
+		{
+			return Proxy.newProxyInstance(resolveClassLoader(),
+				new Class[] { type, Serializable.class, ILazyInitProxy.class,
+						IWriteReplace.class }, handler);
+		}
+		catch (IllegalArgumentException e)
+		{
+			/*
+			 * STW: In some clustering environments it appears the context classloader fails to
+			 * load the proxied interface (currently seen in BEA WLS 9.x clusters). If this
+			 * happens, we can try and fall back to the classloader (current) that actually
+			 * loaded this class.
+			 */
+			return Proxy.newProxyInstance(LazyInitProxyFactory.class.getClassLoader(),
+				new Class[] { type, Serializable.class, ILazyInitProxy.class,
+						IWriteReplace.class }, handler);
+		}
+	}
+	
+	private ClassLoader resolveClassLoader()
+	{
+		ClassLoader classLoader = null;
+		if (Application.exists())
+		{
+			classLoader = Application.get().getApplicationSettings()
+					.getClassResolver().getClassLoader();
+		}
+
+		if (classLoader == null) {
+			classLoader = Thread.currentThread().getContextClassLoader();
+		}
+
+		return classLoader;
+	}
+
+	/**
+	 * Invocation handler for proxies representing interface based object. For interface backed
+	 * objects dynamic jdk proxies are used.
+	 * 
+	 * @author Igor Vaynberg (ivaynberg)
+	 * 
+	 */
+	private static class JdkHandler
+		implements
+			InvocationHandler,
+			ILazyInitProxy,
+			Serializable,
+			IWriteReplace
+	{
+		private static final long serialVersionUID = 1L;
+
+		private final IProxyTargetLocator locator;
+
+		private final String typeName;
+
+		private transient Object target;
+
+		/**
+		 * Constructor
+		 * 
+		 * @param type
+		 *            class of object this handler will represent
+		 * 
+		 * @param locator
+		 *            object locator used to locate the object this proxy represents
+		 */
+		public JdkHandler(final Class<?> type, final IProxyTargetLocator locator)
+		{
+			super();
+			this.locator = locator;
+			typeName = type.getName();
+		}
+
+		/**
+		 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
+		 *      java.lang.reflect.Method, java.lang.Object[])
+		 */
+		@Override
+		public Object invoke(final Object proxy, final Method method, final Object[] args)
+			throws Throwable
+		{
+			if (LazyInitProxyFactory.isFinalizeMethod(method))
+			{
+				// swallow finalize call
+				return null;
+			}
+			else if (LazyInitProxyFactory.isEqualsMethod(method))
+			{
+				return (equals(args[0])) ? Boolean.TRUE : Boolean.FALSE;
+			}
+			else if (LazyInitProxyFactory.isHashCodeMethod(method))
+			{
+				return hashCode();
+			}
+			else if (LazyInitProxyFactory.isToStringMethod(method))
+			{
+				return toString();
+			}
+			else if (method.getDeclaringClass().equals(ILazyInitProxy.class))
+			{
+				return getObjectLocator();
+			}
+			else if (LazyInitProxyFactory.isWriteReplaceMethod(method))
+			{
+				return writeReplace();
+			}
+
+			if (target == null)
+			{
+
+				target = locator.locateProxyTarget();
+			}
+			try
+			{
+				method.setAccessible(true);
+				return method.invoke(target, args);
+			}
+			catch (InvocationTargetException e)
+			{
+				throw e.getTargetException();
+			}
+		}
+
+		/**
+		 * @see org.apache.wicket.proxy.ILazyInitProxy#getObjectLocator()
+		 */
+		@Override
+		public IProxyTargetLocator getObjectLocator()
+		{
+			return locator;
+		}
+
+		/**
+		 * @see org.apache.wicket.proxy.LazyInitProxyFactory.IWriteReplace#writeReplace()
+		 */
+		@Override
+		public Object writeReplace() throws ObjectStreamException
+		{
+			return new ProxyReplacement(typeName, locator);
+		}
+	}
+}
\ No newline at end of file
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
index 63975a0..d21e011 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisCGLibInterceptor.java
@@ -19,13 +19,13 @@ package org.apache.wicket.proxy.objenesis;
 import java.io.ObjectStreamException;
 
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.apache.wicket.proxy.LazyInitProxyFactory.AbstractCGLibInterceptor;
 
 /**
  * Method interceptor for proxies representing concrete object not backed by an interface. These
  * proxies are representing by cglib proxies.
  */
-public class ObjenesisCGLibInterceptor extends LazyInitProxyFactory.AbstractCGLibInterceptor
+public class ObjenesisCGLibInterceptor extends AbstractCGLibInterceptor
 {
 	public ObjenesisCGLibInterceptor(Class<?> type, IProxyTargetLocator locator) {
 		super(type, locator);
diff --git a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java b/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
index cbffee8..fc8f22d 100644
--- a/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
+++ b/wicket-ioc/src/main/java/org/apache/wicket/proxy/objenesis/ObjenesisProxyReplacement.java
@@ -21,7 +21,7 @@ import java.io.ObjectStreamException;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.core.util.lang.WicketObjects;
 import org.apache.wicket.proxy.IProxyTargetLocator;
-import org.apache.wicket.proxy.LazyInitProxyFactory;
+import org.apache.wicket.proxy.LazyInitProxyFactory.WicketNamingPolicy;
 import org.apache.wicket.util.io.IClusterable;
 
 /**
@@ -50,6 +50,6 @@ class ObjenesisProxyReplacement implements IClusterable
 							"] with the currently configured org.apache.wicket.application.IClassResolver");
 			throw new WicketRuntimeException(cause);
 		}
-		return ObjenesisProxyFactory.createProxy(clazz, locator, LazyInitProxyFactory.WicketNamingPolicy.INSTANCE);
+		return ObjenesisProxyFactory.createProxy(clazz, locator, WicketNamingPolicy.INSTANCE);
 	}
 }
diff --git a/wicket-ioc/src/test/java/org/apache/wicket/injection/util/MockDependency.java b/wicket-ioc/src/test/java/org/apache/wicket/injection/util/MockDependency.java
index cc22135..6afeecc 100644
--- a/wicket-ioc/src/test/java/org/apache/wicket/injection/util/MockDependency.java
+++ b/wicket-ioc/src/test/java/org/apache/wicket/injection/util/MockDependency.java
@@ -16,6 +16,8 @@
  */
 package org.apache.wicket.injection.util;
 
+import org.apache.wicket.proxy.LazyInitProxyFactory;
+
 /**
  * Mock dependency that does not implement an interface
  * 
@@ -27,7 +29,8 @@ public class MockDependency
 	private String message;
 
 	/**
-	 * Empty default constructor. It is required by cglib to create a proxy.
+	 * Empty default constructor. It is required by {@link LazyInitProxyFactory}
+	 * to create a proxy.
 	 */
 	public MockDependency()
 	{
diff --git a/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/SpringBeanWithGenericsTest.java b/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/SpringBeanWithGenericsTest.java
index 653a93c..ac743c1 100644
--- a/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/SpringBeanWithGenericsTest.java
+++ b/wicket-spring/src/test/java/org/apache/wicket/spring/injection/annot/SpringBeanWithGenericsTest.java
@@ -290,7 +290,7 @@ class SpringBeanWithGenericsTest
 		@Bean
 		public List<String> stringsList()
 		{
-			return Arrays.asList("foo", "bar", "baz");
+			return List.of("foo", "bar", "baz");
 		}
 
 		@Bean