Spring学习第二弹。

Spring-2-入门补充

基于XML的IOC的细节

1. Spring中工厂的类结构图

spring02-framework

1. BeanFactory和ApplicationContext的区别

  • BeanFactory才是Spring容器中的顶层接口。

  • ApplicationContext是它的子接口

  • 创建对象的时间点不一样(区别)

    • ApplicationContext:一读取配置文件,默认就会创建对象。有点像立即加载(使用应用在单列设计模式)。
    • BeanFactory:什么时候使用,什么时候加载。有点像延迟加载(适合多例场景)。

2. ApplicationContext接口的3个实现类

spring中ApplicationContext接口有三个实现类用于3种方式加载配置。

  1. 类根目录

    ClassPathXmlApplicationContext

    资源文件存放在res下。

  2. 磁盘任意目录

    FileSystemXMLApplicationContext

    资源配置文件存放在磁盘的其他地方。

  3. 注解

    AnnotationConfigApplicationContext

1
2
3
4
5
// 方式1
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

// 方式2
ApplicationContext ac = new FileSystemXmlApplicationContext("E:\\IdeaProjects\\spring02_introduction\\src\\main\\resources\\bean.xml");

2. IOC中bean标签和管理对象细节

1. bean标签

作用:

  • 用于配置对象来让spring来创建
  • 默认情况下调用无参构造,如果没有参数则不能创建成功。

属性:

  • id:给在容器中的对象指定唯一标识
  • class:指定类的全限定类名。用来反射创建对象。默认调用无参数构造方法
  • init-method:指定类中初始化方法名称
  • destroy-method:指定类中销毁方法名称
  • scope:指定对象作用的范围
    • singleton:单例(默认)
    • prototype:多例
    • request:WEB项目中,Spring创建的Bean将其存入request域中
    • session:WEB项目中,Spring创建的Bean将其存入session域中
    • global session:WEB项目中,如果有多集群(多台服务器)的情况下,将其存入global session中

2. bean的作用范围和生命周期

  • 单例:scope=“singleton”

  • 一个应用只有一个对象的实例。作用范围:整个引用

  • 生命周期:

    • 出生:应用加载,创建容器时,对象被创建。
    • 活着:容器存在,就一直活着。
    • 销毁:应用卸载,容器销毁,对象被销毁。
  • 多例:scope=”prototype”

  • 每次访问对象,都会重新创建对象实例

  • 生命周期

    • 出生:当使用对象时,创建新的对象实例。
    • 活着:对象被使用,一直活着。
    • 死亡:长时间没有,java垃圾回收机制回收

3. 实例化Bean的三种方式

  • 无参构造
  • 静态工厂
  • 实例工厂
  1. 使用无参构造(默认)

    1
    2
    3
    4
    5
    6
    public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();
    public void save() {
    userDao.save();
    }
    }
    1
    <bean id="userService" class="top.tobing.service.impl.UserServiceImpl"></bean>
  2. 使用静态工厂生产对象

    1
    2
    3
    4
    5
    public class StaticFactory {
    public static UserServiceImpl createUserService(){
    return new UserServiceImpl();
    }
    }
    1
    2
    <!--静态工厂生产对象-->
    <bean id="staticUserService" class="top.tobing.factory.StaticFactory" factory-method="createUserService"></bean>
  3. 使用实例工厂生产对象

    1
    2
    3
    4
    5
    public class InstanceFactory {
    public UserServiceImpl createUserService(){
    return new UserServiceImpl();
    }
    }
    1
    2
    3
    <!--实例工厂生产对象-->
    <bean id="instanceUserService" class="top.tobing.factory.InstanceFactory"></bean>
    <bean id="userServiceI" factory-bean="instanceUserService" factory-method="createUserService"></bean>

3. Spring的依赖注入

  • 依赖注入,Dependency Injection(简称DJ)。它是spring框架核心ioc的具体实现。

  • 在编写程序时。我们通过Ioc(控制反转)把对象创建的控制权交给了spring。

  • 代码中不可能完全没有依赖,IOC解耦只是降低他们的依赖关系,不可能完全消除依赖关系,例如:业务层仍然需要调持久层。

  • 在使用了spring之后,我们连通把这种依赖也交给了spring维护。

  • 就是说,我们只需让框架把持久层对象传入业务层,而不是我们自己获取。

1. 构造函数注入

  • 通过使用类中构造函数,给成员变量赋值。
  • 赋值的过程是使用配置文件的方式(如xml),让spring帮我们注入。
  • constructor-arg标签
  1. 重写UserServiceImpl类用于演示依赖注入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class UserServiceImpl implements UserService {
    private String name;
    private Integer age;
    private Date birthday;

    public UserServiceImpl(String name, Integer age, Date birthday) {
    this.name = name;
    this.age = age;
    this.birthday = birthday;
    }

    public void save() {
    System.out.println("Save method!"+name+","+age+","+birthday);
    }
    }
  2. 编写bean.xml,给上述service层的对象传值。

    1
    2
    3
    4
    5
    6
    <bean id="userService" class="top.tobing.service.impl.UserServiceImpl">
    <constructor-arg name="name" value="张三"></constructor-arg>
    <constructor-arg name="age" value="19"></constructor-arg>
    <constructor-arg name="birthday" ref="bir"></constructor-arg>
    </bean>
    <bean id="bir" class="java.util.Date"></bean>
  3. 通过调用service层UserServiceImpl的save方法看是否注入成功

    1
    2
    3
    4
    5
    6
    7
    8
    public class UserUI {
    public static void main(String[] args) {
    // 1. 使用ApplicationContext接口(获取spring容器)
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    UserService userService = (UserService) ac.getBean("userService");
    userService.save();
    }
    }

    结果:Save method!张三,19,Wed Dec 11 17:31:25 CST 2019

  4. 依赖注入对应关系

    spring2-依赖注入

  5. 构造函数依赖注入涉及标签

    • constructor-arg属性:
    • ——————————-给谁赋值———————————–
    • index:指定参数在构造函数参数列表的索引位置
    • type:指定参数在构造函数中的数据类型
    • name:指定参数在构造函数中的名称(实际中最常用)
    • ——————————-赋什么赋值——————————–
    • value:基本数据类型和String
    • ref:赋值可以是其他bean,也就是说在配置文件中的其他bean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <beans>
    <bean id="userService" class="top.tobing.service.impl.UserServiceImpl">
    <!--给参数列表中,属性名为name的赋值-->
    <constructor-arg name="name" value="张三"></constructor-arg>
    <!--给参数列表中,数据类型为Integer的参数赋值-->
    <constructor-arg type="java.lang.Integer" value="19"></constructor-arg>
    <!--给参数类表中,索引为2,(即第3个参数)赋值-->
    <!--ref用于引用id为bir的另外一个bean-->
    <constructor-arg index="2" ref="bir"></constructor-arg>
    </bean>
    <bean id="bir" class="java.util.Date" ></bean>
    </beans>

2. set方法注入

通过类中setter方法注入

property标签

  1. 重写UserServiceImpl

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class UserServiceImpl implements UserService {
    private String name;
    private Integer age;
    private Date birthday;
    public void setName(String name) {
    this.name = name;
    }
    // 省略其余setter
    public void save() {
    System.out.println("Save method!"+name+","+age+","+birthday);
    }
    }
  2. 编写配置文件建议对应关系,注入

    1
    2
    3
    4
    5
    6
    7
    8
    <beans>
    <bean id="userService" class="top.tobing.service.impl.UserServiceImpl">
    <property name="age" value="20"></property>
    <property name="name" value="tobing"></property>
    <property name="birthday" ref="bir"></property>
    </bean>
    <bean id="bir" class="java.util.Date" ></bean>
    </beans>
  3. 测试

    1
    2
    3
    4
    // 1. 使用ApplicationContext接口(获取spring容器)
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    // 2. 调用方法
    UserService userService = (UserService) ac.getBean("userService");

    Save method!tobing,20,Wed Dec 11 18:27:46 CST 2019

3. p名称空间注入(本质调用setter方法)

通过在xml中导入p名称空间,使用p:注入属性名称=xxx来注入数据。

  1. 编写service层(和set方法一致)

  2. 编写配置文件

    注意导入p空间: xmlns:p=”http://www.springframework.org/schema/p"

    注意非基本数据类型和String要使用:属性名-ref 来注入。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userService" class="top.tobing.service.impl.UserServiceImpl" p:age="18" p:name="Rongon" p:birthday-ref="bir"></bean>
    <bean id="bir" class="java.util.Date" ></bean>
    </beans>

4. 注入集合属性

通过setter,给集合成员(数组、List、Set、Map、Properties)传值。

  1. 给service层添加集合元素用于测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class UserServiceImpl implements UserService {
    private String[] myStrs;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myPropers;
    public void setMyStrs(String[] myStrs) {
    this.myStrs = myStrs;
    }
    // setter
    public void save() {
    System.out.println(Arrays.toString(myStrs));
    System.out.println(myList);
    System.out.println(mySet);
    System.out.println(myMap);
    System.out.println(myPropers);
    }
    }
  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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    <bean id="userService" class="top.tobing.service.impl.UserServiceImpl">
    <!--字符数组-->
    <property name="myStrs">
    <set>
    <value>AAA</value>
    <value>BBB</value>
    <value>CCC</value>
    <value>DDD</value>
    </set>
    </property>
    <!--List-->
    <property name="myList">
    <list>
    <value>List1</value>
    <value>List2</value>
    <value>List3</value>
    <value>List4</value>
    </list>
    </property>
    <!--Set-->
    <property name="mySet">
    <set>
    <value>Set1</value>
    <value>Set2</value>
    <value>Set3</value>
    <value>Set4</value>
    </set>
    </property>
    <!--Properties-->
    <property name="myPropers">
    <props>
    <prop key="testA">aaa</prop>
    <prop key="testB">bbb</prop>
    </props>
    </property>
    <!--Map-->
    <property name="myMap">
    <map>
    <entry key="mapA" value="aaa"></entry>
    <entry key="mapB" value="bbb"></entry>
    <entry key="mapC">
    <value>CCC</value>
    </entry>
    </map>
    </property>
    </bean>
  2. 测试

    1
    2
    3
    4
    5
    6
    7
    8
    public class UserUI {
    public static void main(String[] args) {
    // 1. 使用ApplicationContext接口(获取spring容器)
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    UserService userService = (UserService) ac.getBean("userService");
    userService.save();
    }
    }
    1
    2
    3
    4
    5
    [AAA, BBB, CCC, DDD]
    [List1, List2, List3, List4]
    [Set1, Set2, Set3, Set4]
    {mapA=aaa, mapB=bbb, mapC=CCC}
    {testB=bbb, testA=aaa}
  3. 注意

    在集合数据注入时,同一种集合元素可以共用一种标签,例如:

    • List结构:
      • array、list、set
    • Map结构:
      • map、entry、props、prop

评论