IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Self Injection

    10k发表于 2023-09-08 00:00:00
    love 0

    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可以“自己注入自己


沪ICP备19023445号-2号
友情链接