Spring学习笔记01

Spring 概述

Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架

程序的耦合以及解耦

我们在开发中,有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。

示例代码:

1
2
3
4
5
6
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDaoImpl();
}

上面的代码表示:

业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种编译期依赖关系,应该在我们开发中杜绝。我们需要优化代码解决。

再比如:

早期我们的 JDBC 操作,注册驱动时,我们为什么不使用 DriverManagerregister 方法,而是采 用 Class.forName 的方式?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.conv.jdbc;

public class JdbcDemo1 {

public static void main(String[] args) throws Exception {
//1. 注册驱动
// DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring?serverTimezone=GMT%2B8",
"root", "4869");
//3. 获取操作数据库的预处理对象
PreparedStatement pstm = connection.prepareStatement("select * from account");
//4. 执行sql,得到结果集
ResultSet rs = pstm.executeQuery();
while (rs.next()){
System.out.println(rs.getString("name"));
}
rs.close();
pstm.close();
connection.close();
}
}

原因就是:

我们的类依赖了数据库的具体驱动类(MySQL),如果这时候更换了数据库品牌(比如 Oracle),需要修改源码来重新数据库驱动。这显然不是我们想要的。

若没有 mysql 的驱动包,registerDriver 方法会在编译器报错,Class.forName 方式在编译期不依赖,运行时才依赖

解耦的思路

  1. 通过反射来创建对象,而避免使用 new 关键字

    Class.forName("com.mysql.jdbc.Driver");

    这里的 "com.mysql.jdbc.Driver" 只是一个字符串,此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运行就不要想了,没有驱动不可能运行成功的)。

  2. 但是这个字符串也是写死的,改动数据库时依然需要改源码,这样不好。通过读取配置文件来获取要创建的对象全限定类名

    通过读取配置文件来获取要创建的对象的全限定类名

工厂模式解耦

在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。

那么,这个读取配置文件,创建和获取三层对象的类就是工厂。

IOC 的概念

上面的思路还有两个问题:

  1. 存哪去?

    分析:由于我们是很多对象,肯定要找个集合来存。这时候有 Map 和 List 供选择。到底选 Map 还是 List 就看我们有没有查找需求。有查找需求,选 Map。

    所以我们的答案就是:在应用加载时,创建一个 Map,用于存放三层对象。我们把这个 Map 称之为容器。

  2. 什么是工厂?

    工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。

    • 之前:我们在获取对象时,都是采用 new 的方式。是主动的。
    • 现在:我们获取对象时,同时跟工厂要,由工厂为我们查找或者创建对象。是被动的。

    这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。

控制反转(IOC)

控制反转(Inversion of Control,缩写为 IoC ),把创建对象的权力交给框架,是框架的重要特征。是面向对象编程中的一种设计原则,可以用来降低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。

明确 ioc的作用:

  • 削减计算机程序的耦合(解除我们代码中的依赖关系)

Spring 中的 IOC(案例)

由于我们是使用 spring 解决依赖关系,并不是真正的要做增删改查操作,所以此时我们没必要写实体类。并且我们在此处使用的是 java 工程,不是 java web 工程。

创建业务层接口和实现类

IAccountService.java

1
2
3
4
5
6
7
8
9
10
11
12
package com.conv.service;

/**
* 操作账户的业务层接口
*/
public interface IAccountService {
/**
* 模拟保存账户
* 为了简单,不保存到数据库中(没有参数 Account)
*/
void saveAccount();
}

AccountServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.conv.service.impl;

import com.conv.dao.IAccountDao;
import com.conv.dao.impl.AccountDaoImpl;
import com.conv.factory.BeanFactory;
import com.conv.service.IAccountService;

/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService {

private IAccountDao accountDao = new AccountDaoImpl();
// private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");

@Override
public void saveAccount() {
accountDao.saveAccount();
}
}

创建持久层接口和实现类

IAccountDao.java

1
2
3
4
5
6
7
8
9
10
11
package com.conv.dao;

/**
* 账户的持久层接口
*/
public interface IAccountDao {
/**
* 模拟保存账户
*/
void saveAccount();
}

AccountDaoImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.conv.dao.impl;

import com.conv.dao.IAccountDao;

/**
* 账户的持久层实现类
*/
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存了账户");
}
}

模拟表现层调用业务层

Client.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.conv.ui;

import com.conv.factory.BeanFactory;
import com.conv.service.IAccountService;
import com.conv.service.impl.AccountServiceImpl;

/**
* 模拟一个表现层,用来调用业务层
* 实际开发中是一个 Servlet
*/
public class Client {
public static void main(String[] args) {
IAccountService accountService = new AccountServiceImpl();
// IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");

accountService.saveAccount();
}
}

这样可以实现基本功能没有问题。但是我们在表现层调用业务层以及业务层调用实体层时,用到了两个 new,用来创建 Service 和 Dao 对象,这样造成了程序间的耦合,应该避免。

工厂模式解耦

  • 首先,需要一个配置文件来配置我们的 Service 和 Dao

    配置的内容:唯一标识 = 全限定类名(key = value)

  • 接下来,通过读取配置文件中配置的内容,反射创建对象

    配置文件可以是 xml 也可以是 properties

src\main\resources\bean.properties

1
2
accountService=com.conv.service.impl.AccountServiceImapl
accountDao=com.conv.dao.impl.AccountDaoImpl
  • 新建一个用来创建 Bean 对象的工厂

    • Bean: 即可重用组件(比如 Service 可以被很多 Servlet 使用,Dao 可以被很多 Service 使用)

    • JavaBean: 用 Java 语言编写的可重用组件

      JavaBean 大于 实体类(实体类可重用组件的一部分)

src\main\java\com\conv\factory\BeanFactory.java

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
package com.conv.factory;

import java.io.InputStream;
import java.util.Properties;

/**
* 一个创建 Bean 对象的工厂
* 用来创建 Service 和 Dao 对象的
*/
public class BeanFactory {
//定义一个 Properties 对象
private static Properties props;

//使用静态代码块为 Properties 对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
//FileInputStream 的路径不固定,不推荐
//InputStream in = new FileInputStream("")
// 创建在 resources 目录下的文件最后会成为类根路径下的文件,可以不用写任何包名
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
} catch (Exception e) {
// 如果配置文件无法读取,那么什么都做不了,直接报错
throw new ExceptionInInitializerError("初始化 properties 失败");
}
}

/**
* 根据 Bean 的名称获取 bean 对象
* @param beanName
* @return
*/
public static Object getBean(String beanName) {
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}
  • 修改 Client.java 和 AccountServiceImpl.java

    1
    2
    3
    IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");

    private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");

分析工厂模式的问题并改造

bean = Class.forName(beanPath).getDeclaredConstructor().newInstance();

这句话意味着每次都会调用默认构造函数创建对象,每次调用都会重新创建一个新的对象出来,效率较低

单例:只被创建一次,从而类中的成员也就只会初始化一次,但是若有类的成员变量,会有线程问题(所以说 Service 和 Dao 最好不要有可以修改的类成员)

多例:对象被创建多次,执行效率没有单例对象高

所以 newInstance() 只执行一次,创建出来后用 map 存起来,修改过后的工厂类如下:

com\conv\factory\BeanFactory.java

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.conv.factory;

import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
* 一个创建 Bean 对象的工厂
* 用来创建 Service 和 Dao 对象的
*/
public class BeanFactory {
//定义一个 Properties 对象
private static Properties props;

//定义一个 Map,用来存放我们要创建的对象。我们称之为容器
private static Map<String, Object> beans;

//使用静态代码块为 Properties 对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
//FileInputStream 的路径不固定,不推荐
//InputStream in = new FileInputStream("")
// 创建在 resources 目录下的文件最后会成为类根路径下的文件,可以不用写任何包名
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);

//实例化容器
beans = new HashMap<String, Object>();
//取出配置文件中所有的 key
Enumeration keys = props.keys();
// 遍历枚举
while (keys.hasMoreElements()) {
//取出每个key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).getDeclaredConstructor().newInstance();
//把 key 和 value 存入容器中
beans.put(key, value);
}
} catch (Exception e) {
// 如果配置文件无法读取,那么什么都做不了,直接报错
throw new ExceptionInInitializerError("初始化 properties 失败");
}
}

/**
* 根据 Bean 的名称获取 bean 对象
* @param beanName
* @return
*/
public static Object getBean(String beanName) {
return beans.get(beanName);
}
}

由于静态代码块只在最开始执行一次,所以满足了要求

使用 spring 的 IOC 解决程序耦合

Spring 中基于 XML 的 IOC 环境搭建

src\main\resources\bean.xml

1
2
3
4
5
6
7
8
9
10
11
<?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">

<!--把对象的创建交给spring来管理-->
<!--配置 service-->
<bean id="accountService" class="com.conv.service.impl.AccountServiceImpl"></bean>
<!--配置 dao-->
<bean id="accountDao" class="com.conv.dao.impl.AccountDaoImpl"></bean>
</beans>

获取spring的IOC核心容器,并根据id获取对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
//-----------ApplicationContext--------------
//1. 获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//ApplicationContext ac = new FileSystemXmlApplicationContext("E:\\MySpace\\IntelliJ IDEA\\Spring\\day01_spring\\src\\main\\resources\\bean.xml");

//2. 根据id获取Bean对象(以下两种写法均可)
IAccountService as = (IAccountService) ac.getBean("accountService");
IAccountDao ad = ac.getBean("accountDao", IAccountDao.class);

////--------BeanFactory----------
//1. 获取核心容器对象
//Resource resource = new ClassPathResource("bean.xml");
//BeanFactory factory = new XmlBeanFactory(resource);

//2. 根据id获取Bean对象
//IAccountService as = (IAccountService)factory.getBean("accountService");

System.out.println(as);
System.out.println(ad);

//as.saveAccount();
}
  • ApplicationContext 的三个常用实现类:

    • ClassPathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径下
    • FileSystemXmlApplicationContext:可以加载磁盘任意路径下的配置文件(必须有访问权限)(不常用)
    • AnnotationConfigApplicationContext:用于读取注解创建容器(见下节内容)
  • 核心容器的两个接口引发出的问题

    • ApplicationContext: 单例模式适用(实际开发中常用)

      它在构建核心容器时,创建对象采用的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件的对象

    • BeanFactory: 多例对象适用

      它在构建核心容器时,创建对象采取的策略是延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象

      BeanFactory 是 Spring 容器中的顶层接口,ApplicationContext 是它的子接口。

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

spring 对 bean 的管理细节:

  1. 创建 bean 的三种方式

    1. 使用默认构造函数创建

      在spring的配置文件中使用 bean 标签,配以 id 和 class 属性之后,且没有其他属性和标签时,采用的就是默认构造函数创建 bean 对象。此时如果类中没有默认构造函数,则对象无法创建。

      1
      2
      <!--1.使用默认构造函数创建-->
      <bean id="accountService" class="com.conv.service.impl.AccountServiceImpl"></bean>
    2. 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)

      • 首先,模拟一个工厂类(可能位于jar包中)

        com\conv\factory\InstanceFactory.java

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        package com.conv.factory;

        import com.conv.service.IAccountService;
        import com.conv.service.impl.AccountServiceImpl;

        /**
        * 模拟一个工厂类(该类可能存在于jar包中,无法通过修改源码的方式提供默认构造函数)
        */
        public class InstanceFactory {
        public IAccountService getAccountService(){
        return new AccountServiceImpl();
        }
        }
      • 创建对象

        bean.xml

        1
        2
        3
        <!--2.使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)-->
        <bean id="instanceFactory" class="com.conv.factory.InstanceFactory"></bean>
        <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
    3. 使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)

      • 模拟一个静态工厂类(可能位于jar包中)

        com\conv\factory\StaticFactory.java

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        package com.conv.factory;

        import com.conv.service.IAccountService;
        import com.conv.service.impl.AccountServiceImpl;

        /**
        * 模拟一个静态工厂类(该类可能存在于jar包中,无法通过修改源码的方式提供默认构造函数)
        */
        public class StaticFactory {
        public static IAccountService getAccountService(){
        return new AccountServiceImpl();
        }
        }
      • 创建对象

        bean.xml

        1
        2
        <!--3.使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)-->
        <bean id="accountService" class="com.conv.factory.StaticFactory" factory-method="getAccountService"></bean>
  2. bean 对象的作用范围

    • bean 标签的 scope 属性:
      • 作用:用于指定 bean 的作用范围
      • 取值:(常用的是前两个)
        1. singleton:单例的(默认值)
        2. prototype:多例的
        3. request:作用于web应用的请求范围
        4. session:作用于web应用的会话范围
        5. global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
  3. bean 对象的生命周期

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static void main(String[] args) {
    //1. 获取核心容器对象
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");

    //2. 根据id获取Bean对象(以下两种写法均可)
    IAccountService as = (IAccountService) ac.getBean("accountService");

    as.saveAccount();

    //父类对象无法调用子类对象的方法,所以 ac 的类型改为子类对象:ClassPathXmlApplicationContext
    ac.close();
    }
    1
    2
    3
    4
    5
    <!--bean 对象的生命周期-->
    <bean id="accountService" class="com.conv.service.impl.AccountServiceImpl"
    scope="singleton" init-method="init" destroy-method="destroy"></bean>
    <!--<bean id="accountService" class="com.conv.service.impl.AccountServiceImpl"
    scope="prototype" init-method="init" destroy-method="destroy"></bean>-->
    1. 单例对象
      • 出生:当容器创建时对象出生
      • 活着:只要容器还在,对象就一直活着
      • 死亡:容器销毁,对象消亡
      • 总结:单例对象的生命周期和容器相同
    2. 多例对象
      • 出生:当我们使用对象时spring框架为我们创建
      • 活着:对象只要在使用过程中就一直活着
      • 死亡:当对象长时间不用,且没有别的对象引用时,由 Java 的垃圾回收器回收

依赖注入(Dependency Injection)

能注入的数据有三类:

  1. 基本类型和String
  2. 其他 bean 类型(在配置文件中或者注解配置过的 bean)
  3. 复杂类型/集合类型

注入的方式有三种:

  1. 使用构造函数提供(不常用)

    • 使用的标签:constructor-arg

    • 标签出现的位置:bean 标签的内部

    • 标签中的属性:

      • type 用于指定要注入的数据类型,该数据类型也是构造函数中某个或某些参数的类型
      • index 用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始的
      • name 用于指定给构造函数中指定名称的参数赋值(常用)
      • value 用于提供基本类型和 String 类型的数据
      • ref 用于指定其他的 bean 类型的数据。它指的就是在 spring 的 IOC 核心容器中出现过的 bean 对象

      其中,type,index,name 用于指定给构造函数的哪个参数赋值,value,ref 用来赋值

    • 例:

      com\conv\service\impl\AccountServiceImpl.java

      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
      package com.conv.service.impl;

      import com.conv.service.IAccountService;

      import java.util.Date;

      /**
      * 账户的业务层实现类
      */
      public class AccountServiceImpl implements IAccountService {

      //如果是经常变化的数据,并不适用于注入的方式
      //比如注册时用户名等每个人都不一样的,就不适合注入
      //这里的三个变量只是为了举例
      private String name;
      private Integer age;
      private Date birthday;

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

      @Override
      public String toString() {
      return "AccountServiceImpl{" +
      "name='" + name + '\'' +
      ", age=" + age +
      ", birthday=" + birthday +
      '}';
      }

      @Override
      public void saveAccount() {
      System.out.println("service中的saveAccount方法执行了\n" + toString());
      }
      }

      bean.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      <?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">

      <bean id="accountService" class="com.conv.service.impl.AccountServiceImpl">
      <constructor-arg name="name" value="test"></constructor-arg>
      <constructor-arg name="age" value="17"></constructor-arg>
      <constructor-arg name="birthday" ref="now"></constructor-arg>
      </bean>

      <!--配置一个日期对象,上面的 ref=“now” 调取这个对象-->
      <bean id="now" class="java.util.Date">
      <constructor-arg name="year" value="120"></constructor-arg>
      <constructor-arg name="month" value="6"></constructor-arg>
      <constructor-arg name="date" value="7"></constructor-arg>
      </bean>
      </beans>
    • 优势:

      在获取 bean 对象时,注入数据是必须的操作,否则对象无法创建成功

    • 弊端

      改变了 bean 对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。

  2. 使用 set 方法提供(更常用)

    • 使用的标签:property

    • 标签出现的位置:bean 标签的内部

    • 标签中的属性:

      • name 用于指定注入时所调用的set方法的名称
      • value 用于提供基本类型和 String 类型的数据
      • ref 用于指定其他的 bean 类型的数据。它指的就是在 spring 的 IOC 核心容器中出现过的 bean 对象
    • 例:

      com\conv\service\impl\AccountServiceImpl.java

      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
      package com.conv.service.impl;

      import com.conv.service.IAccountService;

      import java.util.Date;

      /**
      * 账户的业务层实现类
      */
      public class AccountServiceImpl implements IAccountService {

      //如果是经常变化的数据,并不适用于注入的方式
      //比如注册时用户名等每个人都不一样的,就不适合注入
      //这里的三个变量只是为了举例
      private String name;
      private Integer age;
      private Date birthday;

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

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

      public void setBirthday(Date birthday) {
      this.birthday = birthday;
      }

      @Override
      public String toString() {
      return "AccountServiceImpl{" +
      "name='" + name + '\'' +
      ", age=" + age +
      ", birthday=" + birthday +
      '}';
      }

      @Override
      public void saveAccount() {
      System.out.println("service中的saveAccount方法执行了\n" + toString());
      }
      }

      bean.xml

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <?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">

      <bean id="accountService" class="com.conv.service.impl.AccountServiceImpl">
      <property name="age" value="21"></property>
      <property name="name" value="TEST"></property>
      <property name="birthday" ref="now"></property>
      </bean>

      <bean id="now" class="java.util.Date">
      <constructor-arg name="year" value="120"></constructor-arg>
      <constructor-arg name="month" value="6"></constructor-arg>
      <constructor-arg name="date" value="7"></constructor-arg>
      </bean>
      </beans>
    • 优势:

      创建对象时没有明确的限制,可以直接使用默认构造函数

    • 弊端

      如果某个对象必须有值,则获取对象时 set 方法有可能没有执行

  3. 使用注解提供(见下节内容)

复杂类型的注入/集合类型的注入

就是给类中的集合成员传值,它用的也是 set 方法注入的方式,只不过变量的数据类型都是集合

  • 用于给 List 结构集合注入的标签有:list,array,set
  • 用于给 Map 结构集合注入的标签有:map,props

结构相同,标签可以互换

例:

com\conv\service\impl\AccountServiceImpl.java

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
47
48
49
50
51
52
53
package com.conv.service.impl;

import com.conv.service.IAccountService;

import java.util.*;

/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService {

private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String, String> myMap;
private Properties myProps;

public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}

public void setMyList(List<String> myList) {
this.myList = myList;
}

public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}

public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}

public void setMyProps(Properties myProps) {
this.myProps = myProps;
}

@Override
public String toString() {
return "AccountServiceImpl{" +
"\nmyStrs=" + Arrays.toString(myStrs) +
"\nmyList=" + myList +
"\nmySet=" + mySet +
"\nmyMap=" + myMap +
"\nmyProps=" + myProps +
'}';
}

@Override
public void saveAccount() {
System.out.println("service中的saveAccount方法执行了\n" + toString());
}
}

bean.xml

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
47
48
49
<?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">

<bean id="accountService" class="com.conv.service.impl.AccountServiceImpl">
<property name="myStrs">
<array>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</array>
</property>

<property name="myList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>

<property name="mySet">
<set>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</set>
</property>

<!--下面是map标签和props标签互换,不影响注入-->
<property name="myMap">
<props>
<prop key="propC">CCC</prop>
<prop key="propD">DDD</prop>
</props>
</property>

<property name="myProps">
<map>
<entry key="mapA" value="AAA"></entry>
<entry key="mapB">
<value>BBB</value>
</entry>
</map>
</property>

</bean>
</beans>
坚持原创技术分享,感谢您的支持和鼓励!