Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@LookupMethod annotation for use with component scanning [SPR-5192] #9865

Closed
spring-projects-issues opened this issue Oct 1, 2008 · 20 comments
Assignees
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Oct 1, 2008

Casey Butterworth opened SPR-5192 and commented

I have recently started some work on an @LookupMethod annotation to be used in the following situation:

  • A prototype scoped bean needs to be used within a singleton
  • The singleton was created using component scanning

Currently the most obvious solution would be to forgo component scanning for the singleton and define the lookup-method in the ApplicationContext XML or using spring-java-config, e.g:

<!-- this could also be created using component scanning -->
<bean id="myPrototype" class="sample.MyPrototype" scope="prototype"/>

<bean id="mySingleton" class="sample.MySingleton">
  <lookup-method name="createMyPrototype" bean="myPrototype"/>
</bean>

However, since I've been using component scanning with 2.5, it doesn't feel right defining the wiring outside of the components, and it would be ideal if we could do something like the following:

@Component
public abstract class MySingleton {
   @LookupMethod(beanRef = "myPrototype")
   protected abstract MyPrototype createMyPrototype();
}

@Component
public class MyPrototype {}

It would be even better if @LookupMethod could work in conjunction with autowiring (by type), but that can be a subsequent feature request.

I've started to implement a solution and will attach shortly.


Attachments:

Issue Links:

Referenced from: commits eb0ab84

37 votes, 33 watchers

@spring-projects-issues
Copy link
Collaborator Author

Casey Butterworth commented

I am attaching 2 files:

  • LookupMethod.java - This is the actual annotation and pretty much speaks for itself.
  • LookupMethodAnnotationBeanPostProcessor.java - MergedBeanDefinitionPostProcessor that adds the LookupMethod to the RootBeanDefinition

Note that this solution does not work yet for reasons I will outline in an additional comment.

@spring-projects-issues
Copy link
Collaborator Author

Casey Butterworth commented

As you might expect, this change isn't quite this straightforward otherwise I assume it would already have happened, but hopefully the following can be resolved in a future spring version:

  • The ClassPathScanningCandidateComponentProvider rejects beans that are not concrete but this kind of Method Injection will only ever occur on abstract classes. For my testing I have simply removed this rule (see ClassPathScanningCandidateComponentProvider#isCandidateComponent) but I'm not sure what the overall implications of this might be. Perhaps this could also scan for the presence of @LookupMethod on all non-concrete methods in the class and allow the bean to be registered if that condition is satisfied?

  • More significantly, for reasons I can't yet understand, all registered MergedBeanDefinitionPostProcessors are called after the bean has been instantiated. This is obviously a problem for us as any attempt to instantiate the bean defintion prior to registering the LookupOverrides fails, as (a) the class is abstract and cannot be instantiated, and (b) the InstantiationStrategy is not yet aware that the bean will need to be proxied.

I can think of a few solutions to this:

  1. Find a way to access the RootBeanDefinition from the InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation method (I had a quick look but couldn't figure it out)
  2. Call the MergedBeanDefintionPostProcessors prior to instantiation
  3. Provide a new PostProcessor type (invoked around the same time as InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation) that provides the caller with access to the RootBeanDefinition (much like the existing MergedBeanDefintionPostProcessors).

Personally my preference is option 2, but i guess this will need some impact analysis from someone who understands the lifecycle in more depth than me. If anyone knows how to go about Option 1 I am very open to ideas!

Anyway, hopefully this provides enough information for someone to provide some feedback and ideas.

@spring-projects-issues
Copy link
Collaborator Author

Casey Butterworth commented

Sorry, accidentally attached the annotation twice (can an admin delete?)

In any event, we really only need the 0.3 kB version.

@spring-projects-issues
Copy link
Collaborator Author

Benjamin Lerman commented

Example project for LookupMathodBeanFactoryPostProcessors

@spring-projects-issues
Copy link
Collaborator Author

Benjamin Lerman commented

Hi,

I just attach an other try at this one.

I did not start from an AnnotationBeanPostProcessor but from an BeanFactoryPostProcessor because one need to be able to change the bean before it is instanciated.

I also tried to implements some of the Qualifier mechanism. It is far from complete, but as long a @Qualifier won't be usable on methods, I do not see how to do something more complete.

The other limitation is that I cannot do anything for abstract class, because those are not registered by ClassPathScanningCandidateComponentProvider. That means that bean that are proxied must still have a default implementation of the factory method.

@spring-projects-issues
Copy link
Collaborator Author

Cuneyt Tuna commented

Is there any progress on this issue?

@spring-projects-issues
Copy link
Collaborator Author

Martin Green commented

Is there any chance we could get this annotation - it would be extremely usefull and make the code much more self documenting.

@spring-projects-issues
Copy link
Collaborator Author

Marko Topolnik commented

I would just like to point out that the bean class does not need to be abstract -- in fact, I have never used it that way. This is the way I do it:

public class MySingleton {
  protected MyPrototype createMyPrototype() {
    throw new UnsupportedOperationException("Spring should override this lookup method");
  }
}

@spring-projects-issues
Copy link
Collaborator Author

Alexis Torres commented

Hello,

I was really interested in that option till I understood @ScopedProxy annotation.
The elegant work around is making the singleton autowire a ScopeProxy which has a prototype behavior.

Not saying that this shouldn't be implemented but maybe priority is not as important.

@spring-projects-issues
Copy link
Collaborator Author

Dmitry Katsubo commented

I would vote for @Autowired to work for such lookup methods naturally, so there is no need to introduce new annotation (@LookupMethod):

@Autowired
protected MyPrototype createMyPrototype() { ... }

@spring-projects-issues
Copy link
Collaborator Author

Alexey Fansky commented

So are there any solutions for this?

@spring-projects-issues
Copy link
Collaborator Author

Phil Webb commented

Generally the preferred solution with this type of problem is to use the javax.inject.Provider interface from JSR-330. It tends to result in clearer code and removes the need to throw the UnsupportedOperationException.

public class MySingleton {

  @Autowired
  private Provider<MyPrototype> myPrototype;

  public void operation() {
    MyPrototype instance = myPrototype.get();
    // do something with the instance
  }

}

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Aug 13, 2014

Juergen Hoeller commented

I'm considering this for 4.1 still, following up on the work in #15860 and #12089. It is actually quite straightforward to implement, even nicer with a by-type lookup option if no bean name has been specified... and can easily support arguments for factory methods or constructors as well.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Finally introduced as @Lookup annotation, matching by the method's return type or by a specified bean name (if any), and supporting arguments as well.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

mirak commented

for prototypes, why can't we just call the @Bean method from the @Configuration class ?

@Configuration
public class MyConfig implements HouseFactory {

@Override
@Bean
@Scope("prototype")
public House house(String name, int windows){
    return new House(name,windows);
}
}

public class Street{
   @Autowired
   private HouseFactory houseFactory;

    public void buildSomeHouses(){
        House house1 = houseFactory.house("blue house",5);
        House house2 = houseFactory.house("red house",9);
    }

} 

public interface HouseFactory {

     House house(String name, int windows);
}

Actually I tried this on spring 3.2.16 which I am stuck on until we migrate servlet versions, and the issue is that it fails because it tries to autowire a String bean and Integer bean, which it can't find of course.
But since @Configuration files are kind of factories, what we are doing actually is to implement HouseFactory, that will inject ApplicationContext and call applicationContext.getBean( ... ) wich feels like duplicating code.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

I'm afraid @Bean methods are not meant to be called as factory methods from outside. They are rather designed as a factory method callback mechanism within the container, with the container calling them when a particular bean needs to be created. BeanFactory.getBean is indeed the primary entry point from outside.

Since you mentioned it, why specifically do you feel stuck on Spring 3.2.x? Spring 4.x is still runtime-compatible with Servlet 2.5 containers, so does that mean you're on Servlet 2.4? Which application server is it in your case?

@spring-projects-issues
Copy link
Collaborator Author

mirak commented

This is a Jonas 4 something based on tomcat5, which supports servlet 2.4 only. I realised that when I tried to used spring 4.x . We are supposed to migrate to Websphere 8.5.5.4 someday ...

Regarding the BeanFactory.getBean what we do actually is something like

	@Bean
	public House houseFactory() {
		return new HouseFactory() {

			@Override
			public HouseFactory House house(String name, int windows) {
				return ctx.getBean("house", name, windows );
			}

		};
	}

We don't expose the spring api in our business objects, and if a constructor changes, we know the impacts at compile time.
This is what looked interesting in being able to directly call the @Bean methods.
At first I though it was a bug because getBean manage to call this @Bean method in some way.

@spring-projects-issues
Copy link
Collaborator Author

mirak commented

I think I will emulate the spring 4.x @Lookup method by create a @Lookup annotation, and use an around aspect, on the will call BeanFactory.getBean( ... ) with the appropriate parameters.
This way we will be ready for spring 4.x, and just remove the aspect declaration.

@spring-projects-issues
Copy link
Collaborator Author

mirak commented

By the way I realised it's possible to also use an interface that implements the @Lookup method and the @Bean method in the @Configuration file, to be sure to have the same parameters.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Aug 29, 2016

mirak commented

I'm afraid @Bean methods are not meant to be called as factory methods from outside. They are rather designed as a factory method callback mechanism within the container, with the container calling them when a particular bean needs to be created.

It seems this JIRA #17048 fixes my issue in Spring 4.1.3 .
Maybe you had time to reflect on this, but what you say now about user call of @Bean methods, seems a bit more cautious than what you commented back then here #17048

@spring-projects-issues spring-projects-issues added type: enhancement A general enhancement in: core Issues in core modules (aop, beans, core, context, expression) has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 4.1 RC2 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants