Skip to content

Support @Autowired-like self injection [SPR-8450] #13096

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

Closed
spring-projects-issues opened this issue Jun 13, 2011 · 16 comments
Closed

Support @Autowired-like self injection [SPR-8450] #13096

spring-projects-issues opened this issue Jun 13, 2011 · 16 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 Jun 13, 2011

Sam Brannen opened SPR-8450 and commented

Background

Autowiring a bean with an instance of itself is not something one would normally do, but there are cases where it might be useful -- for example, to route method calls through the proxy that wraps the bean. There are of course alternatives to this, such as using load-time weaving with AspectJ proxies instead of JDK dynamic proxies.

Note that self-autowiring by name via @Resource is permitted by the framework; whereas, self-autowiring by type is not permitted by the framework as can be seen in the following code snippet from DefaultListableBeanFactory's findAutowireCandidates(String, Class, DependencyDescriptor) method.

for (String candidateName : candidateNames) {
    if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, descriptor)) {
        result.put(candidateName, getBean(candidateName));
    }
}

The name of the bean (i.e., the bean that's trying to autowire itself) is beanName. That bean is in fact an autowire candidate, but the above if-condition returns false (since candidateName equals the beanName). Thus you simply cannot autowire a bean with itself by type (at least not as of Spring 3.1 M2).

Goal

Add support for self-autowiring using @Autowired on fields and methods.

Stack Overflow Discussion

This topic was brought to our attention via a discussion on Stack Overflow.


Affects: 3.0.5

Issue Links:

18 votes, 23 watchers

@spring-projects-issues
Copy link
Collaborator Author

Chris Beams commented

On review of the StackOverflow thread, I wonder whether the "workaround" approach of using @Autowired to inject the enclosing ApplicationContext and look the bean up isn't actually a better idea. It reveals the intention more clearly that the user wants the bean as managed by Spring, e.g. post-proxying, etc. It would probably never be intuitive for someone to see a FooService that uses @Autowired to inject a FooService into itself. The fact that @Resource works here is more an inadvertent bonus than it is a feature by design.

I understand that this approach complicates unit-testability somewhat, but the use cases for this (self-injection) are probably few and far between and thus a reasonable tradeoff.

Placing within the General Backlog where it can get voted up and receive further comments, but there is no immediate intention to resolve at this time.

@spring-projects-issues
Copy link
Collaborator Author

Fred Clausen commented

This is a bonus to me. In my specific case, it means I don't have to mess with the default cache proxying:

@Service(value="fooService")
public JPAFooService implements FooService {

  @Resource(name="fooService") FooService fooService;  // only works if injected by name

  @Cacheable
  List<Foo> getAll() {
     return dao.getFoos();
  };

  boolean hasFoo(Foo f) {
    List<Foo> all = getAll();              // THIS WILL BYPASS THE CACHE
    List<Foo> all2 = fooService.getAll();  // THIS WILL USE THE CACHE
  }
}

@spring-projects-issues
Copy link
Collaborator Author

Chris Beams commented

Hi Fred,

Your example helps make clear why this issue is probably best considered as an advertisement for AspectJ-based advice (as opposed to the proxy approach). By using load- or compile-time weaving and spring-aspect's built in AJ aspects, you can forget about this proxy business entirely.

The proxy approach hits about 80% of the use cases, and that's why we advocate it first. But cases like this are really where bytecode-weaving shines.

@spring-projects-issues
Copy link
Collaborator Author

Ben Fagin commented

On the one hand, calling a method on a reference to your 'real' self could probably be described as an anti-pattern. On the other hand, any day that I can avoid bytecode weaving is a good day. The idea of using a self reference is certainly present in other languages. Used judiciously, perhaps in an application transitioning from proxying to weaving, autowiring a self reference can be helpful.

Is autowiring oneself by type safe though? Is there a risk of autowiring some other bean entirely?

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

Is autowiring oneself by type safe though?

No, it is potentially dangerous (see below).

Is there a risk of autowiring some other bean entirely?

Yes, autowiring by type could potentially result in a different bean of the same type being injected. That's why Fred Clausen, in his example above, explicitly references the bean by name in order to ensure that the bean gets a reference to itself (potentially proxied). He does this using the bean name/ID in conjunction with @Resource; however, the same could be achieved using qualifiers (i.e., via @Qualifier).

@spring-projects-issues
Copy link
Collaborator Author

Niels Bech Nielsen commented

Given that the use case for this problem is self proxy invocation, wouldn't it be simpler to address the use case through an explicit annotation, (e.g @AutowireSelf, @AutowireProxy, or something). It would probably be easy for the BeanFactory to resolve the case correctly, and it would probably be more explanatory than both autowire-by-name and autowire-by-type.

I have found the use case a few times and keeps being amazed at how people work around the problem usually in some less than elegant way. Some by autowire of some kind or by massive delegation or using static holders. Usually the use case have been identified through a massive debugging effort, because the developer was unaware of the problem in the first place.

With a specific annotation it would be easy to document the effect in proxy annotations:
"Should you wish to call a method annotated with @xxxProxyAnnotationOfSomeKind from another method in the same class, you must use a self-proxy(link to annotation)."

@spring-projects-issues
Copy link
Collaborator Author

Premraj Motling commented

@Juergen Will this be supported in coming version? I liked the idea of doing this explicitly (like having special annotation @AutowireSelf)

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

For 4.2, I intend to research the options and suggest a recommended solution for this scenario... A dedicated annotation may indeed be the best compromise.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Eduardo Simioni commented

I have a use case affected by this problem, where I think the solution of having a specific annotation wouldn't work, or maybe it would, but then it would have to consider collections as well, holding self and others.

In my applications there is an event mechanism, to put it very simple:

All @ServiceS extend AbstractService, with the abstract having a:

@Autowired
private List<EventListener> eventListeners;

The AbstractService triggers events on some common situations in my application.
A @Service can be EventListener, meaning that it can potentially be injected in itself through the eventListeners, since it extends AbstractService.

Now, naturally I'm not handling events from a service inside itself, but I need this to be implemented this way so that it is generic and duplication free.

The injection works normally, but there are some corner cases that I don't recall right now where it doesn't.
If you want more details or the exact corner cases, I could investigate and provide.

@spring-projects-issues
Copy link
Collaborator Author

Ben Fagin commented

I have been using a @Self annotation in my own code to handle these situations. I usually find it necessary when I want to ensure that my declarative transaction and caching semantics are preserved. In a perfect world the weaving would take care of this, but I have never been able to get that to work 100% of the time.

This is the code I use in my own projects. While not the absolute best, it has worked so far.

@Component
public class SelfWiringBeanPostProcessor implements BeanPostProcessor, SmartLifecycle, BeanFactoryAware {
	private final List<Callable<Void>> injections = new ArrayList<>();
	private BeanFactory beanFactory;
	private boolean isRunning = false;

	@Override
	public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = beanFactory;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		injections.add(() -> {

			// find all fields marked @Self
			ReflectionUtils.doWithFields(bean.getClass(),

				// get the latest bean and inject it
				field -> {
					Object ref = beanFactory.getBean(beanName);
					ReflectionUtils.makeAccessible(field);
					field.set(bean, ref);
				},

				// filter by annotation
				field -> field.isAnnotationPresent(Self.class)
			);

			return null;
		});

		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	// ------------------------------------------------------------------- //

	@Override
	public boolean isAutoStartup() {
		return true;
	}

	@Override
	public void stop(Runnable callback) {
		stop();
		callback.run();
	}

	@Override
	public void start() {
		for (Callable<Void> method : injections) {
			try {
				method.call();
			} catch (Exception ex) {
				throw Throwables.propagate(ex);
			}
		}

		isRunning = true;
	}

	@Override
	public void stop() {
		isRunning = false;
	}

	@Override
	public boolean isRunning() {
		return isRunning;
	}

	@Override
	public int getPhase() {
		return Integer.MIN_VALUE;
	}
}

@spring-projects-issues
Copy link
Collaborator Author

Othon Crelier commented

It will be really interesting to have this solved.
Particularly interested in @Async and @Transactional use cases.

@spring-projects-issues
Copy link
Collaborator Author

Sam Brannen commented

FYI: this has been resolved in the following GitHub commit:

4a0fa69

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Dec 29, 2016

Peter Rader commented

Not working well in 4.3.5.RELEASE!

{{ /**

  • Authorithy to decide what movements are disallowed.

  • <p>

  • Not responsible in case of movements that are blessed in past (i.e. part of a

  • loading-process of mementos, deserialization, loading a.s.).

  • <p>

  • Notice that this council is only responsible for non-headless-environments.
    */
    @Named
    public final class LayerDropAuthorithy extends LayerDragNDropTransferHandler implements DropVetoCouncilor {
    public LayerDropAuthorithy() {
    System.out.println(SpringVersion.getVersion());

      }
    
      /**
       * The council to decide where to drop elements.
       */
      // @Inject
      public final DropVetoCouncilor[] dropVetoCouncil = null;
    
      @Inject
      public final ApplicationContext ac = null;
    
      @Override
      public boolean allowsMove(final VectorPublishNode target, final Set<VectorPublishNode> nodesConcerned) {
      	boolean blocked = false;
      	for (DropVetoCouncilor dropVetoCouncilor : dropVetoCouncil) {
      		blocked |= dropVetoCouncilor.blockMove(nodesConcerned, target);
      	}
      	return blocked;
      }
    
      @Override
      public boolean blockMove(Set<VectorPublishNode> nodesConcerned, VectorPublishNode target) {
      	// Block if root-element shall be moved.
      	for (VectorPublishNode vectorPublishNode : nodesConcerned) {
      		if (vectorPublishNode.getParent() == null) {
      			return true;
      		}
      	}
      	return false;
      }
    
      @PostConstruct
      public void test() {
      	System.out.println(SpringVersion.getVersion());
      	System.out.println("dddddddddd->" + ac.getBeansOfType(DropVetoCouncilor.class).size());
      }
    

    }

}}

gives:
{{4.3.5.RELEASE
dddddddddd->1}}

but uncomment // @Inject gives {{
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'addSquare': Unsatisfied dependency expressed through field 'history'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'historyImpl': Unsatisfied dependency expressed through field 'layer'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerImpl': Unsatisfied dependency expressed through field 'dragNDropHandler'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerDropAuthorithy': Unsatisfied dependency expressed through field 'dropVetoCouncil'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.vectorpublish.desktop.vp.api.layer.dnd.DropVetoCouncilor[]' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject()}
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'addSquare': Unsatisfied dependency expressed through field 'history'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'historyImpl': Unsatisfied dependency expressed through field 'layer'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerImpl': Unsatisfied dependency expressed through field 'dragNDropHandler'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerDropAuthorithy': Unsatisfied dependency expressed through field 'dropVetoCouncil'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.vectorpublish.desktop.vp.api.layer.dnd.DropVetoCouncilor[]' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject()}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1225)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:552)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
at net.vectorpublish.desktop.vp.VectorPublishApplicationContext.<init>(VectorPublishApplicationContext.java:18)
at net.vectorpublish.desktop.vp.Startup.main(Startup.java:31)
at VPTest.testMe(VPTest.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'historyImpl': Unsatisfied dependency expressed through field 'layer'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerImpl': Unsatisfied dependency expressed through field 'dragNDropHandler'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerDropAuthorithy': Unsatisfied dependency expressed through field 'dropVetoCouncil'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.vectorpublish.desktop.vp.api.layer.dnd.DropVetoCouncilor[]' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject()}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1225)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:552)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1136)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 46 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerImpl': Unsatisfied dependency expressed through field 'dragNDropHandler'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerDropAuthorithy': Unsatisfied dependency expressed through field 'dropVetoCouncil'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.vectorpublish.desktop.vp.api.layer.dnd.DropVetoCouncilor[]' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject()}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1225)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:552)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1136)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 59 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'layerDropAuthorithy': Unsatisfied dependency expressed through field 'dropVetoCouncil'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.vectorpublish.desktop.vp.api.layer.dnd.DropVetoCouncilor[]' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject()}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1225)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:552)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:207)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1136)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 72 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.vectorpublish.desktop.vp.api.layer.dnd.DropVetoCouncilor[]' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject()}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1474)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1102)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 85 more
}}

@spring-projects-issues
Copy link
Collaborator Author

Peter Rader commented

Ignore last message please, using 4.3.4.RELEASE solved the problem. Looks like a different bug.

@ankitkpd
Copy link

ankitkpd commented Apr 23, 2024

Looks like after the fix findAutowireCandidates() will either return candidates that are not selfReferenced or candidates that are Collection/Map beans and are self referenced.

But it won't return both i.e. non self referenced candidates that are being added with

for (String candidate : candidateNames) {
	if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
		addCandidateEntry(result, candidate, descriptor, requiredType);
	}
}

AND candidates that are Collection/Map beans & are self referenced being added with the fix i.e.

for (String candidate : candidateNames) {
	if (isSelfReference(beanName, candidate) &&
			(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
			isAutowireCandidate(candidate, fallbackDescriptor)) {
		addCandidateEntry(result, candidate, descriptor, requiredType);
	}
}

Because if condition if (result.isEmpty()) would prevent it to do so.

Is there a way to get autowired candidates of both types?

@ankitkpd
Copy link

@spring-projects-issues appreciate you feedback about #13096 (comment) Thanks

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

3 participants