Spring-IoC

IOC底层原理

  1. 什么是IoC?

百度百科:

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

  • 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
  • 使用IoC的目的: 为了耦合度降低

解耦:降低程序间的依赖关系

实际开发中应该做到: 编译期不依赖 实际开发才依赖

  1. IoC底层原理
  • xml解析, 工厂模式, 反射

image-20210228140749786

这种方式 耦合度太高

工厂模式

image-20210228141302278

这种方式,降低了service和dao的耦合

IoC过程

  1. xml配置文件, 配置创建的对象
  2. 有service类和dao类, 创建工厂类(进一步降低耦合度)

image-20210228142114488

IOC接口

  1. IoC思想基于IoC容器完成, Ioc容器底层就是对象工厂

  2. Spring提供IoC容器实现两种方式: (两个接口)

    • BeanFactory(多例对象适用): IoC容器基本实现, 是Spring内部的使用接口, 不提供开发人员进行使用

      使用这个加载配置文件的时候不会创建对象,在获取或者是使用的时候才去创建

    • ApplicationContext(单例对象适用): BeanFactory接口的子接口,提供更多更强大的功能, 一般是开发人员使用的

      加载配置文件的时候就会把你在配置文件中的对象进行创建

      使用这种方式更合适,在启动的时候就完成,把耗时的工作在提前完成而不是在使用的时候

  3. ApplicationContext接口有实现类(两个主要实现类)

    image-20210301225504142

    ClassPath, 就是src下classpath下

    FileSystem就是C盘下某个路径

Bean管理

  1. 什么是Bean管理

    • Bean管理指的是两个操作

    • Spring创建对象

    • Spring注入属性

  2. Bean管理操作有两种方式

IOC操作Bean管理(基于xml)

  1. 在配置文件中使用bean标签
1
<bean id="userService" class="com.atguigu.spring5.service.UserService">
  1. 在bean标签里有很多的属性,介绍常用的属性

    • id属性: 创建对象的名字 给对象取一个别名, 唯一标识
    • class属性: 创建对象所在类的全路径
  2. 创建对象时候 默认执行无参数构造方法

  3. DI: 依赖注入,就是注入属性. 是IoC一种具体实现

第一种注入方式: 使用Set方法

  • 创建类, 定义属性和对应的 set方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Book {
    //创建属性
    private String bname;
    private String bauthor;
    private String address;
    //创建属性对应的set方法
    public void setBname(String bname) {
    this.bname = bname;
    }
    public void setBauthor(String bauthor) {
    this.bauthor = bauthor;
    }

    public void setAddress(String address) {
    this.address = address;
    }

    public void testDemo() {
    System.out.println(bname+"::"+bauthor+"::"+address);
    }
    }
  • 在Spring的配置文件中配置对象创建, 配置属性注入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--2 set方法注入属性-->
    <bean id="book" class="com.atguigu.spring5.Book">
    <!--使用property完成属性注入
    name:类里面属性名称
    value:向属性注入的值
    -->
    <property name="bname" value="易筋经"></property>
    <property name="bauthor" value="达摩老祖"></property>
    </bean>

第二种注入方式: 使用有参数构造进行注入

  • 创建类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Orders {
    //属性
    private String oname="";
    private String address;
    //有参数构造
    public Orders(String oname,String address) {
    this.oname = oname;
    this.address = address;
    }

    public void ordersTest() {
    System.out.println(oname+"::"+address);
    }
    }
  • 配置文件

    1
    2
    3
    4
    <bean id="orders" class="com.atguigu.spring5.Orders">
    <constructor-arg name="oname" value="电脑"></constructor-arg>
    <constructor-arg name="address" value="China"></constructor-arg>
    </bean>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Test
    public void testOrders() {
    //1 加载spring配置文件
    ApplicationContext context =
    new ClassPathXmlApplicationContext("bean1.xml");

    //2 获取配置创建的对象
    Orders orders = context.getBean("orders", Orders.class);

    System.out.println(orders);
    orders.ordersTest();
    }
  1. p名称空间注入(了解)

    • 使用p名称空间注入 第一步添加名称空间

      1
      xmlns:p="http://www.springframework.org/schema/p"
    • 进行属性注入 可以在bean标签里

      1
      2
      <bean id="book" class="com.atguigu.spring5.Book" p:bname="九阳神功" p:bauthor="无名氏">
      </bean>

IOC操作Bean管理(xml注入其他类型属性)

  1. 字面量
  • null值
1
2
3
4
5
6
7
8
9
10
11
12
<bean id="book" class="com.atguigu.spring5.Book">
<!--使用property完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="易筋经"></property>
<property name="bauthor" value="达摩老祖"></property>
<!--null值-->
<property name="address">
<null/>
</property>
</bean>
  • 属性值包含特殊符号

    1
    2
    3
    4
    5
    6
    7
    <!--属性值包含特殊符号
    1 把<>进行转义 &lt; &gt;
    2 把带特殊符号内容写到CDATA
    -->
    <property name="address">
    <value><![CDATA[<<南京>>]]></value>
    </property>
  1. 注入属性-外部bean

    • 创建两个类 service类和dao类
    • 在service调用dao里面的方法
    • 在Spring的配置文件中进行配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <!--1 service和dao对象创建-->
    <bean id="userService" class="com.atguigu.spring5.service.UserService">
    <!--注入userDao对象
    name属性:类里面属性名称
    ref属性:创建userDao对象bean标签id值
    -->
    <property name="userDao" ref="userDaoImpl"></property>
    </bean>
    <bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
    • 测试类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Test
    public void testBean1() {
    //1 加载spring配置文件
    ApplicationContext context =
    new ClassPathXmlApplicationContext("bean2.xml");

    //2 获取配置创建的对象
    UserService userService = context.getBean("userService", UserService.class);

    userService.add();
    }
  2. 注入属性-内部bean和级联赋值

  • 一对多关系: 部门和员工

一个部门有多个员工,一个员工属于一个部门

部门是一,员工是多

  • 在实体类之间表示一对多关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//部门类
public class Dept {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}

@Override
public String toString() {
return "Dept{" +
"dname='" + dname + '\'' +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//员工类
public class Emp {
private String ename;
private String gender;
//员工属于某一个部门,使用对象形式表示
private Dept dept;

//生成dept的get方法
public Dept getDept() {
return dept;
}

public void setDept(Dept dept) {
this.dept = dept;
}
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}

public void add() {
System.out.println(ename+"::"+gender+"::"+dept);
}
}
  • 在Spring的配置文件中配置
1
2
3
4
5
6
7
8
9
10
11
12
<!--内部bean-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--设置对象类型属性-->
<property name="dept">
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="安保部"></property>
</bean>
</property>
</bean>
  • 测试
1
2
3
4
5
6
7
8
9
10
11
@Test
public void testBean2() {
//1 加载spring配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext("bean4.xml");

//2 获取配置创建的对象
Emp emp = context.getBean("emp", Emp.class);

emp.add();
}
  1. 注入属性-级联赋值
1
2
3
4
5
6
7
8
9
10
11
12
<!--级联赋值-->
<bean id="emp" class="com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
<property name="ename" value="lucy"></property>
<property name="gender" value="女"></property>
<!--级联赋值-->
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.atguigu.spring5.bean.Dept">
<property name="dname" value="财务部"></property>
</bean>

IOC操作Bean管理(xml注入集合属性)

  1. 创建实体类,定义数组,list,map
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Stu {
//1 数组类型属性
private String[] courses;
//2 list集合类型属性
private List<String> list;
//3 map集合类型属性
private Map<String,String> maps;
//4 set集合类型属性
private Set<String> sets;

//学生所学多门课程
private List<Course> courseList;
public void setCourseList(List<Course> courseList) {
this.courseList = courseList;
}

public void setSets(Set<String> sets) {
this.sets = sets;
}
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 test() {
System.out.println(Arrays.toString(courses));
System.out.println(list);
System.out.println(maps);
System.out.println(sets);
System.out.println(courseList);
}
}
  1. 配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!--1 集合类型属性注入-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<!--数组类型属性注入-->
<property name="courses">
<array>
<value>java课程</value>
<value>数据库课程</value>
</array>
</property>
<!--list类型属性注入-->
<property name="list">
<list>
<value>张三</value>
<value>小三</value>
</list>
</property>
<!--map类型属性注入-->
<property name="maps">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set类型属性注入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>Redis</value>
</set>
</property>
<!--注入list集合类型,值是对象-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>
  1. 在集合里设置对象类型值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--1 集合类型属性注入-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<!--注入list集合类型,值是对象-->
<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>
  1. 把集合注入部分提取出来
  • 在Spring中引入名称空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

<!--1 提取list集合类型属性注入-->
<util:list id="bookList">
<value>易筋经</value>
<value>九阴真经</value>
<value>九阳神功</value>
</util:list>

<!--2 提取list集合类型属性注入使用-->
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
<property name="list" ref="bookList"></property>
</bean>
</beans>
  • 使用util标签完成list集合注入提取

IOC操作Bean管理(FactoryBean)

  1. Spring有两种类型bean,一种普通bean,一种叫工厂bean(Factorybean)

  2. 普通bean: 在配置文件中定义bean类型就是返回类型

  3. Factorybean: 在配置文件中定义bean类型可以和返回类型不一样

    1. 第一步 创建类 让这个类作为工厂bean,实现接口FactoryBean

    2. 实现接口里的方法,在实现的方法中定义

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      //加上泛型类 返回的对象是Course而不是Mybean
      public class MyBean implements FactoryBean<Course> {

      //定义返回bean
      @Override
      public Course getObject() throws Exception {
      Course course = new Course();
      course.setCname("abc");
      return course;
      }

      @Override
      public Class<?> getObjectType() {
      return null;
      }

      @Override
      public boolean isSingleton() {
      return false;
      }
      }
    3. 配置文件

      1
      2
      <bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
      </bean>
    4. 测试类

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

IOC操作Bean管理(Bean的作用域)

  1. 在Spring里面,设置创建bean实例是单实例还是多实例

  2. 在Spring里面,在默认情况下,bean是一个单实例对象

  3. 如何设置单实例还是多实例

    1. 在Spring配置文件bean标签里有属性(scope)用于设置单实例还是多实例

    2. scope属性值

      第一个 默认值 singleton 表示单实例

      第二个 prototype 多实例

    3. singleton和prototype区别

      第一 singleton单实例 prototype多实例

      第二 设置scope值是singleton时候 加载配置文件的时候就会创建单实例对象

      设置scope的值是prototype的时候,在调用getbean方法的时候创建多实力对象

  4. 其他值 request一次请求 session一次会话

IOC操作Bean管理(Bean的生命周期)

  1. 生命周期

    (1)从对象创建到对象销毁的过程

  2. bean的生命周期

    (1)通过构造器创建bean实例(无参数构造)

    (2)为bean的属性设置值 和 对其他bean的引用(调用Set方法)

    (3)把bean的实例传递给bean后置处理器的方法 postProcessBeforeInitialization

    (4)调用bean的初始化的方法(需要进行配置)

    (5)把bean的实例传递给bean后置处理器的方法postProcessAfterInitialization

    (6)bean可以使用了(对象获取到了)

    (7)当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)

  3. 演示bean的生命周期

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Orders {
    //无参数构造
    public Orders() {
    System.out.println("第一步 执行无参数构造创建bean实例");
    }
    private String oname;
    public void setOname(String oname) {
    this.oname = oname;
    System.out.println("第二步 调用set方法设置属性值");
    }
    //创建执行的初始化的方法
    public void initMethod() {
    System.out.println("第三步 执行初始化的方法");
    }
    //创建执行的销毁的方法
    public void destroyMethod() {
    System.out.println("第五步 执行销毁的方法");
    }
    }

    配置类

    1
    2
    3
    <bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="oname" value="手机"></property>
    </bean>

    测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        @Test
    public void testBean3() {
    // ApplicationContext context =
    // new ClassPathXmlApplicationContext("bean4.xml");
    ClassPathXmlApplicationContext context =
    new ClassPathXmlApplicationContext("bean4.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println("第四步 获取创建bean实例对象");
    System.out.println(orders);

    //手动让bean实例销毁
    context.close();
    }
  4. Bean的后置处理器 bean的生命周期有七步

  5. 演示添加后置处理器效果

    (1)创建类 实现接口BeanPostProcessor,创建后置处理器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("在初始化之前执行的方法");
    return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("在初始化之后执行的方法");
    return bean;
    }
    }

    配置类

    1
    2
    <!--配置后置处理器-->
    <bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>

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

  1. 什么是自动装配

    (1)根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入

  2. 演示自动装配过程

    创建部门与员工类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Emp {
    private Dept dept;
    public void setDept(Dept dept) {
    this.dept = dept;
    }
    @Override
    public String toString() {
    return "Emp{" +
    "dept=" + dept +
    '}';
    }
    public void test() {
    System.out.println(dept);
    }
    }
    1
    2
    3
    4
    5
    6
    public class Dept {
    @Override
    public String toString() {
    return "Dept{}";
    }
    }

    xml配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--实现自动装配
    bean标签属性autowire,配置自动装配
    autowire属性常用两个值:
    byName根据属性名称注入 ,注入值bean的id值和类属性名称一样
    byType根据属性类型注入
    -->
    <bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType">
    <!--<property name="dept" ref="dept"></property>-->
    </bean>
    <bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>

    测试类

    1
    2
    3
    4
    5
    6
    7
    @Test
    public void test4() {
    ApplicationContext context =
    new ClassPathXmlApplicationContext("bean5.xml");
    Emp emp = context.getBean("emp", Emp.class);
    System.out.println(emp);
    }

IOC操作Bean管理(引入外部的属性文件)

  1. 直接配置数据库信息

    (1)配置Druid连接池

    (2)引入Druid依赖

    1
    2
    3
    4
    5
    6
    7
    <!--直接配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
    </bean>
  2. 引入外部属性文件配置数据库连接池

    (1)写外部属性文件,properties 格式文件,写数据库信息

    1
    2
    3
    4
    prop.driverClass=com.mysql.jdbc.Driver
    prop.url=jdbc:mysql://localhost:3306/userDb
    prop.userName=root
    prop.password=root

    (1)配置类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!--引入外部属性文件-->
    <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>