From 89b365120ad577bd5ad8007044991a2b6742304b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 26 Sep 2012 19:30:42 +0200 Subject: [PATCH] TransactionInterceptor avoids reflective method search for method identification As of Spring 3.0.4, we were trying to expose the target method signature as transaction name. Unfortunately, the algorithm called the ClassUtils.getMostSpecificMethod helper method which performs a quite expensive reflective search. As of this commit, we're simply concatenating the target class name with the method name, accepting the use of the concrete target class (which is arguably more meaningful for monitoring anyway) even when the method implementation actually sits on a base class. Issue: SPR-9802 --- .../interceptor/TransactionAspectSupport.java | 18 ++++++++--------- .../TransactionAttributeSource.java | 20 ++++++++++--------- .../BeanFactoryTransactionTests.java | 10 ++++------ 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java index 049ca612f4..995c4bcbd5 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java @@ -33,7 +33,6 @@ import org.springframework.transaction.NoTransactionException; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionSystemException; -import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -260,11 +259,11 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init /** * Create a transaction if necessary, based on the given method and class. *

Performs a default TransactionAttribute lookup for the given method. - * @param method method about to execute - * @param targetClass class the method is on + * @param method the method about to execute + * @param targetClass the class that the method is being invoked on * @return a TransactionInfo object, whether or not a transaction was created. - * The hasTransaction() method on TransactionInfo can be used to tell if there - * was a transaction created. + * The hasTransaction() method on TransactionInfo can be used to + * tell if there was a transaction created. * @see #getTransactionAttributeSource() */ protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) { @@ -279,8 +278,8 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init * for use in logging. Can be overridden in subclasses to provide a * different identifier for the given method. * @param method the method we're interested in - * @param targetClass class the method is on - * @return log message identifying this method + * @param targetClass the class that the method is being invoked on + * @return a String representation identifying this method * @see org.springframework.util.ClassUtils#getQualifiedMethodName */ protected String methodIdentification(Method method, Class targetClass) { @@ -288,8 +287,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init if (simpleMethodId != null) { return simpleMethodId; } - Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); - return ClassUtils.getQualifiedMethodName(specificMethod); + return (targetClass != null ? targetClass : method.getDeclaringClass()).getName() + "." + method.getName(); } /** @@ -297,7 +295,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init * for use in logging. Can be overridden in subclasses to provide a * different identifier for the given method. * @param method the method we're interested in - * @return log message identifying this method + * @return a String representation identifying this method * @deprecated in favor of {@link #methodIdentification(Method, Class)} */ @Deprecated diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java index 14df0875b5..2073b47d6a 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -19,23 +19,25 @@ package org.springframework.transaction.interceptor; import java.lang.reflect.Method; /** - * Interface used by TransactionInterceptor. Implementations know - * how to source transaction attributes, whether from configuration, - * metadata attributes at source level, or anywhere else. + * Strategy interface used by {@link TransactionInterceptor} for metadata retrieval. + * + *

Implementations know how to source transaction attributes, whether from configuration, + * metadata attributes at source level (such as Java 5 annotations), or anywhere else. * * @author Rod Johnson * @since 15.04.2003 * @see TransactionInterceptor#setTransactionAttributeSource * @see TransactionProxyFactoryBean#setTransactionAttributeSource + * @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource */ public interface TransactionAttributeSource { /** - * Return the transaction attribute for this method. - * Return null if the method is non-transactional. - * @param method method - * @param targetClass target class. May be null, in which - * case the declaring class of the method must be used. + * Return the transaction attribute for the given method, + * or null if the method is non-transactional. + * @param method the method to introspect + * @param targetClass the target class. May be null, + * in which case the declaring class of the method must be used. * @return TransactionAttribute the matching transaction attribute, * or null if none found */ diff --git a/spring-tx/src/test/java/org/springframework/transaction/interceptor/BeanFactoryTransactionTests.java b/spring-tx/src/test/java/org/springframework/transaction/interceptor/BeanFactoryTransactionTests.java index fb78991529..09388b1d81 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/interceptor/BeanFactoryTransactionTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/interceptor/BeanFactoryTransactionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2012 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. @@ -127,16 +127,14 @@ public class BeanFactoryTransactionTests extends TestCase { final TransactionStatus ts = (TransactionStatus) statusControl.getMock(); ptm = new PlatformTransactionManager() { private boolean invoked; - public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { + public TransactionStatus getTransaction(TransactionDefinition def) throws TransactionException { if (invoked) { throw new IllegalStateException("getTransaction should not get invoked more than once"); } invoked = true; - System.out.println(definition.getName()); - if (!((definition.getName().indexOf(TestBean.class.getName()) != -1) && - (definition.getName().indexOf("setAge") != -1))) { + if (!(def.getName().contains(DerivedTestBean.class.getName()) && def.getName().contains("setAge"))) { throw new IllegalStateException( - "transaction name should contain class and method name: " + definition.getName()); + "transaction name should contain class and method name: " + def.getName()); } return ts; }