10k

Self Injection

Self Injection

What

  1. 我们知道Spring中有依赖注入Dependency Injection,是将依赖的实例的获取交给Spring来执行。而self Injection是在一个类中注入他自己,从而获取到他自己的一个实例。

Why

  1. 从@Transactional讲起。我之前在做的需求就出现了这个使用。
@Service
public class MyService {
    public void doSomething() {
        // ...
        doSomethingElse();
    }

    @Transactional
    public void doSomethingElse() {
        // ...
    }
}
  1. 当我们调用doSomething 的时候发现@Transactional并未生效。-> 在一个实例方法中调用被@Transactional注解标记的另一个方法,且两个方法都属于同一个类时,事务不会生效。
  2. @Transactional是基于切面编程实现,在执行的时候会将原来的类包装成为一个新的代理类。切面是一段代码执行在本身业务代码前后的增强型代码。在当前类调用他自己的另一个方法doSomethingElse过程中,没有外部调用,所以代理类中的function就不会被执行(即切面代码就不会被执行)(因为可能会再次执行切面本身,无限循环。)

  3. 对应到这个例子中就是(伪代码)

    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

  1. 模块之间循环依赖。
  2. 当实例很多时候可能造成内存泄漏和性能问题。

Conclusion

小心使用。

Reference

  1. Self-Injection In Spring
  2. 为什么Spring可以“自己注入自己
Thoughts? Leave a comment