Self Injection!
We know that there are dependency injection in Spring, who helps us to do the bean instantiation. The self injection , you can tell from its name, is a bean inject itself in its class, to overcome the limitation described below of AOP.
Why
- Start from @Transactional
```java @Service public class MyService { public void doSomething() { // ... doSomethingElse(); }
@Transactional public void doSomethingElse() { // ... }
} ```
When
doSomething()
is called, the transactional annotation doesn't take effect. -> when you call another method in a method(self invoking method) , and both methods belongs to the same class, the @Transactional will not take effective.
@Transactional
, annotation in Spring is based on Aspect oriented programming. The original class will be wrapped as a new proxy class. -> to avoid the repeat execution of such code,calling class method directly will not take effect -> so when
doSomething
calldoSomethingElse
, the aspect code in the proxy class won't be executed.e.g.
The code that java generate agent class
```java public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// transactional stuff, maybe add lock Object result = method.invoke(proxiedObject, args);
// transactional stuff, maybe release lock return result; } ```
How self injection avoid this
```java @Service public class MyService { @Autowired private MyService service; public void doSomething() { // ... service.doSomethingElse(); }
@Transactional public void doSomethingElse() { // ... } } ```
After self injection, the call of
doSomethingElse()
will be regarded as another instance function so the aspect code will take effect.
Risk
- Circular dependencies between modules. (APPENDIX 1)
- Memory leak(same class instance being managed by the GC) and performance issue(additional instance is created)
Conclusion
Use carefully
Referece
Appendix 1
Spring 4.3 handle the circular dependencies cause by self injection
/** * Find bean instances that match the required type. * Called during autowiring for the specified bean. * @param beanName the name of the bean that is about to be wired * @param requiredType the actual type of bean to look for * (may be an array component type or collection element type) * @param descriptor the descriptor of the dependency to resolve * @return a Map of candidate names and candidate instances that match * the required type (never {@code null}) * @throws BeansException in case of errors * @see #autowireByType * @see #autowireConstructor */ protected Map<String, Object> findAutowireCandidates( @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length); for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) { Class<?> autowiringType = classObjectEntry.getKey(); if (autowiringType.isAssignableFrom(requiredType)) { Object autowiringValue = classObjectEntry.getValue(); autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); if (requiredType.isInstance(autowiringValue)) { result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); break; } } } for (String candidate : candidateNames) { if (!***isSelfReference***(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } if (result.isEmpty()) { boolean multiple = indicatesMultipleBeans(requiredType); // Consider fallback matches if the first pass failed to find anything... DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); for (String candidate : candidateNames) { if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) && (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) { addCandidateEntry(result, candidate, descriptor, requiredType); } } if (result.isEmpty() && !multiple) { // Consider self references as a final pass... // but in the case of a dependency collection, not the very same bean itself. for (String candidate : candidateNames) { if (isSelfReference(beanName, candidate) && (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) && isAutowireCandidate(candidate, fallbackDescriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } } } return result; }
关键点在:isSelfReference
, beanName 是需要注入的self bean,candidateName是当前正在初始化的bean:
/** * Determine whether the given beanName/candidateName pair indicates a self reference, * i.e. whether the candidate points back to the original bean or to a factory method * on the original bean. */ private boolean isSelfReference(@Nullable String beanName, @Nullable String candidateName) { return (beanName != null && candidateName != null && (beanName.equals(candidateName) || (containsBeanDefinition(candidateName) && beanName.equals(getMergedLocalBeanDefinition(candidateName).getFactoryBeanName())))); }
考虑到requiredType有可能是接口类,故通过BeanFactoryUtils.beanNamesForTypeIncludingAncestors方法可以拿到具体的需要注入的beanNames,并放在candidateNames。