Self injection with Spring

JavaSpringDependency InjectionIoc Container

Java Problem Overview


I tried the following code with Spring 3.x which failed with BeanNotFoundException and it should according to the answers of a question which I asked before - https://stackoverflow.com/questions/5107505/can-i-inject-same-class-using-spring

@Service
public class UserService implements Service{
    @Autowired
    private Service self;
}

Since I was trying this with Java 6, I found the following code works fine:

@Service(value = "someService")
public class UserService implements Service{
    @Resource(name = "someService")
    private Service self;
}

but I don't understand how it resolves the cyclic dependency.

EDIT:
Here's the error message. The OP mentioned it in a comment on one of the answers:

> Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.spring.service.Service] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Java Solutions


Solution 1 - Java

Update: February 2016

Self autowiring will be officially supported in Spring Framework 4.3. The implementation can be seen in this GitHub commit.


The definitive reason that you cannot autowire yourself is that the implementation of Spring's DefaultListableBeanFactory.findAutowireCandidates(String, Class, DependencyDescriptor) method explicitly excludes the possibility. This is visible in the following code excerpt from this method:

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

FYI: 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 in fact equals the beanName). Thus you simply cannot autowire a bean with itself (at least not as of Spring 3.1 M1).

Now as for whether or not this is intended behavior semantically speaking, that's another question. ;)

I'll ask Juergen and see what he has to say.

Regards,

Sam (Core Spring Committer)

p.s. I've opened a Spring JIRA issue to consider supporting self-autowiring by type using @Autowired. Feel free to watch or vote for this issue here: https://jira.springsource.org/browse/SPR-8450

Solution 2 - Java

This code works too:

@Service
public class UserService implements Service {

    @Autowired
    private ApplicationContext applicationContext;

    private Service self;

    @PostConstruct
    private void init() {
        self = applicationContext.getBean(UserService.class);
    }
}

I don't know why, but it seems that Spring can get the bean from ApplicationContext if is created, but not initialized. @Autowired works before initialization and it cannot find the same bean. So, @Resource maybe works after @Autowired and before @PostConstruct.

But I don't know, just speculating. Anyway, good question.

Solution 3 - Java

Given above code I don't see a cyclic dependency. You injecting some instance of Service into UserService. The implementation of the injected Service does not necessarily need to be another UserService so there is no cyclic dependency.

I do not see why you would inject a UserService into UserService but I'm hoping this is a theoretic try out or such.

Solution 4 - Java

By the way, the more elegant solution to the self-invocation problem is to use AspectJ Load-Time Weaving for your transactional proxies (or whatever AOP-introduced proxy you're using).

For example, with annotation-driven transaction management, you can use the "aspectj" mode as follows:

<tx:annotation-driven mode="aspectj" />

Note that the default mode is "proxy" (i.e., JDK dynamic proxies).

Regards,

Sam

Solution 5 - Java

https://stackoverflow.com/questions/7482666/get-aop-proxy-from-the-object-itself question suggests alternative hacky approach with AopContext.currentProxy() that may be suitable for special cases.

Solution 6 - Java

It looks like spring creates and configures an object and then places it in the bean look up context. But, in the case of Java, I think it creates the object and ties it to the name and the during configuration when the object is looked up by the name it is found in the context.

Solution 7 - Java

Just another aproach:

@EnableAsync
@SpringBootApplication
public class Application {

    @Autowired
    private AccountStatusService accountStatusService;

    @PostConstruct
    private void init() {
        accountStatusService.setSelf(accountStatusService);
    }
}

@Service
public class AccountStatusService {
    private AccountStatusService self;

    public void setSelf(AccountStatusService self) {
        this.self = self;
    }
}

with this your service will be in proxy. I did this to work with async methods inside itself.

I have tryied @sinuhepop solution:

@PostConstruct
private void init() {
    self = applicationContext.getBean(UserService.class);
}

It did a injection but the service wasn't inside proxy and my methods wasn't running on a new thread. With that aproach it works as i would like.

Solution 8 - Java

This is my solution for small to medium sized projects. No AspectJ or application context magic, it works with singletons and constructor injection and is very easy to test.

@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {

    private final PersonDao _personDao;

    @Autowired
    public PersonDao(PersonDao personDao) {
        _personDao = personDao;
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPremrajView Question on Stackoverflow
Solution 1 - JavaSam BrannenView Answer on Stackoverflow
Solution 2 - JavasinuhepopView Answer on Stackoverflow
Solution 3 - JavaStijn GeukensView Answer on Stackoverflow
Solution 4 - JavaSam BrannenView Answer on Stackoverflow
Solution 5 - JavaVadzimView Answer on Stackoverflow
Solution 6 - JavaKrishnaView Answer on Stackoverflow
Solution 7 - JavaGilsonView Answer on Stackoverflow
Solution 8 - JavaMario EisView Answer on Stackoverflow