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
This commit is contained in:
Juergen Hoeller
2012-09-26 19:31:05 +02:00
parent c81543e1a4
commit bbfc807b0c
3 changed files with 23 additions and 25 deletions

View File

@@ -33,7 +33,6 @@ import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.TransactionSystemException; import org.springframework.transaction.TransactionSystemException;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; 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. * Create a transaction if necessary, based on the given method and class.
* <p>Performs a default TransactionAttribute lookup for the given method. * <p>Performs a default TransactionAttribute lookup for the given method.
* @param method method about to execute * @param method the method about to execute
* @param targetClass class the method is on * @param targetClass the class that the method is being invoked on
* @return a TransactionInfo object, whether or not a transaction was created. * @return a TransactionInfo object, whether or not a transaction was created.
* The hasTransaction() method on TransactionInfo can be used to tell if there * The <code>hasTransaction()</code> method on TransactionInfo can be used to
* was a transaction created. * tell if there was a transaction created.
* @see #getTransactionAttributeSource() * @see #getTransactionAttributeSource()
*/ */
protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) { 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 * for use in logging. Can be overridden in subclasses to provide a
* different identifier for the given method. * different identifier for the given method.
* @param method the method we're interested in * @param method the method we're interested in
* @param targetClass class the method is on * @param targetClass the class that the method is being invoked on
* @return log message identifying this method * @return a String representation identifying this method
* @see org.springframework.util.ClassUtils#getQualifiedMethodName * @see org.springframework.util.ClassUtils#getQualifiedMethodName
*/ */
protected String methodIdentification(Method method, Class targetClass) { protected String methodIdentification(Method method, Class targetClass) {
@@ -288,8 +287,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
if (simpleMethodId != null) { if (simpleMethodId != null) {
return simpleMethodId; return simpleMethodId;
} }
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); return (targetClass != null ? targetClass : method.getDeclaringClass()).getName() + "." + method.getName();
return ClassUtils.getQualifiedMethodName(specificMethod);
} }
/** /**
@@ -297,7 +295,7 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
* for use in logging. Can be overridden in subclasses to provide a * for use in logging. Can be overridden in subclasses to provide a
* different identifier for the given method. * different identifier for the given method.
* @param method the method we're interested in * @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 in favor of {@link #methodIdentification(Method, Class)}
*/ */
@Deprecated @Deprecated

View File

@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import java.lang.reflect.Method;
/** /**
* Interface used by TransactionInterceptor. Implementations know * Strategy interface used by {@link TransactionInterceptor} for metadata retrieval.
* how to source transaction attributes, whether from configuration, *
* metadata attributes at source level, or anywhere else. * <p>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 * @author Rod Johnson
* @since 15.04.2003 * @since 15.04.2003
* @see TransactionInterceptor#setTransactionAttributeSource * @see TransactionInterceptor#setTransactionAttributeSource
* @see TransactionProxyFactoryBean#setTransactionAttributeSource * @see TransactionProxyFactoryBean#setTransactionAttributeSource
* @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
*/ */
public interface TransactionAttributeSource { public interface TransactionAttributeSource {
/** /**
* Return the transaction attribute for this method. * Return the transaction attribute for the given method,
* Return null if the method is non-transactional. * or <code>null</code> if the method is non-transactional.
* @param method method * @param method the method to introspect
* @param targetClass target class. May be <code>null</code>, in which * @param targetClass the target class. May be <code>null</code>,
* case the declaring class of the method must be used. * in which case the declaring class of the method must be used.
* @return TransactionAttribute the matching transaction attribute, * @return TransactionAttribute the matching transaction attribute,
* or <code>null</code> if none found * or <code>null</code> if none found
*/ */

View File

@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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(); final TransactionStatus ts = (TransactionStatus) statusControl.getMock();
ptm = new PlatformTransactionManager() { ptm = new PlatformTransactionManager() {
private boolean invoked; private boolean invoked;
public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { public TransactionStatus getTransaction(TransactionDefinition def) throws TransactionException {
if (invoked) { if (invoked) {
throw new IllegalStateException("getTransaction should not get invoked more than once"); throw new IllegalStateException("getTransaction should not get invoked more than once");
} }
invoked = true; invoked = true;
System.out.println(definition.getName()); if (!(def.getName().contains(DerivedTestBean.class.getName()) && def.getName().contains("setAge"))) {
if (!((definition.getName().indexOf(TestBean.class.getName()) != -1) &&
(definition.getName().indexOf("setAge") != -1))) {
throw new IllegalStateException( 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; return ts;
} }