While working on #28976, it became apparent that a BeanFactory initializer registered in BeanFactoryInitializationCode could use a more flexible signature. Rather than injecting the DefaultListableBeanFactory the contribution needs a ConfigurableEnvironment and a ResourceLoader. The former can be retrieved by the bean factory (although a bit odd). The latter is harder.

MethodReference provides a very nice abstraction and we've almost what we need but I'd like to use the opportunity to see if we can avoid adding yet another toCodeBlock method as the current ones are already a bit confusing IMO.

GeneratedMethod

It's often needed to get a MethodReference from a GeneratedMethod. It seems it would be relatively easy to pass a ClassName to GeneratedMethods so that the GenerateMethod it produces can return a MethodReference (toMethodReference or something like that).

Also a method reference that's built from a MethodSpec this way knows about its name, kind, and declaring class.

MethodReference getDeclaringClass and getMethodName

Both of these seem to be unused so they should probably be removed.

CodeBlock patterns

MethodReference provides several ways of invoking the method:

  • toCodeBlock(): is producing a method reference, expecting the current context to match the signature and the method to be defined in the current class (if not static).
  • toCodeBlock(String instanceVariable): is producing a method reference, expecting the current context to match the signature and the method to be defined by the class referred to the instanceVariable argument. (It looks like it's only used in tests or by toCodeBlock that's calling with a null instance variable).
  • toInvokeCodeBlock(CodeBlock... args): is producing a method invocation expected to match the arguments and the method to be defined in the current class (if not static)
  • toInvokeCodeBlock(String instanceVariable, CodeBlock... args): is producing a method invocation expected to match the arguments and the method to be defined by the class referred to the instanceVariable argument.

I wonder if a unique toCodeBlock that provides a context could let the MethodReference decide for itself how the method invocation should occur. The following information would be needed:

  • A resolver for arguments that could work at two potential levels:
  • Provide a CodeBlock for a given Class
  • Expected well-known variable name to be present
  • If the declaring class of the method is the current class. If not, the value of an instanceVariable.
  • Whether or not lambda references are possible (that last bit is still a bit blurry).

Comment From: snicoll

InstanceSupplierCodeGenerator also has a method that could become irrelevant to some extent:

private CodeBlock generateReturnStatement(GeneratedMethod getInstanceMethod) {
    return CodeBlock.of("$T.$L()", this.className, getInstanceMethod.getName());
}

Comment From: snicoll

toInvokeCodeBlockForInstance also creates a new instance of the declaring class which looks a very specific decision such a generic method would take.

Comment From: snicoll

Most of the of static factory method of MethodReference are only used in tests.

Comment From: snicoll

I've had a brainstorming session with @wilkinsona and @bclozel today. We've discussed various options and the pros and cons and we conclude that it is overkill to try to avoid both side having some sort of shared context. Trying to do that means too much context needs to be provided by the caller and MethodReference has to know too many things to take the right decision.

We came back with the original need of offering a more flexible signature and we'd like to explore how toInvokeCodeBlock could be changed to take something that resolves a limited number of arguments, rather than the current CodeBlock vararg.

Comment From: snicoll

I've made some good progress in https://github.com/snicoll/spring-framework/tree/gh-29005 - One advantage of knowing the ultimate location of the code is that we don't expand the class name for static internal calls. For instance, the following code:

beanDefinition.setInstanceSupplier(ApplicationAvailabilityAutoConfiguration__BeanDefinitions.getApplicationAvailabilityInstanceSupplier());

Is now:

beanDefinition.setInstanceSupplier(getApplicationAvailabilityInstanceSupplier());

However, knowing the ultimate target class isn't always possible so we might need to adapt some API. Perhaps methodReference itself could offer some sort of support for this.