【Spring】Spring控制反转(Inversion of Control,IoC)(笔记一)

【Spring】Spring控制反转(Inversion of Control,IoC)(笔记一)

注:学习Spring的初期可能无法理解控制反转、依赖注入、注解等概念,不要气馁,坚持学习。学完注解开发基础后,再回头尝试去理解这些感念,会发现容易许多。(个人的一些感想)

一、基础概念

1、Spring框架概述

  • 2003年兴起的轻量级Java开发框架,为解决企业应用开发的复杂性而创建。

  • Spring的核心是 控制反转(IoC)面向切面编程(AOP)

  • Spring官网

  • 可以将Spring容器看成一个超级大工厂,负责创建、管理所有Java对象,这些Java对象被称为Bean。Spring通过“依赖注入(DI)”的方式管理Bean之间的依赖关系,使用“控制反转(IoC)”实现对象之间的“解耦合”。

2、Spring的优点

  • 1)轻量级:Spring框架使用的jar均较小,通常早1M以下,Spring核心功能所需的jar共4M左右。Spring框架运行占用资源少,运行效率高,且不依赖其它jar包。
  • 2)针对接口编程,解耦合:Spring提供了 控制反转(IoC),由容器管理对象及对象之间的依赖关系。
  • 3)AOP编程的支持:即将公共的、通用的重复的代码单独开发,在需要的时候反织回去,底层的原理是动态代理。
  • 4)方便集成各种优秀框架:Spring不排斥各种优秀的开源框架,且Spring可降低各种框架的使用难度,Spring提供了对各种优秀框架(Struts2、Hibernate、MyBatis等)的直接支持。

3、Spring体系架构

  • Spring由20多个模块组成,可分为:数据访问/集成、Web、AOP、JVM代理、消息发送、核心容器、测试。
    在这里插入图片描述

4、IOC的概念(重点)

  • IoC是一个概念,又是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现独享的创建、属性的赋值、依赖的管理。
  • IoC的实现,当今流行的方式为依赖注入

5、依赖注入(Dependency Injection,DI)

  • 依赖:A类中含有B类的实例,在A中调用B的方法完成某项功能,即称A类对B类有依赖。

  • DI, 程序代码不用做定位查询,这些工作由容器自行完成。DI是指程序运行过程中,若需要调用另一个对象协助,无序代码中创建被调用者,而是依赖外部容器(Spring),有外部容器创建后传递给程序。

  • Spring框架使用依赖注入(DI)实现控制反转(IoC)。

二、控制反转(Inversion of Control,IoC)

以下通过案例阐述IoC与DI

案例基于Maven实现,有关Maven的使用不再阐述。

1、一个简单的Spring程序

1.1 目录结构

在这里插入图片描述

1.2 引入Maven依赖(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.Etui</groupId>
  <artifactId>spring_01_demo</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

	<!-- 引入Spring依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.18</version>
    </dependency>

  </dependencies>

  <build>
    <resources>

      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.properties</include>
        </includes>
      </resource>

      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.xml</include>
          <include>**/*.properties</include>
        </includes>
      </resource>
    </resources>

  </build>
</project>
1.3 自定义实体类Student
package com.Etui.entity01;

public class Student {
    
    
    private String name;
    private Integer age;

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
	
    public Student() {
    
    
        System.out.println("Student对象被创建……………………");
    }
}
1.4 Spring的配置文件applicationContext.xml
  • Spring 的配置文件位于rsrc/main/resources目录下,文件名任意。
  • <bean />:用于定义一个实例对象,一个实例对象对应一个bean元素。
    • id:bean实例的唯一标识,相当于实例对象的名称。
    • class:指定该bean的类,这里只能是类不能是接口
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
        创建学生对象
        等同于 Student stu = new Student();
        id就是对象名称
        name是对象的类型,底层通过反射构建对象

        启动容器即创建对象
    -->
    <bean id="stu" class="com.Etui.entity01.Student">
        <!-- setter注入方式 -->
        <property name="name" value="神里绫华"></property>
        <property name="age" value="20"></property>
    </bean>

</beans>
1.5 测试类testSpring.java
package com.Etui.test01;

import com.Etui.entity01.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testSpring {
    
    

//  传统常见Student对象的方法
    @Test
    public void testStudent() {
    
    
        Student student = new Student();
        System.out.println(student);
    }
    
//  通过Spring创建对象的方法
    @Test
    public void testStudentSpring() {
    
    
        ApplicationContext ac = new ClassPathXmlApplicationContext("s01/applicationContext.xml");
        Student student = (Student) ac.getBean("stu");
        System.out.println(student);
    }
}
1.6 运行结果

在这里插入图片描述

2、Spring的容器接口和实现类

2.1 ApplicationContext 接口(容器)
  • ApplicationContext用于加载Spring的配置文件,在程序中充当“容器”的角色,它的实现类有两个:
    在这里插入图片描述

  • Spring 配置文件在项目的类路径下,则使用ClassPathXmlApplicationContext 实现类进行加载。

2.2 ApplicationContext容器中对象的装配时机
  • ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。 以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。Spring初始化对象时要使用无参的构造方法,切记保证类中有无参构造方法。
2.3 Spring创建java对象结构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-12RfOq7h-1650805242647)(F:\CSDN写作素材\Sprin容器创建对象.jpg)]

3、注入分类(重点)

bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。根据注入方式的不同,常用的有两类:set 注入、构造注入

3.1 set注入
  • 通过setter方法传入调用者的实例。这种注入方式简单直观,在spring的依赖注入中大量使用。
3.1.1 简单类型
  • School类

    package com.Etui.entity02;
    
    public class School {
          
          
        private String name;
        private String address;
    
        public School() {
          
          
            System.out.println("School类被创建………………");
        }
    
        @Override
        public String toString() {
          
          
            return "School{" +
                    "name='" + name + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    
        public void setName(String name) {
          
          
            this.name = name;
        }
    
        public void setAddress(String address) {
          
          
            this.address = address;
        }
    }
    
    
  • spring配置文件

        <!-- 创建School对象 -->
        <bean id="school" class="com.Etui.entity02.School">
            <property name="name" value="合肥学院"></property>
            <property name="address" value="合肥市蜀山区"></property>
        </bean>
    
  • 测试类
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PCZpxNmX-1650805242648)(C:\Users\34162\AppData\Roaming\Typora\typora-user-images\image-20220424172003413.png)]

3.1.2 引用类型
  • 当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref 的值必须为某 bean 的 id 值。如下,Student类中含有Scholar类。

    package com.Etui.entity02;
    
    public class Student {
          
          
        private String name;
        private Integer age;
        private School school;
    
        @Override
        public String toString() {
          
          
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", school=" + school +
                    '}';
        }
    
        public void setName(String name) {
          
          
            this.name = name;
        }
    
        public void setAge(Integer age) {
          
          
            this.age = age;
        }
    
        public void setSchool(School school) {
          
          
            this.school = school;
        }
    
        public Student() {
          
          
        }
    }
    
  • 对其他对象的引用,使用bean标签的ref属性:

    <!-- 创建Student对象 -->
    <bean id="student" class="com.Etui.entity02.Student">
        <property name="name" value="凝光"></property>
        <property name="age" value="21"></property>
        <property name="school" ref="school"></property>
    </bean>
    
  • 测试类
    在这里插入图片描述

3.2 构造方法的注入
  • 构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器依赖关系。在实体类中必须提供相应参数的构造方法。

  • 构造注入通过<constructor-arg / >标签实现,该标签属性有:

    • name:指定参数名称。
    • index:参数对应着构造器的第几个参数,从零开始。可不写,但若参数类型相同,或参数之间有包含关系,则需要保证赋值顺序与构造器中的参数顺序一致。
  • 实体类如下:

    package com.Etui.entity02;
    public class School {
          
          
        private String name;
        private String address;
    }
    public class Student {
          
          
        private String name;
        private Integer age;
    }
    public class Student {
          
          
        private String name;
        private Integer age;
        private School school;
    }
    
  • 构造方法注入共有以下三种注入方式:

    <!-- 创建学校的对象,使用构造方法参数名称注入值 -->
    <bean id="school" class="com.Etui.entity03.School">
        <constructor-arg name="name" value="合肥学院"></constructor-arg>
        <constructor-arg name="address" value="合肥市蜀山区"></constructor-arg>
    </bean>
    
    <!-- 创建学生对象,使用构造方法的参数的下标注入值 -->
    <bean id="student" class="com.Etui.entity03.Student">
        <constructor-arg index="0" value="钟离"></constructor-arg>
        <constructor-arg index="1" value="23"></constructor-arg>
        <constructor-arg index="2" ref="school"></constructor-arg>
    </bean>
    
    <!-- 创建学生对象,使用默认的构造方法的参数顺序 -->
    <bean id="studentSequence" class="com.Etui.entity03.Student">
        <constructor-arg value="甘雨"></constructor-arg>
        <constructor-arg value="18"></constructor-arg>
        <constructor-arg ref="school"></constructor-arg>
    </bean>
    
3.3 引用类型属性自动注入
3.3.1 byName方式自动注入
  • 当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。
3.3.2 byType 方式自动注入
  • 使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类, 要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。

4、基于注解的DI

  • DI 使用注解,不再需要在Spring配置文件中声明bean实例。Spring中使用注解,需要在原有的基础之上配置组件扫描器,用于指定在基本包中扫描注解。如下:
    在这里插入图片描述

4.1 指定多个包的三种方式

  • 1)使用多个 context:component-scan 指定多个不同的包路径

    <context:component-scan base-package="com.Etui.entity"></context:component-scan>
    <context:component-scan base-package="com.Etui.service"></context:component-scan>
    
  • 2)指定 base-package的值使用分隔符

    <!-- 分隔符可使用逗号、分号或空格 -->
    <context:component-scan base-package='com.Etui.entity, com.Etui.service'></context:component-scan>
    
  • 3)base-package指定到父包名

    <context:component-scan base-package="com.Etui"></context:component-scan>
    

4.2 常用注解

4.2.1 创建对象的注解
  • @Component:创建所有对象,都可以使用此注解,除了控制器,业务逻辑层、数据访问层的对象。
  • @Controller:创建控制器层的对象,此对象可以接收用户请求,返回处理结果。
  • @Service:创建业务逻辑层的对象,此对象可实施事务控制,向上给控制器返回数据,向下调用数据访问层。
  • @Repository:创建数据访问层的对象,对数据库中的数据进行增删改查。
4.2.2 给对象赋值的注解
  • @Value:给简单类型赋值。
  • @Autowired:给引用类型按类型注入。
  • Qualifier:给引用类型按名称注入。
4.2.3 应用案例
  • 目录结构:
    在这里插入图片描述

  • 实体类如下:

    // School类
    @Component("schoolParent")
    public class School {
          
          
        @Value("合肥大学")
        private String name;
        @Value("合肥市蜀山区")
        private String address;
    
        public School() {
          
          
            System.out.println("School类的构造函数………………");
        }
    
        @Override
        public String toString() {
          
          
            return "School{" +
                    "name='" + name + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    
    
    // Student类
    @Component
    public class Student {
          
          
    
        @Value("砂糖")
        private String name;
        @Value("18")
        private Integer age;
    
        // 引用类型按类型注入
    //    @Autowired
    //    private School school;
    
        // 引用类型按名称注入
        @Autowired
        @Qualifier("school")
        private School school;
    
        @Override
        public String toString() {
          
          
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", school=" + school +
                    '}';
        }
    
        public Student() {
          
          
        }
    }
    
    
    // Student类的子类
    @Component("school")
    public class SubSchool extends School{
          
          
        @Value("合肥168中学")
        private String name;
        @Value("合肥市经开区")
        private String address;
    
        @Override
        public String toString() {
          
          
            return "SubSchool{" +
                    "name='" + name + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    
        public SubSchool() {
          
          
            System.out.println("SubSchool的构造方法…………");
        }
    }
    
  • Spring配置文件(applicationContext.xml)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:component-scan base-package="com.Etui.entity03"></context:component-scan>
    
    </beans>
    
  • 测试代码
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qsjarrfP-1650805242650)(C:\Users\34162\AppData\Roaming\Typora\typora-user-images\image-20220424205031344.png)]

Over!

猜你喜欢

转载自blog.csdn.net/m0_47015897/article/details/124391745