Self Injection
What
- 我们知道Spring中有依赖注入Dependency Injection,是将依赖的实例的获取交给Spring来执行。而self Injection是在一个类中注入他自己,从而获取到他自己的一个实例。
Why
- 从@Transactional讲起。我之前在做的需求就出现了这个使用。
@Service public class MyService { public void doSomething() { // ... doSomethingElse(); } @Transactional public void doSomethingElse() { // ... } }
- 当我们调用doSomething 的时候发现
@Transactional
并未生效。-> 在一个实例方法中调用被@Transactional
注解标记的另一个方法,且两个方法都属于同一个类时,事务不会生效。 -
@Transactional是基于切面编程实现,在执行的时候会将原来的类包装成为一个新的代理类。切面是一段代码执行在本身业务代码前后的增强型代码。在当前类调用他自己的另一个方法doSomethingElse过程中,没有外部调用,所以代理类中的function就不会被执行(即切面代码就不会被执行)(因为可能会再次执行切面本身,无限循环。)
-
对应到这个例子中就是(伪代码)
Spring生成的代理方法是
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; }
这里当调用doSomethingElse方法的时候本应该走的是这个invoke方法(代理方法),这里的Method就是doSomethingElse。但是doSomething直接调用了doSomethingElse,而不是这个代理方法invoke,所以切面不会被执行。
How
@Service public class MyService { @Autowired private MyService service; public void doSomething() { // ... service.doSomethingElse(); } @Transactional public void doSomethingElse() { // ... } }
当自注入后,调用doSomethingElse就是另一个实例(外部),就不会有切面不生效的问题。这里当前的this指的是当前实例二走到service.doSomeThingElse service是另一个实例。这里产生了外部调用
Risk
- 模块之间循环依赖。
- 当实例很多时候可能造成内存泄漏和性能问题。
Conclusion
小心使用。