From 07a691a57598594d07780ece296e68c1c1f0ff2b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 16 Feb 2009 01:35:35 +0000 Subject: [PATCH] bridge method resolution works with Hibernate-generated CGLIB proxies as well (SPR-5414) git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@640 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../core/BridgeMethodResolver.java | 35 +++++---- .../CglibProxyBridgeMethodTests.java | 73 +++++++++++++++++++ 2 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/CglibProxyBridgeMethodTests.java diff --git a/org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java index ea47b61b45e..4adf3acac6d 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 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. @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -41,13 +42,9 @@ import org.springframework.util.ReflectionUtils; *

See * The Java Language Specification for more details on the use of bridge methods. * - *

Only usable on JDK 1.5 and higher. Use an appropriate {@link JdkVersion} - * check before calling this class if a fallback for JDK 1.4 is desirable. - * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 - * @see JdkVersion */ public abstract class BridgeMethodResolver { @@ -62,7 +59,6 @@ public abstract class BridgeMethodResolver { if (bridgeMethod == null || !bridgeMethod.isBridge()) { return bridgeMethod; } - // Gather all methods with matching name and parameter size. List candidateMethods = new ArrayList(); Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass()); @@ -71,16 +67,12 @@ public abstract class BridgeMethodResolver { candidateMethods.add(candidateMethod); } } - - Method result; - // Now perform simple quick checks. + // Now perform simple quick check. if (candidateMethods.size() == 1) { - result = candidateMethods.get(0); + return candidateMethods.get(0); } - else { - result = searchCandidates(candidateMethods, bridgeMethod); - } - + // Search for candidate match. + Method result = searchCandidates(candidateMethods, bridgeMethod); if (result == null) { throw new IllegalStateException( "Unable to locate bridged method for bridge method '" + bridgeMethod + "'"); @@ -95,13 +87,23 @@ public abstract class BridgeMethodResolver { * @return the bridged method, or null if none found */ private static Method searchCandidates(List candidateMethods, Method bridgeMethod) { + if (candidateMethods.isEmpty()) { + return null; + } Map typeParameterMap = GenericTypeResolver.getTypeVariableMap(bridgeMethod.getDeclaringClass()); + Method previousMethod = null; + boolean sameSig = true; for (Method candidateMethod : candidateMethods) { if (isBridgeMethodFor(bridgeMethod, candidateMethod, typeParameterMap)) { return candidateMethod; } + else if (previousMethod != null) { + sameSig = sameSig && + Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes()); + } + previousMethod = candidateMethod; } - return null; + return (sameSig ? candidateMethods.get(0) : null); } /** @@ -185,7 +187,8 @@ public abstract class BridgeMethodResolver { } } // A non-array type: compare the type itself. - if (!candidateParameter.equals(GenericTypeResolver.resolveType(genericParameter, typeVariableMap))) { + Class resolvedParameter = GenericTypeResolver.resolveType(genericParameter, typeVariableMap); + if (!candidateParameter.equals(resolvedParameter)) { return false; } } diff --git a/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/CglibProxyBridgeMethodTests.java b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/CglibProxyBridgeMethodTests.java new file mode 100644 index 00000000000..ba55810ffed --- /dev/null +++ b/org.springframework.orm/src/test/java/org/springframework/orm/hibernate3/CglibProxyBridgeMethodTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2009 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. + * 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.orm.hibernate3; + +import java.io.Serializable; + +import org.hibernate.proxy.HibernateProxy; +import org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer; +import org.junit.Test; + +import org.springframework.beans.BeanUtils; + +/** + * Test for compatibility of Spring's BeanUtils and its BridgeMethodResolver use + * when operating on a Hibernate-generated CGLIB proxy class. + * + * @author Arnout Engelen + * @author Juergen Hoeller + */ +public class CglibProxyBridgeMethodTests { + + @Test + public void introspectHibernateProxyForGenericClass() { + BeanUtils.getPropertyDescriptor(CglibInstantieMedewerker.class, "organisatie"); + Class clazz = CGLIBLazyInitializer.getProxyFactory( + CglibInstantieMedewerker.class, new Class[] {HibernateProxy.class}); + BeanUtils.getPropertyDescriptor(clazz, "organisatie"); + } + + + public interface CglibIOrganisatie { + } + + + public class CglibOrganisatie implements CglibIOrganisatie { + } + + + public class CglibInstantie extends CglibOrganisatie { + } + + + public interface CglibIOrganisatieMedewerker { + + void setOrganisatie(T organisatie); + } + + + public class CglibOrganisatieMedewerker implements CglibIOrganisatieMedewerker { + + public void setOrganisatie(CglibOrganisatie organisatie) { + } + } + + + public class CglibInstantieMedewerker extends CglibOrganisatieMedewerker implements Serializable { + } + +}