spring-IOC(DI)-控制反转、依赖注入

IoC思想

  • IoC:Inversion of Control,控制反转。是指 把创建对象的控制权交给框架:要想得到一个对象,由原来的主动创建对象,变成自己被动接收 框架创建的对象
  • IoC是Spring的核心思想之一
    作用:用于降低程序间的耦合性

控制反转IoC

1. 快速入门

需求描述
  • UserDao接口和UserDaoImpl实现类
  • 通过Spring容器得到UserDaoImpl的实例对象(IoC方式)
开发步骤
  1. 创建Maven项目,导入依赖坐标:Spring的依赖坐标
  2. 编写dao接口UserDao及实现UserDaoImpl
  3. 创建Spring核心配置文件,并配置UserDaoImpl
  4. 测试:使用Spring的API,获取Bean实例对象
需求实现
1. 创建Maven项目,导入依赖坐标
<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.1.2.RELEASEversion>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
    dependency>
dependencies>
2. 编写dao接口UserDao及实现UserDaoImpl
  • 接口UserDao
public interface UserDao {
    void save();
}
  • 实现类UserDaoImpl
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDaoImpl.save......");
    }
}
3. 创建Spring核心配置文件,并配置UserDaoImpl
  • 配置文件名称,通常叫applicationContext.xml

<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="userDao" class="com.itheima.dao.impl.UserDaoImpl">bean>
beans>
4. 使用Spring的API,获取Bean实例对象
  • 编写测试类
public class SpringIocQuickStartTest {
    @Test
    public void test(){
        ApplicationContext context = 
            new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = context.getBean("userDao", UserDao.class);
        userDao.save();
    }
}
小结
  1. 导入依赖:spring-context

  2. 编写自己的业务代码:UserDao, UserDaoImpl

  3. 创建配置文件:applicationContext.xml

    <bean id="唯一标识" class="全限定类名">bean>
    
  4. 创建容器,加载配置文件,从容器里获取bean对象

    ApplicationContext app = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
    
    Object obj = app.getBean("唯一标识");
    

2. 配置文件详解

1. bean标签的基本配置
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">bean>
  1. 介绍

    • 用于配置:把对象交给Spring进行控制

    • 默认情况下,Spring是调用类的无参构造来创建对象的;如果没有无参构造,则不能创建成功

  2. 基本属性

    • id:唯一标识
    • class:bean的全限定类名

    了解:bean的id和name的区别

    1. 一个bean只能有一个id;一个bean可以有多个name
    2. bean的name值:多个name之间以, ; 空格 隔开,第1个name作为id,其它作为别名
2. bean标签的作用范围配置
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" scope="singleton">bean>
  • scope属性取值如下:
取值 说明
singleton 默认,表示单例的,一个Spring容器里,只有一个该bean对象
prototype 多例的,一个Spring容器里,有多个该bean对象
request web项目里,Spring创建的bean对象将放到request域中:一次请求期间有效
session web项目里,Spring创建的bean对象将放到session域中:一次会话期间有效
globalSession web项目里,应用在Portlet环境/集群环境;如果没有Portlet/集群环境,那么globalSession相当于session(新版本中已删除)
  • 不同scope的bean,生命周期:

    • singleton:bean的生命周期和Spring容器的生命周期相同

      • 整个Spring容器中,只有一个bean对象
      • 何时创建:加载Spring配置文件,初始化Spring容器时,bean对象创建
      • 何时销毁:Spring容器销毁时,bean对象销毁
    • prototype:bean的生命周期和Spring容器无关。Spring创建bean对象之后,交给JVM管理了

      • 整个Spring容器中,会创建多个bean对象,创建之后由JVM管理
  • 何时创建:调用getBean方法获取bean对象时,bean对象创建

    • 何时销毁:对象长时间不用时,垃圾回收
3. bean生命周期相关方法的配置【了解】
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" 
      init-method="" destroy-method="">bean>
  • init-method:指定类中初始化方法名称,该方法将在bean对象被创建时执行

  • destroy-method:指定类中销毁方法名称,该方法将在bean对象被销毁时执行

    注意:

    • prototype类型的bean:Spring容器销毁时,也不会执行销毁方法,因为Spring不负责它的销毁
    • singleton类型的bean:在Spring容器显式关闭时,会执行destroy-method指定的方法
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    UserDao userDao = context.getBean("userDao", UserDao.class);
    //显式的关闭Spring容器
    ((ClassPathXmlApplicationContext)context).close();
    
4. bean实例化的三种方式
  • 无参构造方法实例化,默认的:让Spring调用bean的无参构造,生成bean实例对象给我们
  • 工厂静态方法实例化:让Spring调用一个工厂类的静态方法,得到一个bean实例对象
  • 工厂非静态方法实例化(实例化方法):让Spring调用一个工厂对象的非静态方法,得到一个bean实例对象
1. 无参构造方法实例化
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">bean>
2. 工厂静态方法实例化
  • 工厂类如下:com.itheima.factory.StaticFactory
public class StaticFactory{
    public static UserDao createUserDao(){
        return new UserDaoImpl();
    }
}
  • 配置如下:
<bean id="userDao" class="com.itheima.factory.StaticFactory" 
      factory-method="createUserDao">bean>
3. 工厂非静态方法实例化
  • 工厂类如下:com.itheima.factory.InstanceFactory
public class InstanceFactory{
    public UserDao createUserDao(){
        return new UserDaoImpl();
    }
}
  • 配置如下:

<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory">bean>


<bean id="userDao" factory-bean="instanceFactory" factory-method="createUserDao">bean>

3. IoC小结

  • bean标签的基本配置
<bean id="唯一标识" class="全限定类名">bean>
  • bean对象的作用范围。使用bean标签的scope属性进行设置的

    • singleton:默认的,单例的。
      • 何时创建:容器初始化时
      • 何时销毁:容器关闭时
      • 生命周期:非懒加载的单例bean对象和容器同生共死
    • prototype:多例的
      • 何时创建:获取bean对象时
      • 何时销毁:长时间不使用,JVM垃圾回收
  • bean对象生命周期相关的方法配置:

    • 给bean标签增加init-method属性,用于指定一个初始化方法
      • 当bean对象被创建之后,会执行一次
    • 给bean标签增加destroy-method属性,用于指定一个销毁方法
      • 当单例bean被销毁前,会执行一次
      • 多例bean销毁时,是不会执行销毁方法(因为Spring不管理多例bean对象)
  • bean实例化方式:

    • 默认的无参构造
    <bean id="" class="">bean>
    
    • 工厂的静态方法:
      • 如果要得到一个类的代理对象,就可以使用工厂的静态方法
    <bean id="" class="工厂类全限定类名" factory-method="生成对象的静态方法">bean>
    
    • 工厂的非静态方法:
      • 如果要得到一个类的代理对象,就可以使用工厂的非静态方法
    <bean id="" factory-bean="工厂bean对象" factory-method="生成对象的非静态方法">bean>
    
    <bean id="工厂bean" class="工厂bean的全限定类名">bean>
    

依赖注入DI

  • 依赖注入:Dependency Injection,是Spring的Ioc核心的具体实现。

    • 类里依赖什么,由Spring注入(提供)什么
  • 我们需要进行配置:告诉Spring,依赖什么

    我们通过Ioc把bean对象交给了Spring容器进行管理,降低了耦合性。

    但是耦合性不能彻底消除,bean之间还是有一些依赖关系。比如:业务层userService要依赖于持久层userDao。

    这样的依赖关系,可以交给Spring帮我们进行依赖的注入,而不用我们自己注入依赖

快速入门

需求描述
  • 有dao层:UserDaoUserDaoImpl
  • 有service层:UserServiceUserServiceImpl
  • UserServiceImpl中的方法依赖于UserDaoImpl
  • 使用Spring,把UserDaoImpl注入给UserServiceImpl
开发步骤
  1. 创建Maven项目,导入依赖坐标
  2. 编写dao层UserDaoUserDaoImpl、service层UserServiceUserServiceImpl
  3. 创建Spring核心配置文件,并配置bean和依赖注入
  4. 使用Spring的API,测试
需求实现
1. 创建Maven项目,导入依赖
<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.1.2.RELEASEversion>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
    dependency>
dependencies>
2. 编写dao层和service层代码
  • dao层接口UserDao
public interface UserDao {
    void save();
}
  • dao层实现类UserDaoImpl
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDaoImpl.save......");
    }
}
  • service层接口UserService
public interface UserService {
    void save();
}
  • service层实现类UserServiceImpl
public class UserServiceImpl implements UserService {
    //依赖于dao层的UserDao,定义一个成员变量
    private UserDao userDao;

    @Override
    public void save() {
        userDao.save();
    }
    
    //提供userDao的get/set方法
    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
3. 创建Spring核心配置文件,并配置bean和依赖注入

<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="userDao" class="com.itheima.dao.impl.UserDaoImpl">bean>
    
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
        
        <property name="userDao" ref="userDao"/>
    bean>
beans>
4. 使用Spring的API,测试
public class UserTest {
    public static void main(String[] args) {
        ApplicationContext context = 
            new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.save();
    }
}
小结
  • UserServiceImpl里有userDao,需要注入UserDaoImpl对象

    1. 修改userServiceImpl,里边增加userDao属性
    private UserDao userDao;
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    
    1. 修改xml配置文件
    <bean id="" class="UserServiceImpl全限定类名">
    	<property name="userDao" ref="要注入的bean对象"/>
    bean>
    

2. 三种常见注入方式

set方法注入
1) 介绍

在类中提供需要注入的成员(依赖项)的set方法,在配置文件中注入属性的值

<bean id="" class="">
	<property name="属性名" value="属性值">property>
    <property name="属性名" ref="bean的id">property>
bean>
  • property标签:用在bean标签内部,表示要给某一属性注入数据
    • name:属性名称
    • value:要注入的属性值,注入简单类型值
    • ref:要注入的属性值,注入其它bean对象

优势:创建bean对象时没有明确的限制,可以使用无参构造直接创建

缺点:如果某个成员必须有值,则获取对象时,有可能set方法未执行

需求实现
1. 创建Maven项目,导入依赖
<dependencies>
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>5.1.2.RELEASEversion>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
    dependency>
dependencies>
2. 编写dao层和service层代码
  • dao层接口UserDao
public interface UserDao {
    void save();
}
  • dao层实现类UserDaoImpl
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDaoImpl.save......");
    }
}
  • service层接口UserService
public interface UserService {
    void save();
}
  • service层实现类UserServiceImpl
public class UserServiceImpl implements UserService {
    //依赖于dao层的UserDao,定义一个成员变量
    private UserDao userDao;

    @Override
    public void save() {
        userDao.save();
    }
    
    //提供userDao的get/set方法
    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
3. 创建Spring核心配置文件,并配置bean和依赖注入

<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="userDao" class="com.itheima.dao.impl.UserDaoImpl">bean>
    
    <bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
        
        <property name="userDao" ref="userDao"/>
    bean>
beans>
4. 使用Spring的API,测试
public class UserTest {
    public static void main(String[] args) {
        ApplicationContext context = 
            new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.save();
    }
}
2) 示例
构造方法注入< constructor-arg >
1) 介绍

在类中提供构造方法,构造方法的每个参数就是一个依赖项,通过构造方法给依赖项注入值。

<bean id="" class="">
	<constructor-arg name="构造参数名称" value="构造参数的值">constructor-arg>
    <constructor-arg name="构造参数名称" ref="bean的id">constructor-arg>
bean>
  • name:构造参数的名称
  • type:构造参数的类型
  • index:构造参数的索引
  • value:要注入的值,注入简单类型值
  • ref:要注入的值,注入其它bean对象

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

缺点:改变了bean对象的实例化方式,如果在创建对象时用不到这些数据,也必须要提供

2) 示例
p名称空间注入
1) 介绍

p名称空间注入,本质仍然是set方法注入

在xml中引入p名称空间的约束

然后通过p:属性名称=""来注入简单数据、使用p:属性名称-ref=""注入其它bean对象,它的本质仍然是set方法注入

<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="" class="" p:属性名="简单值" p:属性名-ref="bean的id">bean>
    
beans>
2) 示例
小结
  1. 通过set方法注入(注入属性)

    • bean里的依赖项,必须有set方法
    • 在xml的bean标签里,使用property标签注入每个属性值
    <bean id="" class="" >
    	<property name="属性名称" value="注入简单值"/>
        <property name="属性名称" ref="注入其它bean对象"/>
    bean>
    
  2. 通过构造方法注入

    • bean里必须有构造方法。构造方法里每个参数,就是一个依赖项
    • 在xml的bean标签里,使用constructor-arg标签注入每个构造参数的值
    <bean id="" class="">
    	<constructor-arg name="属性名称" value="注入简单值"/>
        <constructor-arg name="属性名称" ref="注入其它bean对象"/>
    bean>
    
  3. 通过p名称空间注入

    • 本质还是set方法注入,只是xml里的配置的语法不同
    • bean里每个依赖项必须有set方法
    • 在xml里:
      • 引入p名称空间
      • bean标签上,使用p:属性名或者 p:属性名-ref注入值
    <bean id="" class="" p:属性名="注入简单值" p:属性名-ref="注入其它bean对象">bean>
    

3. 注入集合数据

介绍
  • 前边我们介绍了如何注入简单数据类型和bean对象,但是在实际开发中,可能会需要给集合属性注入数据,比如:给数组、List、Set、Map等注入数据
示例
UserDaoImpl需要注入数据
public class UserDaoImpl implements UserDao {
    private String[] arr;
    private List<String> list;
    private Set<String> set;
    private Map<String,String> map;
    private Properties properties;

    // get/set...

    public void show(){
        System.out.println(Arrays.toString(this.arr));
        System.out.println(this.list);
        System.out.println(this.set);
        System.out.println(this.map);
        System.out.println(this.properties);
    }
}
配置注入数据
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
    
    <property name="arr">
        <array>
            <value>avalue>
            <value>bvalue>
            <value>cvalue>
        array>
    property>
    
    <property name="list">
        <list>
            <value>a1value>
            <value>b1value>
            <value>c1value>
        list>
    property>
    
    <property name="set">
        <set>
            <value>a2value>
            <value>b2value>
            <value>c2value>
        set>
    property>
    
    <property name="map">
        <map>
            <entry key="a3" value="a3"/>
            <entry key="b3" value="b3"/>
            <entry key="c3" value="c3"/>
        map>
    property>
    
    <property name="properties">
        <props>
            <prop key="a4">a4prop>
            <prop key="b4">b4prop>
            <prop key="c4">c4prop>
        props>
    property>
bean>

所有单列结构的数据集合,标签可以互换使用。例如:List、Set、数组等

所有键值对结构的数据集合,标签可以互换使用。例如:Map、Properties等

小结
  • 给数组、List、Set属性注入数据,使用:array/list/set中任一标签均可
  • 给Map、Properties属性中注入数据,使用:map/props中任一标签均可

相关API介绍

1. ApplicationContext的继承体系

  • ApplicationContext:接口,代表应用上下文,可以通过其实例对象获取Spring容器中的bean对象

spring-IOC(DI)-控制反转、依赖注入_第1张图片

2. ApplicationContext

2.1 BeanFactoryApplicationContext的区别
  • ApplicationContext 是现在使用的工厂

    ApplicationContext context = 
        new ClassPathXmlApplicationContext("applicationContext.xml");
    
  • XmlBeanFactory是老版本使用的工厂,目前已经被废弃【了解】

    BeanFactory beanFactory = 
        new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
    
  • 两者的区别:

    • ApplicationContext加载方式是框架启动时就开始创建所有单例的bean,存到了容器里面

    • BeanFactory加载方式是用到bean时再加载(目前已经被废弃)

2.2 ApplicationContext的实现类
2.2.1 ClassPathXmlApplicationContext
  • 从类加载路径里,加载xml配置文件
  • 什么是类加载路径:代码编译之后的那个classes文件夹,
    • 开发中可以认为Maven项目的:Java文件夹、resources文件夹,都是类加载路径
2.2.2 FileSystemXmlApplicationContext
  • 从磁盘路径里,加载xml配置文件
2.2.3 AnnotationConfigApplicationContext
  • 用注解配置Spring时,通过此类加载配置类创建Spring容器,它用于读取类上的注解配置
2.3 getBean()方法
  • ApplicationContext提供了多种getBean方法的重载,常用的如下:
方法 参数 返回值
getBean(String beanId) bean的id Object,bean对象
getBean(String beanId,Class beanType) bean的Class类型 bean对象
getBean(Class beanType) bean对象

CURD练习

需求描述

  • 完成帐户信息的增、删、改、查操作,要求使用Spring对service层和dao层解耦

需求分析

  1. 准备工作:
    • 创建Maven的Java项目,配置坐标,引入依赖
    • 创建JavaBean
  2. 编写代码:
    • 创建service和dao的接口和实现类,并添加上:查询全部、添加帐号、修改帐号、删除帐号的功能
  3. 配置文件:
    • 创建Spring核心配置文件,配置所有的bean
  4. 测试
    • 创建单元测试类,测试功能是否正常

需求实现

3.1 准备工作
  1. 创建Maven的Java项目,项目坐标自定,然后引入依赖如下:

    <dependencies>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.47version>
        dependency>
        
        
        <dependency>
            <groupId>com.mchangegroupId>
            <artifactId>c3p0artifactId>
            <version>0.9.5.2version>
        dependency>
        
        
        <dependency>
            <groupId>commons-dbutilsgroupId>
            <artifactId>commons-dbutilsartifactId>
            <version>1.7version>
        dependency>
        
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.2.RELEASEversion>
        dependency>
        
        
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>
    
  2. 创建JavaBean:Account类如下:

    public class Account {
        private Integer id;
        private String name;
        private Float money;
    
        //get/set...
        //toString...
    }
    
3.2 编写代码
1) Service层代码如下:
  1. Service层接口:AccountService

    public interface AccountService {
        List<Account> queryAll() throws SQLException;
        
        void save(Account account) throws SQLException;
        
        void edit(Account account) throws SQLException;
        
        void delete(Integer id) throws SQLException;
    }
    
  2. Service实现类:AccountServiceImpl

    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
    
        /***************业务功能方法*****************/
        
        @Override
        public List<Account> queryAll() throws SQLException {
            return accountDao.queryAll();
        }
        
        @Override
        public void save(Account account) throws SQLException {
            accountDao.save(account);
        }
        
        @Override
        public void edit(Account account) throws SQLException {
            accountDao.edit(account);
        }
        
        @Override
        public void delete(Integer id) throws SQLException {
            accountDao.delete(id);
        }
    
        
        /***************get/set方法*****************/
        public AccountDao getAccountDao() {
            return accountDao;
        }
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    }
    
2) dao层代码如下:
  1. dao层接口:AccountDao

    public interface AccountDao {
        List<Account> queryAll() throws SQLException;
        
        void save(Account account) throws SQLException;
        
        void edit(Account account) throws SQLException;
        
        void delete(Integer id) throws SQLException;
    }
    
  2. dao实现类:AccountDaoImpl

    public class AccountDaoImpl implements AccountDao {
    
        private QueryRunner runner;
    
        /***************功能方法*****************/
        
        @Override
        public List<Account> queryAll() throws SQLException {
            return runner.query("select * from account", new BeanListHandler<>(Account.class));
        }
        
        public void save(Account account) throws SQLException{
            runner.update("insert into account (id,name,money) values (?,?,?)", account.getId(), account.getName(), account.getMoney());
        }
        
        public void edit(Account account) throws SQLException{
            runner.update("update account set name = ?, money = ? where id = ?", account.getName(), account.getMoney(), account.getId());
        }
        
        public void delete(Integer id) throws SQLException{
            runner.update("delete from account where id = ?", id);
        }
        
        /***************get/set方法*****************/
    
        public QueryRunner getRunner() {
            return runner;
        }
    
        public void setRunner(QueryRunner runner) {
            this.runner = runner;
        }
    }
    
3.3 提供配置
  1. 创建Spring的核心配置文件:applicationContext.xml

    
    <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.itheima.service.impl.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"/>
        bean>
    
        <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
            <property name="runner" ref="runner"/>
        bean>
    
        <bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
            <constructor-arg name="ds" ref="dataSource"/>
        bean>
    
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
            <property name="jdbcUrl" value="jdbc:mysql:///spring"/>
            <property name="user" value="root"/>
            <property name="password" value="root"/>
        bean>
    beans>
    
3.4 功能测试
  1. 编写单元测试类AccountTest如下:

    public class AccountTest {
    
        private AccountService accountService;
    
        @Before
        public void before(){
            ApplicationContext context = 
                new ClassPathXmlApplicationContext("applicationContext.xml");
            accountService = context.getBean("accountService", AccountService.class);
        }
    
        @Test
        public void testQueryAll() throws SQLException {
            List<Account> accounts = accountService.queryAll();
            for (Account account : accounts) {
                System.out.println(account);
            }
        }
        
        @Test
        public void testSave() throws SQLException {
            Account account = new Account();
            account.setName("tom");
            account.setMoney(10000f);
            accountService.save(account);
        }
        
        @Test
        public void testEdit() throws SQLException {
        	Account account = new Account();
            account.setId(3);
            account.setName("jerry");
            account.setMoney(5000f);
            accountService.edit(account);    
        }
        
        @Test
        public void testDelete() throws SQLException {
            accountService.delete(3);
        }
    }
    

文件引入:

引入properties文件

如果需要applicationContext.xml中引入properties文件:

  • 准备一个properties文件放在resources里:jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring
jdbc.username=root
jdbc.password=root
  • applicationContext.xml中引入并使用jdbc.properties
    • Spring的名称空间(建议使用idea自动生成的,如果idea抽风了,就自己手写)
<beans
       xmlns:名称空间="http://www.springframework.org/schema/名称空间"
       xsi:scehmaLocation="
          http://www.springframework.org/schema/名称空间
         http://www.springframework.org/schema/名称空间/spring-名称空间.xsd">
beans>
  • 使用context名称空间提供的标签,引入外部的properties文件

<context:property-placeholder location="classpath:jdbc.properteis"/>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
bean>
分模块提供配置文件

在大型项目开发中,如果把所有的配置都写在一个配置文件applicationContext.xml中,会导致:

  • 配置文件过于臃肿
  • 不利于分模块开发,不利于模块之间的解耦

Spring提供了分模块配置的方式,即:每个模块提供一个配置文件,在核心配置文件中引入模块配置:

  • dao模块有一个配置文件:applicationContext-dao.xml 只配置dao相关的对象
  • service模块有一个配置文件:applicationContext-service.xml只配置service相关的对象
  • 有一个总的核心配置文件:applicationContext.xml如下
<import resource="classpath:applicationContext-service.xml"/>
<import resource="classpath:applicationContext-dao.xml"/>

Spring整合Junit

​在上边的CURD中,单元测试类里还需要我们自己去创建ApplicationContext,并自己去获取bean对象。Spring提供了整合Junit的方法,让单元测试更简洁方便。

注解简介

注解 说明
@RunWith 用在测试类上,用于声明不再使用Junit,而是使用Spring提供的运行环境
@ContextConfiguration 用在测试类上,用于指定Spring配置类、或者Spring的配置文件

Spring提供了单元测试的运行环境:SpringJunit4ClassRunner,配置到@RunWith注解上:

@RunWith(SpringJunit4ClassRunner.class)

spring-IOC(DI)-控制反转、依赖注入_第2张图片

  • 要使用以上注解,需要导入jar包依赖:spring-testjunit
<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-testartifactId>
    <version>5.1.2.RELEASEversion>
dependency>

<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.12version>
dependency>

使用示例

步骤
  1. 在pom.xml文件中增加依赖:spring-testjunit

  2. 修改单元测试类

    1. 在单元测试类上增加注解:@RunWith(SpringJunit4ClassRunner.class)

      目的:使用Spring的单元测试运行器,替换Junit原生的运行器

    2. 在单元测试类上增加注解:@ContextConfiguration()

      目的:指定配置文件或配置类

    3. 在测试类里的依赖项上,直接使用@Autowired注入依赖

实现
  1. 在pom.xml文件中增加依赖:spring-testjunit

    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-testartifactId>
        <version>5.1.2.RELEASEversion>
    dependency>
    
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
    dependency>
    
  2. 修改单元测试类

    @RunWith(SpringJUnit4ClassRunner.class)//使用Spring的单元测试运行器
    @ContextConfiguration("classpath:applicationContext.xml")//指定核心配置类/核心配置文件
    public class AccountTest {
    
        @Autowired
        private AccountService accountService;
    
        @Test
        public void queryAll() throws SQLException {
            List<Account> accounts = accountService.queryAll();
            for (Account account : accounts) {
                System.out.println(account);
            }
        }
        
    	//......
    }
    

你可能感兴趣的:(ioc,spring,bean,ioc)