• Abstract类中使用@Autowire


    背景

    项目使用的就是SpringBoot默认的结构,我看了下,依赖注入使用了最不推荐的字段注入。

    字段注入

    为了保持项目风格统一,省的有些理论派挑刺,还是延续字段注入的操作。
    某个业务场景下,有一个抽象的父类和多个具体的子类,子类中需要用到父类注入的对象。
    当即有人就说,这么写:

    public abstract class AbstractClass{
        @Autowired
        protected InjectedBean injectedBean;
    }
    

    it works!
    但是我们看到为了只让子类使用该对象,我们使用了protected 访问修饰符,但这意味着,子类也可以set该对象一个新的值。基础知识。
    哦,改进一下呗

    public abstract class AbstractClass{
        @Autowired
        protected final InjectedBean injectedBean;
    }
    

    final加持,完美的解决了问题。很可惜,不可以。
    为什么呢?

    Having @Autowired and final on a field are contradictory.
    The latter says: this variable has one and only one value, and it's initialized at construction time.
    The former says: Spring will construct the object, leaving this field as null (its default value). Then Spring will use reflection to initialize this field with a bean of type WorkspaceRepository.
    If you want final fields autowired, use constructor injection

    ref: https://stackoverflow.com/questions/34580033/spring-io-autowired-the-blank-final-field-may-not-have-been-initialized

    简单来说,就是二者赋值的时机不统一造成了互斥。

    构造函数注入

    那我们使用上面提到的,通constructor injection注入试试

    public abstract class AbstractClass{
        private InjectedBean injectedBean;
    
        @Autowired
        public void AbstractClass(InjectedBean injectedBean) {
            this.injectedBean = injectedBean;
        }
    }
    

    Spring 不会在抽象类的构造函数上解析 @Autowired 注解。可以通过子类的构造函数注入实现。

    public abstract class AbstractClass{
        private InjectedBean injectedBean;
    
        public void AbstractClass(InjectedBean injectedBean) {
            this.injectedBean = injectedBean;
        }
    }
    
    public class ChildClass{
        private InjectedBean injectedBean;
    
        @Autowired
        public void ChildClass(InjectedBean injectedBean) {
            super(injectedBean);
        }
    }
    

    这么写真的有些繁琐了...而且,就我的项目而言,我的初衷是给子类使用,这么岂不是多此一举。
    好的,也就引出了Setter注入。

    Setter注入

    public abstract class AbstractClass{
        private LogRepository logRepository;
        @Autowired
        public final void setLogRepository(LogRepository logRepository) {
            this.logRepository = logRepository;
        }
    }
    

    Setter的时候,标记为public final
    Getter的时候,标记为protected

    可以说是最佳实践了。

    ref: https://segmentfault.com/a/1190000039053805

    为什么不推荐使用字段注入?

    事实上,当你使用IDE开发的时候,你使用了Spring的字段注入,你会得到一个提示:

    Field injection is not recommended
    

    当然是因为这种方式有好多缺点:

    1. 不允许不可变字段的声明,像我们刚才说的 @Autowired protected final

    2. 代码坏味道的潜在根源
      因为字段注入是如此的方便,你可以“只要需要”就注入一个你想操作的对象,结果不知不觉中注入了十几个甚至几十个(见贤思齐,我所在的项目中确实已经有这样的问题了)。
      反之,如果我们通过构造函数注入,随着注入对象的增多,构造函数的参数不断变多,你明显的就能闻到“坏味道”了,这时候你得开始想,是时候把当前类重新划分了,怎么能承担如此多的职责呢?职责单一忘了吗?SOLID天天盯着你们哪!

    3. 和Spring的容器紧耦合
      如果这点你不觉得有什么,那你是不是忘记写单元测试了?
      单元测试的时候你也引入了Spring框架?就是为了使用“注入”这个功能?
      如果我们使用构造函数注入,或者Setter注入,我们在脱离Spring框架之后(比如单元测试)的时候,我们可以通过其他方式传入目标对象。反之,我们只能强依赖Spring框架了。

    4. 隐藏了依赖
      当使用依赖项注入模式时,受影响的类应该通过公开构造函数中所需的依赖项或使用方法(setter)的可选依赖项,使用公共接口清楚地公开这些依赖项。当使用基于字段的依赖项注入时,类本质上是向外部世界隐藏这些依赖项。

    ref: https://blog.marcnuri.com/field-injection-is-not-recommended

  • 相关阅读:
    java学习day62-Spring boot整合Shiro配置
    java学习day62-springboot中的拦截
    java学习day62-DB项目-首页菜单动态显示
    疯狂学java的第26天
    疯狂学java的第25天
    疯狂学java的第24天
    疯狂学java的第23天
    疯狂学java的第22天
    疯狂学java的第21天
    疯狂学java的第20天
  • 原文地址:https://www.cnblogs.com/talentzemin/p/15093010.html
Copyright © 2020-2023  润新知