CRUD补全计划
首页
  • Java-集合框架

    • Java集合-概述
    • Java集合-源码解析
  • Java-并发相关

    • Java并发-概述
    • Java并发-线程池
    • Java并发-锁详解
  • Java-JVM相关

    • Java-类加载机制
    • Java-垃圾回收机制
  • SQL 数据库

    • MySQL详解
    • MySQL-索引
    • MySQL-事务
  • NoSQL 数据库

    • Redis-概述
    • Redis-Zset实现原理
    • Redis-AOF与RDB
  • Spring知识体系

    • Spring-IOC概述
    • Spring-IOC源码分析
    • Spring-AOP原理详解
  • ORM框架

    • Mybatis架构
    • Mybatis执行流程
    • Mybatis缓存原理
  • RPC框架

    • Dubbo详解
  • 限流框架

    • 限流框架详解
  • Web容器

    • Tomcat详解
  • 架构基础

    • 高并发-缓存
    • 高并发-限流
  • 场景实现

    • 场景概述
    • 订单过期关闭
    • 库存扣减
  • 概述

    • 机器学习概述
    • 网站roadmap
    • 关于我
    • 友链
首页
  • Java-集合框架

    • Java集合-概述
    • Java集合-源码解析
  • Java-并发相关

    • Java并发-概述
    • Java并发-线程池
    • Java并发-锁详解
  • Java-JVM相关

    • Java-类加载机制
    • Java-垃圾回收机制
  • SQL 数据库

    • MySQL详解
    • MySQL-索引
    • MySQL-事务
  • NoSQL 数据库

    • Redis-概述
    • Redis-Zset实现原理
    • Redis-AOF与RDB
  • Spring知识体系

    • Spring-IOC概述
    • Spring-IOC源码分析
    • Spring-AOP原理详解
  • ORM框架

    • Mybatis架构
    • Mybatis执行流程
    • Mybatis缓存原理
  • RPC框架

    • Dubbo详解
  • 限流框架

    • 限流框架详解
  • Web容器

    • Tomcat详解
  • 架构基础

    • 高并发-缓存
    • 高并发-限流
  • 场景实现

    • 场景概述
    • 订单过期关闭
    • 库存扣减
  • 概述

    • 机器学习概述
    • 网站roadmap
    • 关于我
    • 友链
  • Spring知识体系

    • Spring-IOC概述
      • Spring-IOC源码分析
      • Spring-AOP原理详解
    • Spring
    • Spring知识体系
    zfd
    2023-05-13
    目录

    Spring-IOC概述

    # IoC是什么

    Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

    我们来深入分析一下:

    • 谁控制谁,控制什么?

    传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

    • 为何是反转,哪些方面反转了?

    有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

    • 用图例说明一下?

    传统程序设计下,都是主动去创建相关对象然后再组合起来:

    当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图

    # IoC能做什么

    IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。

    传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

    其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

    IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

    # IoC和DI是什么关系

    控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。通俗来说就是IoC是设计思想,DI是实现方式。

    DI—Dependency Injection,即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

    我们来深入分析一下:

    • 谁依赖于谁?

    当然是应用程序依赖于IoC容器;

    • 为什么需要依赖?

    应用程序需要IoC容器来提供对象需要的外部资源;

    • 谁注入谁?

    很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;

    • 注入了什么?

    就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

    • IoC和DI有什么关系呢?

    其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。通俗来说就是IoC是设计思想,DI是实现方式。

    # IoC和DI使用问题小结

    这里总结下实际开发中会遇到的一些问题:

    # 为什么推荐构造器注入方式?

    先来看看Spring在文档里怎么说:

    The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.

    简单的翻译一下:这个构造器注入的方式能够保证注入的组件不可变,并且确保需要的依赖不为空。此外,构造器注入的依赖总是能够在返回客户端(组件)代码的时候保证完全初始化的状态。

    下面来简单的解释一下:

    • 依赖不可变:其实说的就是final关键字。
    • 依赖不为空(省去了我们对其检查):当要实例化UserServiceImpl的时候,由于自己实现了有参数的构造函数,所以不会调用默认构造函数,那么就需要Spring容器传入所需要的参数,所以就两种情况:1、有该类型的参数->传入,OK 。2:无该类型的参数->报错。
    • 完全初始化的状态:这个可以跟上面的依赖不为空结合起来,向构造器传参之前,要确保注入的内容不为空,那么肯定要调用依赖组件的构造方法完成实例化。而在Java类加载实例化的过程中,构造方法是最后一步(之前如果有父类先初始化父类,然后自己的成员变量,最后才是构造方法),所以返回来的都是初始化之后的状态。

    所以通常是这样的

    
     @Service
    public class UserServiceImpl {
    
        /**
         * user dao impl.
         */
        private final UserDaoImpl userDao;
    
        /**
         * init.
         * @param userDaoImpl user dao impl
         */
        public UserServiceImpl(final UserDaoImpl userDaoImpl) {
            this.userDao = userDaoImpl;
        }
    
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    如果使用setter注入,缺点显而易见,对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。而且将一直是个潜在的隐患,因为你不调用将一直无法发现NPE的存在。

    // 这里只是模拟一下,正常来说我们只会暴露接口给客户端,不会暴露实现。
    UserServiceImpl userService = new UserServiceImpl();
    userService.findUserList(); // -> NullPointerException, 潜在的隐患
    
    1
    2
    3

    循环依赖的问题:使用field注入可能会导致循环依赖,即A里面注入B,B里面又注入A:

    public class A {
        @Autowired
        private B b;
    }
    
    public class B {
        @Autowired
        private A a;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    如果使用构造器注入,在spring项目启动的时候,就会抛出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?从而提醒你避免循环依赖,如果是field注入的话,启动的时候不会报错,在使用那个bean的时候才会报错。

    # Autowired、Resource、Inject有何区别?

    @Autowired和@Resource以及@Inject等注解注入有何区别? 这时平时在开发中,或者常见的面试题。

    # @Autowired

    • Autowired注解源码

    在Spring 2.5 引入了 @Autowired 注解

    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
      boolean required() default true;
    }
    
    1
    2
    3
    4
    5
    6

    从Autowired注解源码上看,可以使用在下面这些地方:

    @Target(ElementType.CONSTRUCTOR) #构造函数
    @Target(ElementType.METHOD) #方法
    @Target(ElementType.PARAMETER) #方法参数
    @Target(ElementType.FIELD) #字段、枚举的常量
    @Target(ElementType.ANNOTATION_TYPE) #注解
    
    1
    2
    3
    4
    5

    还有一个value属性,默认是true。

    • 简单总结:

    1、@Autowired是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor 类实现的依赖注入

    2、@Autowired可以作用在CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION_TYPE

    3、@Autowired默认是根据类型(byType )进行自动装配的

    4、如果有多个类型一样的Bean候选者,需要指定按照名称(byName )进行装配,则需要配合@Qualifier。

    指定名称后,如果Spring IOC容器中没有对应的组件bean抛出NoSuchBeanDefinitionException。也可以将@Autowired中required配置为false,如果配置为false之后,当没有找到相应bean的时候,系统不会抛异常

    • 简单使用代码:

    在字段属性上。

    @Autowired
    private HelloDao helloDao;
    
    1
    2

    或者

    private HelloDao helloDao;
    public HelloDao getHelloDao() {
     return helloDao;
    }
    @Autowired
    public void setHelloDao(HelloDao helloDao) {
     this.helloDao = helloDao;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8

    或者

    private HelloDao helloDao;
    //@Autowired
    public HelloServiceImpl(@Autowired HelloDao helloDao) {
     this.helloDao = helloDao;
    }
    // 构造器注入也可不写@Autowired,也可以注入成功。
    
    1
    2
    3
    4
    5
    6

    将@Autowired写在被注入的成员变量上,setter或者构造器上,就不用再xml文件中配置了。

    如果有多个类型一样的Bean候选者,则默认根据设定的属性名称进行获取。如 HelloDao 在Spring中有 helloWorldDao 和 helloDao 两个Bean候选者。

    @Autowired
    private HelloDao helloDao;
    
    1
    2

    首先根据类型获取,发现多个HelloDao,然后根据helloDao进行获取,如果要获取限定的其中一个候选者,结合@Qualifier进行注入。

    @Autowired
    @Qualifier("helloWorldDao")
    private HelloDao helloDao;
    
    1
    2
    3

    注入名称为helloWorldDao 的Bean组件。@Qualifier("XXX") 中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。

    多个类型一样的Bean候选者,也可以@Primary进行使用,设置首选的组件,也就是默认优先使用哪一个。

    注意:使用@Qualifier 时候,如何设置的指定名称的Bean不存在,则会抛出异常,如果防止抛出异常,可以使用:

    @Qualifier("xxxxyyyy")
    @Autowired(required = false)
    private HelloDao helloDao;
    
    1
    2
    3

    在SpringBoot中也可以使用@Bean+@Autowired进行组件注入,将@Autowired加到参数上,其实也可以省略。

    @Bean
    public Person getPerson(@Autowired Car car){
     return new Person();
    }
    // @Autowired 其实也可以省略
    
    1
    2
    3
    4
    5

    # @Resource

    • Resource注解源码
    @Target({TYPE, FIELD, METHOD})
    @Retention(RUNTIME)
    public @interface Resource {
        String name() default "";
        // 其他省略
    }
    
    1
    2
    3
    4
    5
    6

    从Resource注解源码上看,可以使用在下面这些地方:

    @Target(ElementType.TYPE) #接口、类、枚举、注解
    @Target(ElementType.FIELD) #字段、枚举的常量
    @Target(ElementType.METHOD) #方法
    
    1
    2
    3

    name 指定注入指定名称的组件。

    • 简单总结:

    1、@Resource是JSR250规范的实现,在javax.annotation包下

    2、@Resource可以作用TYPE、FIELD、METHOD上

    3、@Resource是默认根据属性名称进行自动装配的,如果有多个类型一样的Bean候选者,则可以通过name进行指定进行注入

    • 简单使用代码:
    @Component
    public class SuperMan {
        @Resource
        private Car car;
    }
    
    1
    2
    3
    4
    5

    按照属性名称 car 注入容器中的组件。如果容器中BMW还有BYD两种类型组件。指定加入BMW。如下代码:

    @Component
    public class SuperMan {
        @Resource(name = "BMW")
        private Car car;
    }
    
    1
    2
    3
    4
    5

    name 的作用类似 @Qualifier

    # @Inject

    • Inject注解源码
    @Target({ METHOD, CONSTRUCTOR, FIELD })
    @Retention(RUNTIME)
    @Documented
    public @interface Inject {}
    
    1
    2
    3
    4

    从Inject注解源码上看,可以使用在下面这些地方:

    @Target(ElementType.CONSTRUCTOR) #构造函数
    @Target(ElementType.METHOD) #方法
    @Target(ElementType.FIELD) #字段、枚举的常量
    
    1
    2
    3
    • 简单总结:

    1、@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject jar包 ,才能实现注入

    2、@Inject可以作用CONSTRUCTOR、METHOD、FIELD上

    3、@Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;

    • 简单使用代码:

    指定加入BMW组件。

    @Inject
    @Named("BMW")
    private Car car;
    
    1
    2
    3

    @Named 的作用类似 @Qualifier!

    # 总结

    1、@Autowired是Spring自带的,@Resource是JSR250规范实现的,@Inject是JSR330规范实现的

    2、@Autowired、@Inject用法基本一样,不同的是@Inject没有required属性

    3、@Autowired、@Inject是默认按照类型匹配的,@Resource是按照名称匹配的

    4、@Autowired如果需要按照名称匹配需要和@Qualifier一起使用,@Inject和@Named一起使用,@Resource则通过name进行指定

    # 参考文章

    Inversion of Control Containers and the Dependency Injection pattern在新窗口打开 (opens new window)

    https://www.iteye.com/blog/jinnianshilongnian-1413846

    https://blog.csdn.net/qq_35634181/article/details/104276056

    https://www.cnblogs.com/diandianquanquan/p/11518365.html

    上次更新: 2024/06/05, 22:36:57

    Spring-IOC源码分析→

    Theme by Vdoing | Copyright © 2013-2025 zfd 苏ICP备2023039568号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式