1. IOC底层的原理

1.1 什么是IOC

(1)Inversion of Control 控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
(2)使用 IOC 目的:为了耦合度降低——》eg: 在user类中调用person类中的方法,首先需要在user类中创建person对象,通过person对象调用person类中的方法。
(3)做入门案例就是 IOC 实现。

2.2 IOC的底层原理

(1) xml 解析、工厂模式、反射

2.3 实现过程原理图
图1.png

2. IOC解耦(BeanFactory)

图2.png

2.1 IOC接口

2.1.1 IOC思想基于IOC容器完成,IOC容器底层就是beanFactory

2.1.2 Spring提供IOC容器实现的两种方式(2个接口):

实现原理是:
step1: 加载Spring配置文件;
step2: 获取配置文件中创建的实例。

  • method1: ApplicationContext
    • BeanFactory(工厂类)接口的子接口,功能更加强大。是面向开发人员使用的。
    • 特点: 在加载配置文件时,在配置文件中就会创建对象。——》优点:服务器启动时就创建对象,而不是在操作时才执行
  • method 2: BeanFactory
    • 是spring中内置使用的接口,不提供给开发人员使用。(不建议用)
    • 特点:
      • 加载配置文件的时候,不会创建对象。只有在获取对象的时候,才会创建对象。
        1. public class testDemo {
        2. /** 测试spring创建对象的过程*/
        3. @Test
        4. public void testAdd() {
        5. // step1: 加载spring的配置文件
        6. ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        7. // step2: 获取配置所创建的对象
        8. User user = context.getBean("user", User.class); // 括号中user是id的值,User.class表示把对象转换成User类对象
        9. System.out.println(user); // 输出user对象
        10. user.add(); // user对象调用方法
        11. }
        12. }

        2.1.4 ApplicationContext实现类

        image.png
  1. (1)中需要写的是:配置文件在盘中的路径: c://
  2. (2)中需要写的是:在当前项目中的相对路径

    3. IOC操作-Bean管理1(基于xml)

    3.1 什么是Bean管理?

    | Bean 管理指的是两个操作:
    (1) Spring 创建对象
    (2) Spring 注入属性—->类中有属性。传统的方法是通过类中的set()来给属性设置值。现在这个步骤也交给spring来管理。 | | —- |

3.2 Bean管理操作有2种方式

(1)基于 xml 配置文件方式实现
(2)基于注解方式实现

3.3 基于xml方式创建对象

使用bean标签,添加对应的属性:
——-》执行无参构造方法完成对象的创建。

  1. <!--配置User对象的创建:-->
  2. <!-- id: 对象的表示-->
  3. <!-- class:所要创建的类的全路径(包和类的路径)-->
  4. <!-- name: (了解) 和id的作用一致。 id中不能加/,name中可以加特殊符号。-->
  5. <bean id="user" class="com.atguigu.spring5.User"></bean>

3.4 基于xml方式注入属性

3.4.1 DI

DI: 依赖注入。——》就是注入属性。在创建对象的基础之上执行。
2种实现方式:

  - method1:  通过类中的属性的set()和xml中的properties属性
  - method2:  通过有参构造

3.4.2 多种实现方式

3.4.2.1 基于set()实现

step1: 编写类及属性、属性的set() 方法
step2: xml文件配置
step3: test
 <!-- step1: 注入类及对象   -->
    <bean id="book" class="com.atguigu.spring5.Book">
        <!--        step2: 注入属性-->
        <property name="bAuthor" value="神雕侠侣"></property>
        <property name="bName" value="金庸"></property>

    </bean>
/** */
  @Test
  public void testBook() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Book book = context.getBean("book", Book.class);
    System.out.println(book);
  }

2.4.2.2 基于有参构造函数实现

<bean id="order" class="com.atguigu.spring5.Orders">

        <constructor-arg index="0" value="book的订单"/>
        <constructor-arg index="1" value="京东"/>
    </bean>

3.5 基于xml注入其他类型属性

3.5.1 基本概念

  1. 字面量:给属性设置固定值,这个固定值就是字面量。

    1. 字面量可以是空值null:

      <!-- step1: 注入类及对象   -->
      <bean id="book" class="com.atguigu.spring5.Book">
        <!--        step2: 注入属性-->
        <!--        设置一个空值-->
        <property name="bAuthor">
            <null/>
        </property>
      </bean>
      
    2. 字面量中包含特殊符号:

      <!-- step1: 注入类及对象   -->
      <bean id="book" class="com.atguigu.spring5.Book">
        <!--        step2: 注入属性-->
        <!--        设置值中含有特殊符号-->
        <!--        方法一:&lt;&gt 进行转义-->
        <!--        方法二:CDATA-->
        <property name="bAuthor">
            <value>
                <![CDATA[<<南京!南京!>>]]>
            </value>
        </property>
      </bean>
      

      3.5.2 注入属性—外部bean

      | web层调用service层, service层调用dao层。
      note:
      dao: data access object | | —- |

(1)创建2个类,service类和dao类;
(2) 在service增加dao类作为属性,添加set();
image.png
(3)在spring配置文件中进行配置——property标签

<!--    step1: 创建dao和service对象-->
    <bean id="service" class="com.atguigu.spring5.service.UserService">
        <!--        step2: 在service中注入dao: name: 类中的属性名称, ref指向所创建的dao对象id-->
        <property name="userDao" ref="dao"></property>
    </bean>
    <bean id="dao" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>

(4) 测试

public class TestBean {
  /** */
  @Test
  public void testBean() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");
    UserService service = context.getBean("service", UserService.class);
    service.add();
  }
}

3.5.3 注入属性—内部bean和级联赋值

析: 类A当作类B 的一个属性;
(1) 一对多关系: 部门-员工; 一个部门有多个员工,一个员工属于一个部门。
(2) 在实体类之间表示一对多关系:

public class employee {
    private String eName;
    private String eGender;

    // 员工属于某一个部门
    private Department dept;

    public void seteName(String eName) {
        this.eName = eName;
    }

    public void seteGender(String eGender) {
        this.eGender = eGender;
    }

    public void setDept(Department dept) {
        this.dept = dept;
    }
}

(3) xml中配置属性
== 注意 属性dept的级联赋值;

<!--    step1: 内部bean-->
    <bean id="emp" class="com.atguigu.spring5.bean.employee">
        <!--        step2: 普通的内部属性-->
        <property name="eGender" value="female"></property>
        <property name="eName" value="lucy"></property>
        <!--        step2: 外部bean属性-->
        <property name="dept">
            <bean id="innerDept" class="com.atguigu.spring5.bean.Department">
                <property name="dName" value="保安部"></property>
            </bean>
        </property>
    </bean>

3.6 xml注入集合属性

3.6.1 注入一个array类型属性

3.6.2 注入一个List集合类型的属性

3.6.3 注入Map集合类型属性

step1: 创建student类,并在类中设置各种数据类型的值;

public class Student {
    // 数组类型的属性
    private String[] courses;
    // 创建list类型的数组
    private List<String> list;
    // set类型的属性
    private Set<String> set;

    // map类型的属性
    private Map<String,String> maps;

    public void setCourses(String[] courses) {
        this.courses = courses;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMaps(Map<String, String> maps) {
        this.maps = maps;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    @Override
    public String toString() {
        return "Student{" +
                "courses=" + Arrays.toString(courses) +
                ", list=" + list +
                ", set=" + set +
                ", maps=" + maps +
                '}';
    }

    public void test(){
    System.out.println(Arrays.toString(courses));
    System.out.println(list);
    System.out.println(maps.entrySet());
    }
}

step2: 在xml中注入属性值

<bean id="stu" class="com.atguigu.spring5.collectionType.Student">
        <property name="courses">
            <array>
                <value>java课程</value>
                <value>MySQL课程</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>张三</value>
                <value>三哥</value>
            </list>
        </property>
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="PHP" value="php"></entry>
            </map>
        </property>
        <property name="set">
            <set>
                <value>李四</value>
                <value>王麻子</value>
            </set>
        </property>
    </bean>

3.6.4 如何在集合中设置 对象类型 的值?

析: 对象只能引用!

  <!--    在List集合中注入Course类型的值-->
    <bean id="std" class="com.atguigu.spring5.collectionType.Stu">
        <property name="courseList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>
    </bean>

    <!--    创建多个course对象-->
    <bean id="course1" class="com.atguigu.spring5.collectionType.Course">
        <property name="cName" value="Spring5框架"></property>
    </bean>
    <bean id="course2" class="com.atguigu.spring5.collectionType.Course">
        <property name="cName" value="MyBatis框架"></property>
    </bean>

3.6.5 如何把集合注入的部分提取出来?让其作为公共的部分

step1: 在配置文件中引入util名称空间

xmlns:util="http://www.springframework.org/schema/util"

step2: 使用util标签完成List集合属性注入

 <!--    step1: 提取list集合-->
    <util:list id="stuList">
        <value>易经经</value>
        <value>九阳真经</value>
        <value>乾坤大挪移</value>
    </util:list>
    <!--  step2: list的注入-->
    <bean id="stu" class="com.atguigu.spring5.collectionType.Stu">
        <property name="courseList" ref="stuList"></property>
    </bean>

4. IOC操作-Bean管理2(基于FactoryBean)

4.1 bean概念梳理

Spring中有2种bean: 一种是普通bean, 一种是factoryBean

4.1.1 普通bean

通过xml文件中bean标签创建bean. 实例名就是id。 此时xml中定义的class类型就是返回的实例类型。

4.1.2 FactoryBean

xml中定义的类型可以和返回的类型不一致。

4.2 FactoryBean的实现过程

析: 实质是半手动创建bean的过程。
原因: 在工厂bean中是手动创建对象,而工厂bean的实例则是由xml文件创建的。

4.2.1 step1: 创建类,让这个类作为FactoryBean, 实现接口FactoryBean

4.2.2 step2: 实现接口里的方法,在现实的方法中定义返回的bean类型

具体实现:
1. 类的编写:
定义的是MyBean类,返回的是Course类。

/*
*  在MyBean中创建Couse的对象
*/
public class MyBean implements FactoryBean<Course> {

    /**
     * 返回  couse对象
     * @return
     * @throws Exception
     */
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setcName("spring");
        return course;
    }
}
  1. xml文件的配置:

    <bean id="myBean" class="com.atguigu.spring5.factoryBean.MyBean"></bean>
    
  2. test:

    @Test
     public void test3(){
         ApplicationContext context= new ClassPathXmlApplicationContext("bean3.xml");
         Course course = context.getBean("myBean", Course.class);
         System.out.println(course);
     }
    

    5. Bean管理(作用域)

    5.1 单实例/多实例

    在spring里可以设置是单实例还是多实例;
    在默认情况下,bean是单实例对象;

    5.2 如何设置单实例还是多实例?

    在spring配置文件里,bean标签里的 scope=”prototype” 代表多实例。

    <bean id="myBean" class="com.atguigu.spring5.factoryBean.MyBean" scope="prototype"></bean>   // prototype: 原型
    

    5.3 singleton和prototype区别

  3. singleton单实例,prototype多实例;

  4. 设置scope值是prototype时候,不是在加载spring配置文件时候创建对象,而是在getBean() 时创建多实例对象。

6. Bean管理(生命周期)

6.1 什么是生命周期?

对象从创建到销毁的过程。

6.2 bean的生命周期

  1. 创建bean实例:通过无参/有参构造器创建
  2. 为bean的属性设置值和对其他bean引用(set( ))
  3. 调用bean初始化的方法:需要配置
  4. bean可以使用了
  5. 当容器在关闭的时候,会调用bean销毁的方法: 需要配置

7. IOC操作-Bean管理(xml自动装配)

7.1 自动装配

手动装配:给bean配置属性和属性值;
自动装配:根据指定装配规则(属性名称或者属性类型),Spring会自动将匹配得属性值进行注入。

7.2 演示自动装配过程

7.2.1 method1: 根据属性名称自动注入

 <!--    实现自动装配-->
    <!--    根据属性名称实现自动装配-->
    <bean id="emp" class="com.atguigu.spring5.autoWire.Emp" autowire="byName">
    </bean>

    <bean id="dep" class="com.atguigu.spring5.autoWire.Dep"></bean>

7.2.2 method2: 根据属性类型自动注入

<!--    实现自动装配-->
    <!--    属性类型-->
    <bean id="emp" class="com.atguigu.spring5.autoWire.Emp" autowire="byType">
    </bean>

    <bean id="dep" class="com.atguigu.spring5.autoWire.Dep"></bean>

8. IOC操作-Bean管理(外部属性文件)

场景:操作数据库,读取property文件

8.1 实现步骤

8.1.1 配置数据库信息

  1. 配置德鲁伊连接池
  2. 引入德鲁伊连接池依赖的jar包 | 资源池对象:
    驱动;
    url
    user
    passwd | | —- |

image.png

8.1.2 引入外部属性文件配置数据库连接池

  1. 创建外部属性文件,properties格式文件,写数据库信息

    prop.driverClass=com.mysql.jdbc.Driver
    prop.url=jdbc:mysql://localhost:3306/userDb
    prop.userName=root
    prop.password=root
    
  2. 把外部properties属性文件引入到spring配置文件中。

step1: 首先要引入context名称空间;
step2: 在spring配置文件使用标签引入外部属性文件。

<!--    外部属性文件的引入-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.userName}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>

9. IOC操作-Bean管理(基于注解方式)

9.1 什么是注解

  1. 注解是代码中特殊的标记, 格式:@注解名称(属性名称1=属性值1,属性名称2=属性值2…)
  2. 注解可以用在类上、属性上、方法上
  3. 使用注解的目的:简化xml配置。

9.2 Spring针对Bean管理中创建对象提供的注解

  1. @Component
    1. 普通的注解
  2. @Service
    1. 业务逻辑层上
  3. @Controller
    1. web层
  4. @repository
    1. dao层

9.3 基于注解方式实现对象创建

9.3.1 step1: 引入依赖

image.png

9.3.2 step2: 开启组件扫描

告诉Spring,在哪些类上会使用注解。让Spring去扫描这些类。

  1. 引入context名称空间

xmlns:context=”http://www.springframework.org/schema/context

  1. 指定需要开启组件扫描的包目录:

    <context:component-scan base-package="com.atguigu.spring5">
    </context:component-scan>
    
  2. 创建类,通过注解创建对象

    // 注解中value="" 可以不写
    // 不写时,默认值是类名的首字母小写
    @Component(value = "userService")  // 此写法等同于在xml中注入bean:  <bean id="" class=""
    public class UserService {
    public void add() {
     System.out.println("service add.....");
    }
    }
    
  3. 测试

    public class TestDemo {
     /** */
     @Test
     public void testService() {
         ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
         UserService userService = context.getBean("userService", UserService.class);
         System.out.println(userService);
         userService.add();
     }
    }
    

    9.4 基于注解的方式实现属性注入

    9.4.1 Spring提供了哪些注解

  4. @AutoWired

    1. 根据属性类型进行注入
    2. step1: 把service和dao类上添加创建对象的注解;
    3. step2: 在service中创建dao类型的属性。使用@Autowired进行注解。
      // 定义Dao类型的属性, 不需要添加set方法,添加注入属性注解
      @Autowired
      private UserDao userDaoImp;
      
  5. @Qualifier

    1. 根据属性名称进行注入,要和autowired一起使用。
      // 定义Dao类型的属性, 不需要添加set方法,添加注入属性注解
      @Autowired
      @Qualifier(value = "userDaoImp1")  // 避免多个接口实现类对象间的混淆
      private UserDao userDaoImp;
      
  6. @Resource

    1. 既可以根据类型又可以根据名称进行注入
    2. 是javax包中的,不是Spring中的
    3. 不建议使用 ```java @Resource //根据类型进行注入 private UserDao userDaoImp;

@Resource(name = “userDaoImp1”) // 根据名字进行注入 private UserDao userDaoImp;


4. @Value
   1. 注入普通类型属性
```java
 @Value(value = "abc")  // 把值“abc”注入到属性中
  private String name;

10. 纯注解开发

10.1 step1: 创建配置类,替代xml配置文件

需要使用@Configration 和 @ComponentScan(basePackages={“包的目录”})

@Configuration  // 把当前类作为配置类
@ComponentScan(basePackages = {"com.atguigu"})  // 开始组件扫描
public class SpringConfig {

}

10.2 step2: 编写类和属性,以及注入

10.3 step3: 测试

注意:加载配置类的方法: AnnotationConfigApplicationContext

ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();