2.4.5 Spring IOC控制反转, 概述, 入门, API, 配置文件, 实例化方式, 依赖注入方式/数据类型, 整合DBUtils, 注解开发, 整合JUint

目录

一 Spring概述

1.1 Spring是什么

1.2 Spring发展历程

1.3 Spring优势

1.4 Spring体系结构

二 初识IOC

2.1 概述

2.2 自定义IOC容器

2.2.1 介绍

2.2.2 实现

2.2.3 知识小结

三 Spring快速入门

3.1 介绍

3.2 实现

3.3 知识小结

四 Spring相关API

4.1 API继承体系介绍

4.2 BeanFactory

4.4 知识小结

五 Spring配置文件

5.1 Bean标签基本配置

5.2 Bean标签范围配置scope

5.3 Bean生命周期配置

5.4 Bean实例化三种方式

5.4.1 无参构造方法实例化 (最常用)

5.4.2 工厂静态方法实例化应用场景

5.4.3 工厂普通方法实例化

5.5 Bean依赖注入概述

5.6 Bean依赖注入方式

5.6.1 构造方法

5.6.2 set方法

5.6.3 P命名空间注入(本质也是set注入)

5.7 Bean依赖注入的数据类型

5.7.1 注入普通数据类型

5.7.2 注入集合数据类型

5.8 配置文件模块化

5.9 知识小结

六 DbUtils(IOC实战)

6.1 DbUtils是什么?

6.2 Spring的xml整合DbUtils

6.2.1 介绍

6.2.2 实现

6.3 知识小结

七 Spring注解开发

7.1 Spring常用注解

7.1.1 介绍

7.1.2 实现

7.2 Spring常用注解整合DbUtils

7.3 Spring新注解

7.4 Spring纯注解整合DbUtils

八 Spring整合Junit

8.1 普通Junit测试问题

8.2 Spring整合Junit


IOC控制反转

主要内容:

一 Spring概述

1.1 Spring是什么

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

1.2 Spring发展历程

1.3 Spring优势


1.4 Spring体系结构

二 初识IOC

2.1 概述

控制反转(Inverse Of Control)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。

控制:在java中指的是对象的控制权限(创建、销毁)

反转:指的是对象控制权由原来 由开发者在类中手动控制 反转到 由Spring容器控制

举个栗子

2.2 自定义IOC容器

2.2.1 介绍

需求

实现service层与dao层代码解耦合

步骤分析

1. 创建java项目,导入自定义IOC相关坐标
2. 编写Dao接口和实现类
3. 编写Service接口和实现类
4. 编写测试代码

2.2.2 实现

1)创建java项目,导入自定义IOC相关坐标

 <dependencies>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
        <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

2)编写Dao接口和实现类

package com.lagou.dao;

public interface IUserDao {

    public void save();

}
package com.lagou.dao.impl;

import com.lagou.dao.IUserDao;

public class UserDaoImpl implements IUserDao {

    public void save() {
        System.out.println("dao被调用了,保存成功...");
    }
}

3)编写Service接口和实现类

package com.lagou.service;

public interface IUserService {

    public void save() throws ClassNotFoundException, IllegalAccessException, InstantiationException;


}
package com.lagou.service.impl;

import com.lagou.dao.IUserDao;
import com.lagou.service.IUserService;
import com.lagou.utils.BeanFactory;

public class UserServiceImpl implements IUserService {


    public void save() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 调用dao层方法 传统方式 :存在编译期依赖:耦合重
        IUserDao userDao = new UserDaoImpl();
     
        userDao.save();

    }

}

4)编写测试代码

package com.lagou.test;

import com.lagou.service.IUserService;
import com.lagou.service.impl.UserServiceImpl;
import org.junit.Test;

public class SpringTest {

    @Test
    public void test1() throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        // 获取业务层对象
        IUserService userService = new UserServiceImpl();

        // 调用save方法
        userService.save();
        
    }
}


5)问题

当前service对象和dao对象耦合度太高,而且每次new的都是一个新的对象,导致服务器压力过大。

解耦合的原则是编译期不依赖,而运行期依赖就行了。

6)编写beans.xml
把所有需要创建对象的信息定义在配置文件中

<beans>

<!--id:标识    class:存的就是要生成实例的类的全路径-->
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>

</beans>

7)编写BeanFactory工具类

package com.lagou.service.impl;

public class BeanFactory {

    // IOC容器, 装实例对象
    private static Map<String,Object> iocmap = new HashMap<>();

    // 静态代码块, 可以再程序启动时,初始化对象实例
    static {

        //1.读取配置文件
        // getClassLoader获取类加载器
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");

        //2.解析bean.xml(使用dom4j)
        SAXReader saxReader = new SAXReader();
        try {
            // 读取bean.xml形成的文档对象
            Document document = saxReader.read(resourceAsStream);

            //3.编写xpath表达式, 指向根标签下的所有bean标签
            String xpath = "//bean";

         //4.获取到所有的bean标签
            List<Element> list = document.selectNodes(xpath);

         //5.遍历并使用反射创建对象实例,存到map集合(ioc容器)中
            for (Element element : list) {

                // 获取到id的值
                String id = element.attributeValue("id");

                //className : class属性的值 com.lagou.dao.impl.UserDaoImpl
                String className = element.attributeValue("class");

                // 使用反射生成实例对象, 不强转, 因为有多个bean
                Object o = Class.forName(className).newInstance();

                // 存到map中 key:id value:o
                iocmap.put(id,o);
            }

        } catch (DocumentException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

        // 该方法用来获取实例对象, beanId表示获取哪个类的实例对象
        public static Object getBean(String beanId){
            Object o = iocmap.get(beanId);
            return o;
        }
}

8)修改UserServiceImpl实现类

package com.lagou.service.impl;

import com.lagou.dao.IUserDao;
import com.lagou.service.IUserService;
import com.lagou.utils.BeanFactory;

public class UserServiceImpl implements IUserService {

    public void save() throws ClassNotFoundException, IllegalAccessException, InstantiationException {

        // 调用dao层方法 传统方式 :存在编译期依赖:耦合重
        //IUserDao userDao = new UserDaoImpl();

        //反射  直接反射存在硬编码问题, 所以要从IOC容器中获取实例对象
        // IUserDao userDao = (IUserDao) Class.forName("com.lagou.dao.impl.UserDaoImpl").newInstance();

        // 下面通过beanId = userDao 获取实例对象, 并且强转类型
        IUserDao userDao = (IUserDao) BeanFactory.getBean("userDao");
        userDao.save();
    }
}

2.2.3 知识小结

三 Spring快速入门

3.1 介绍

需求:借助spring的IOC实现service层与dao层代码解耦合

步骤分析


3.2 实现

1)创建java项目,导入spring开发基本坐标

<!--  导入spring依赖  -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

2)编写Dao接口和实现类

package com.lagou.dao;

public interface IUserDao {

    public void save();

}
public class UserDaoImpl implements UserDao {

    public void save() {

        System.out.println("调用了...");
    }
}

3)创建spring核心配置文件

<?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">
<!--  本文件命名约定俗成  -->

</beans>

4)在spring配置文件中配置 UserDaoImpl

    <!--在spring配置文件中配置 UserDaoImpl
        id: 唯一标识,不能重复
        class:类全路径 -->
    
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" ></bean>

5)使用spring相关API获得Bean实例

    @Test
    public void test1(){

        // 获取到了spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象 ,
        // 加载的同时就创建了bean对象存到容器中, 传入spring核心配置文件路径
         ApplicationContext classPathXmlApplicationContext =
                 new ClassPathXmlApplicationContext("applicationContext.xml");

        // 使用上下文对象从IOC容器中获取到了bean对象
        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        // 调用方法
        userDao.save();
    }

3.3 知识小结

Spring的开发步骤


四 Spring相关API

4.1 API继承体系介绍

Spring的API体系异常庞大,我们现在只关注两个BeanFactoryApplicationContext

4.2 BeanFactory

BeanFactory是 IOC 容器的核心接口,它定义了IOC的基本功能。

特点:在第一次调用getBean()方法时,创建指定对象的实例

    @Test
    public void test2(){

        // 核心接口,不会创建bean对象存到容器中
        BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        //getBean的时候才真正创建bean对象
        IUserDao userDao = (IUserDao) xmlBeanFactory.getBean("userDao");

        // ...
    }

4.3 ApplicationContext2

代表应用上下文对象,可以获得spring中IOC容器的Bean对象。

特点:在spring容器启动时,加载并创建所有对象的实例

    @Test
    public void test1(){

        // 获取到了spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象 ,
        // 与test2相比, 本行代码加载xml的同时就创建了bean对象存到容器中, 传入spring核心配置文件路径
         ApplicationContext classPathXmlApplicationContext =
                 new ClassPathXmlApplicationContext("applicationContext.xml");

        // ...
    }

常用实现类

    @Test
    public void test1(){

        // ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 实际下面的不常用
        ApplicationContext fileSystemXmlApplicationContext = 
            new FileSystemXmlApplicationContext("D:\\spring_code\\spring_quickstart\\src\\main\\resources\\applicationContext.xml");

        // ...
    }

getBean的重载方法

        // 使用上下文对象从IOC容器中获取到了bean对象
        // 1.根据beanid在容器中找对应的bean对象
        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        // 2.根据类型在容器中进行查询:
        // 有可能报错的情况:根据当前类型匹配到多个实例, 因为IUserDao是个接口, 可能有多个实例类型
        IUserDao userDao = classPathXmlApplicationContext.getBean(IUserDao.class);

        // 3. 下面的联合查找, 可以准确定位对象
        IUserDao userDao = classPathXmlApplicationContext.getBean("userDao",IUserDao.class);

public class UserDaoImpl1 implements IUserDao {
    @Override
    public void save() {

    }
}

public class UserDaoImpl2 implements IUserDao {
    @Override
    public void save() {

    }
}
<!-- 下面两个都实现了IUserDao, Test中根据 IUserDao.class 匹配实例会报错 -->
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" ></bean>
    <bean id="userDao2" class="com.lagou.dao.impl.UserDaoImpl2" ></bean>

4.4 知识小结

五 Spring配置文件

5.1 Bean标签基本配置

5.2 Bean标签范围配置scope


scope属性指对象的作用范围,取值如下:

portlet 是一种组件环境

    /*
        测试scope属性:singleton效果
     */
    @Test
    public void test3(){


        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        IUserDao userDao2 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        System.out.println(userDao);

        System.out.println(userDao2);

    }

scope = "Singleton"

  scope="singleton" :默认值:创建出来的bean是单例的
  prototype  : 创建出来的bean是多例的,每次从容器中获取都会创建一个新的对象

  <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"  scope="singleton"></bean>

scope = "prototype"

  scope="singleton" :默认值:创建出来的bean是单例的
  prototype  : 创建出来的bean是多例的,每次从容器中获取都会创建一个新的对象

    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"  scope="prototype"></bean>

5.3 Bean生命周期配置

    @Test
    public void test4(){

        // 将下面的父类接口类型改为子类的类型, 才能使用子类中的close方法, 测试destroy的方法
        // ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        IUserDao userDao2 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        System.out.println(userDao);

        System.out.println(userDao2);

        classPathXmlApplicationContext.close();

    }

5.4 Bean实例化三种方式

  • 无参构造方法实例化
  • 工厂静态方法实例化
  • 工厂普通方法实例化

5.4.1 无参构造方法实例化 (最常用)

它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败

5.4.2 工厂静态方法实例化
应用场景

依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。

如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

package com.lagou.factory;

import com.lagou.dao.IUserDao;
import com.lagou.dao.impl.UserDaoImpl;

public class StaticFactoryBean {
    
    public static IUserDao createUserDao(){

        return  new UserDaoImpl();
    }
}
    <!--方式二:工厂静态方法实例化 
      下面的标签自动创建实例对象, 保存到容器中  -->
     <bean id="userDao" class="com.lagou.factory.StaticFactoryBean"  factory-method="createUserDao"></bean>


5.4.3 工厂普通方法实例化

应用场景
依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。

如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

package com.lagou.factory;

import com.lagou.dao.IUserDao;
import com.lagou.dao.impl.UserDaoImpl;

public class DynamicFactoryBean {

    public IUserDao createUserDao(){

        return  new UserDaoImpl();
    }
}
    <!--方式三:工厂普通方法实例化
        factory-bean: 指定工厂bean对象
        factory-method: 调用工厂类中的哪个方法   -->
        <bean id="dynamicFactoryBean" class="com.lagou.factory.DynamicFactoryBean"></bean>
        <bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"></bean>

5.5 Bean依赖注入概述

依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。

那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是通过框架把持久层对象传入业务层,而不用我们自己去获取。

使用DI 就会变成以下情况

5.6 Bean依赖注入方式

5.6.1 构造方法

在UserServiceImpl中创建有参构造


public class UserSerivceImpl implements IUserService {

    // 下面用来接收注入进来的userDao实例
    private IUserDao userDao;

    // 创建有参构造, 准备构造注入
    public UserSerivceImpl(IUserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        // 调用dao层的save方法
        userDao.save();
    }
}

配置Spring容器调用有参构造时进行注入

<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">

    <!-- 根据下面的ref 找到容器中的实例对象 userDao, 注入给userService有参构造 -->
    <!--<constructor-arg index="0" type="com.lagou.dao.UserDao" ref="userDao"/>-->

<!-- 简写: 
    根据下面的ref 找到容器中的实例对象 userDao, 注入给userService有参构造, 
    根据name 的值定位有参构造的参数名     -->
    <constructor-arg name="userDao" ref="userDao"/>
</bean>

5.6.2 set方法

在UserServiceImpl中创建set方法


public class UserSerivceImpl implements IUserService {

    // 下面用来接收注入进来的userDao实例
    private IUserDao userDao;

    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        // 调用dao层的save方法
        userDao.save();
    }
}

配置Spring容器调用set方法进行注入

   <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>

    <!--配置UserService-->
   <bean id="userService" class="com.lagou.servlet.impl.UserSerivceImpl">

       <!--set方法完成依赖注入-->
       <property name="userDao" ref="userDao"></property>
   </bean>

5.6.3 P命名空间注入(本质也是set注入)

P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:

首先,需要引入P命名空间:

<?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"

        xmlns:p="http://www.springframework.org/schema/p">
       <!-- 引入上面的语句  -->
    
</beans>

其次,需要修改注入方式:

   <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>
   <!--配置UserService-->
   <bean id="userService" class="com.lagou.servlet.impl.UserSerivceImpl" p:userDao-ref="userDao">

       <!--set方法完成依赖注入-->
      <!-- <property name="userDao" ref="userDao"></property>-->
    </bean>

5.7 Bean依赖注入的数据类型

上面操作,都是注入Bean对象,除了对象的引用可以注入,普通数据类型和集合都可以在容器中进行注入。

注入数据的三种数据类型
1. 普通数据类型
2. 引用数据类型
3. 集合数据类型

    其中引用数据类型,此处就不再赘述了,之前的操作都是对UserDao对象的引用进行注入的。下面将以set方法注入为例,演示普通数据类型和集合数据类型的注入。

5.7.1 注入普通数据类型

public class UserDaoImpl implements IUserDao {

    // 注入普通数据类型
    private String username;
    private Integer age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
<!--配置UserDao 方式一:无参构造方法进行实例化-->
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" >
        <!--ref:用于引用数据类型的注入,value是用于普通数据类型的注入-->
        <property name="username" value="子慕"></property>
        <property name="age" value="18"></property>

    </bean>

5.7.2 注入集合数据类型

package com.lagou.dao.impl;

import com.lagou.dao.IUserDao;

import java.util.*;

public class UserDaoImpl implements IUserDao {

    // 注入集合数据类型
    private List<Object> list;
    private Set<Object> set;

    // 注入数组类型
    private Object[] array;
    private Map<String,Object> map;

    // 内容都是字符串, 数据也是用 map形式保存
    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public void setArray(Object[] array) {
        this.array = array;
    }

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

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

    public void setUsername(String username) {
        this.username = username;
    }

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

    public void save() {
        System.out.println("list集合:" + list);
        System.out.println("set集合:" + set);
        System.out.println("array数组:" + Arrays.toString(array));
        System.out.println("map集合:" + map);
        System.out.println("properties:" + properties);

        System.out.println("dao被调用了....");
    }
}
    <!--配置User对象, 用来展示在集合注入中注入引用类型的对象 -->
    <bean id="user" class="com.lagou.domain.User">
        <property name="username" value="柳岩"/>
        <property name="age" value="18"/>
    </bean>

<!--配置UserDao 方式一:无参构造方法进行实例化-->
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" >


        <!--进行list集合数据类型的注入-->
        <property name="list">
            <list>
                <!-- 假如要注入的第一个元素是普通类型, 则如下 -->
                <value>aaa</value>
                 <!-- 如果注入第二是引用类型的user -->
                <ref bean="user"></ref>
            </list>
        </property>

        <!--进行set集合数据类型的注入-->
        <property name="set">
            <set>
                <value>bbb</value>
                <ref bean="user"></ref>
            </set>
        </property>

        <!--进行array数组数据类型的注入-->
        <property name="array">
            <array>
                <value>ccc</value>
                <ref bean="user"></ref>
            </array>
        </property>
<!--  -->
        <!--进行map集合数据类型的注入-->
        <property name="map">
            <map>
                <!-- 和上面不一样的标签 entry -->
                <entry key="k1" value="ddd"></entry>
                <entry key="k2" value-ref="user"></entry>
            </map>
        </property>

        <!--进行properties数据类型的注入-->
        <property name="properties">
            <props> 
                <!-- 和上面不一样的标签 prop -->
                <prop key="k1">v1</prop>
                <prop key="k2">v2</prop>
                <prop key="k3">v3</prop>
            </props>
        </property>

    </bean>

1)List集合注入

2)Set集合注入

3)Array数组注入

4)Map集合注入

5)Properties配置注入

5.8 配置文件模块化

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,也就是所谓的配置文件模块化。

按层进行拆分(service/ dao / ...), 或者按模块进行拆分 (user / oeder / product / ....)

1)并列的多个配置文件加载方式

2)主配置文件中的,  主从配置文件加载方式

<!--在主配置xml中, 借助import标签-->
    <import resource="classpath:applicationContext-user.xml"></import>


注意:

  • 同一个xml中不能出现相同名称的bean,如果出现会报错
  • 多个xml如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean

5.9 知识小结

Spring的重点配置


六 DbUtils(IOC实战)

6.1 DbUtils是什么?

DbUtils是Apache的一款用于简化Dao代码的工具类,它底层封装了JDBC技术。

核心对象

核心方法


举个栗子
查询数据库所有账户信息到Account实体中

public class DbUtilsTest {
    @Test
    public void findAllTest() throws Exception {
        // 创建DBUtils工具类,传入连接池
        QueryRunner queryRunner = new QueryRunner(JdbcUtils.getDataSource());
        // 编写sql
        String sql = "select * from account";
        // 执行sql
        List<Account> list = queryRunner.query(sql, new BeanListHandler<Account>(Account.class));
        // 打印结果
        for (Account account : list) {
            System.out.println(account);
        }
    }
}

6.2 Spring的xml整合DbUtils

6.2.1 介绍

需求
基于Spring的xml配置实现账户的CRUD案例
步骤分析

6.2.2 实现

1)准备数据库环境

CREATE DATABASE `spring_db`;

USE `spring_db`;

CREATE TABLE `account` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) DEFAULT NULL,
`money` DOUBLE DEFAULT NULL,
PRIMARY KEY (`id`)
) ;

INSERT INTO `account`(`id`,`name`,`money`) VALUES (1,'tom',1000),(2,'jerry',1000);

2)创建java项目,导入坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lagou</groupId>
    <artifactId>spring_dbutils</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--指定编码及JDK版本-->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.11</java.version>
        <maven.compiler.source>1.11</maven.compiler.source>
        <maven.compiler.target>1.11</maven.compiler.target>
    </properties>

    <dependencies>
        <!--  数据库驱动  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        
        <!--  数据库连接池  -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.9</version>
        </dependency>

        <!--  数据库工具类  -->
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.6</version>
        </dependency>

        <!--  spring依赖  -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>

        <!--  单元测试  -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!--  单元测试依赖  -->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>
        
    </dependencies>
</project>

3)编写Account实体类

package com.lagou.domain;

public class Account {

    private Integer id;
    private String name;
    // 钱要设置成double类型
    private Double money;

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
    
    // getter setter 略
}

4)编写AccountDao接口和实现类

package com.lagou.dao;

public interface AccountDao {

    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);

}
package com.lagou.dao.impl;

public class AccountDaoImpl implements AccountDao {
    
    // 声明成员变量, 支持使用spring的set注入
    private QueryRunner queryRunner;

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    public List<Account> findAll() {

        List<Account> list = null;
        //编写sql
        String sql = "select * from account";
        try {
            // 执行sql
            list = queryRunner.query(sql, new BeanListHandler<Account>(Account.class));

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }

    public Account findById(Integer id) {

        Account account = null;
        // 编写sql
        String sql = "select * from account where id = ?";

        try {
             account = queryRunner.query(sql, new BeanHandler<Account>(Account.class),id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return account;
    }

    public void save(Account account) {

        String sql = "insert into account values(null,?,?)";
        try {
            queryRunner.update(sql,account.getName(),account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void update(Account account) {

        String sql = "update account set name = ?,money = ? where id = ?";
        try {
            queryRunner.update(sql,account.getName(),account.getMoney(),account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void delete(Integer id) {

        String sql = "delete from account where id = ?";
        try {
            queryRunner.update(sql,id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

5)编写AccountService接口和实现类

package com.lagou.servlet;

public interface AccountService {
    
    public List<Account> findAll();

    public Account findById(Integer id);

    public void save(Account account);

    public void update(Account account);

    public void delete(Integer id);
}
package com.lagou.servlet.impl;

public class AccountServiceImpl implements AccountService {

    private AccountDao accountDao;

    public void setaDao(AccountDao accountDao) {
        this.accountDao= accountDao;
    }

    public List<Account> findAll() {

        return  accountDao.findAll();
    }

    public Account findById(Integer id) {

        return accountDao.findById(id);
    }

    public void save(Account account) {

        accountDao.save(account);
    }

    public void update(Account account) {

        accountDao.update(account);
    }

    public void delete(Integer id) {

        accountDao.delete(id);
    }
}

6)编写spring核心配置文件applicationContext.xml

<?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">

    <!--要先创建数据源对象dataSource存在容器中, 才能创建queryRunner对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 下面本质上也是在进行注入 -->
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///spring_db?characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--要先创建queryRunner对象, 才能对AccountDao对象进行注入-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <!-- 有参构造, 用下面的标签,  -->
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

    <!--要先创建AccountDao对象, 才能对AccountService进行注入-->
    <!-- 强调: 下面class要放实现类的全路径 -->
    <bean id="accountDao" class="com.lagou.dao.impl.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"/>
    </bean>

    <!--AccountService-->
    <bean id="accountService" class="com.lagou.servlet.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

7)编写测试代码

package com.lagou.test;

public class AccountServiceTest {

    // 每个方法中都有以下余军, 因此提取为成员变量
    ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService accountService = (AccountService) classPathXmlApplicationContext.getBean("accountService");

    //测试添加
    @Test
    public void testSave(){

        Account account = new Account();
        account.setName("lucy");
        // 加d 表示double 类型
        account.setMoney(888d);
        accountService.save(account);
    }

    //测试查询
    @Test
    public void testFindById(){
        Account account = accountService.findById(3);
        System.out.println(account);
    }

    //测试查询所有
    @Test
    public void testFindAll(){
        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
    }

    //测试更新
    @Test
    public void testUpdate(){
        Account account = new Account();
        account.setId(3);
        account.setName("jack");
        account.setMoney(2000d);

        accountService.update(account);
    }

    //测试删除
    @Test
    public void testDelete(){
        accountService.delete(3);
    }
}

8)抽取jdbc配置文件

applicationContext.xml加载jdbc.properties配置文件获得连接信息。
首先,需要引入context命名空间和约束路径:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db?characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       	http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd">



    <!--引入jdbc.properties-->
    <!-- 引入外部配置, 记得都要加上classpath: -->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>


    <!--要先创建数据源对象dataSource存在容器中, 才能创建queryRunner对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 下面本质上也是在进行注入 -->
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

6.3 知识小结

七 Spring注解开发

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。

7.1 Spring常用注解

7.1.1 介绍

Spring常用注解主要是替代 <bean> 的配置

说明:
JDK11以后完全移除了javax扩展导致不能使用@resource注解, 需要Maven引入依赖

        <!--  注解依赖  -->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>

注意
使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。

 <!--***注解扫描-->
    <context:component-scan base-package="com.lagou"></context:component-scan>

7.1.2 实现

1)Bean实例化(IOC)

使用@Service标识需要Spring进行实例化。

    <!--AccountService-->
  <!--  <bean id="accountService" class="com.lagou.servlet.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>-->
@Service("accountService")  //相当于配置了bean标签 括号内为id属性
public class AccountServiceImpl implements AccountService {

}

2)属性依赖注入(DI)
使用@Autowired或者@Autowired+@Qulifier或者@Resource进行的注入

/*  
    // 下面这种用的更多, resource用的少
    @Autowired  //根据类型进行注入 使用这种方式时, 如果有多个实例对象, 会根据变量名进行二次匹配, 如果匹配不上则会报错
    @Qualifier("accountDao") // 必须结合 Autowired使用, 增加这种注解时, 也会进行二次匹配 */
    @Resource(name = "accountDao")  // 等同于 Q 和 A 结合使用, 但是需要引入javax依赖
    private AccountDao aDao;

3)@Value
使用@Value进行字符串的注入,结合SPEL表达式获得配置参数

    @Value("注入普通属性")
    private String str;

    @Value("${jdbc.driverClassName}") // 可以使用这个读取到jdbc配置文件中的内容
    private String driver;

4)@Scope
使用@Scope标注Bean的范围

@Scope("singleton") // 标注作用范围
public class AccountServiceImpl implements AccountService {


}

5)Bean生命周期
使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法

    @PostConstruct
    public void init(){
        System.out.println("初始化方法....");
    }


    @PreDestroy
    public void destory(){
        System.out.println("销毁方法....");
    }

7.2 Spring常用注解整合DbUtils

步骤分析


1)拷贝xml配置项目,改为常用注解配置项目
过程略....

2)修改AccountDaoImpl实现类

package com.lagou.dao.impl;

@Repository //相当于配置了bean标签
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner queryRunner;

    // set方法就不需要了
/*    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }*/

    public List<Account> findAll() {

        List<Account> list = null;
        //编写sql
        String sql = "select * from account";
        try {
            // 执行sql
            list = queryRunner.query(sql, new BeanListHandler<Account>(Account.class));

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }

    public Account findById(Integer id) {

        Account account = null;
        // 编写sql
        String sql = "select * from account where id = ?";

        try {
            account = queryRunner.query(sql, new BeanHandler<Account>(Account.class),id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return account;
    }

    public void save(Account account) {

        String sql = "insert into account values(null,?,?)";
        try {
            queryRunner.update(sql,account.getName(),account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void update(Account account) {

        String sql = "update account set name = ?,money = ? where id = ?";
        try {
            queryRunner.update(sql,account.getName(),account.getMoney(),account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void delete(Integer id) {

        String sql = "delete from account where id = ?";
        try {
            queryRunner.update(sql,id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

3)修改AccountServiceImpl实现类

package com.lagou.servlet.impl;

@Service("accountService")      //相当于配置了bean标签 括号内为id属性
public class AccountServiceImpl implements AccountService {

    @Autowired      //根据类型进行注入
    private AccountDao aDao;

    // 底层采用反射机制, 不再使用set方式进行注入
//    public void setaDao(AccountDao aDao) {
//        this.aDao = aDao;
//    }

    public List<Account> findAll() {

        return  aDao.findAll();
    }

    public Account findById(Integer id) {

        return aDao.findById(id);
    }

    public void save(Account account) {

        aDao.save(account);
    }

    public void update(Account account) {

        aDao.update(account);
    }

    public void delete(Integer id) {

        aDao.delete(id);
    }
}

4)修改spring核心配置文件

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       	http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd">

    <!--***注解扫描-->
    <context:component-scan base-package="com.lagou"></context:component-scan>

    <!--引入jdbc.properties-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--要先创建数据源对象dataSource存在容器中, 才能创建queryRunner对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 下面本质上也是在进行注入 -->
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>


    <!--要先创建queryRunner对象, 才能对AccountDao对象进行注入-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <!-- 有参构造, 用下面的标签,  -->
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

    <!--要先创建AccountDao对象, 才能对AccountService进行注入-->
    <!-- 强调: 下面class要放实现类的全路径 -->
<!--    <bean id="accountDao" class="com.lagou.dao.impl.AccountDaoImpl">
        <property name="queryRunner" ref="queryRunner"/>
    </bean>-->

    <!--AccountService-->
  <!--  <bean id="accountService" class="com.lagou.servlet.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>-->

</beans>

5)编写测试代码

    //测试查询所有
    @Test
    public void testFindAll(){
        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
        //classPathXmlApplicationContext.close();
    }

7.3 Spring新注解

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:

<!--***注解扫描-->
    <context:component-scan base-package="com.lagou"></context:component-scan>

    <!--引入jdbc.properties-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>


    <!-- 下面的bean是非自定义的 --> 
    <!--要先创建数据源对象dataSource存在容器中, 才能创建queryRunner对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <!-- 下面本质上也是在进行注入 -->
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>


    <!--要先创建queryRunner对象, 才能对AccountDao对象进行注入-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <!-- 有参构造, 用下面的标签,  -->
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

7.4 Spring纯注解整合DbUtils

步骤分析

 
1)编写Spring核心配置类

package com.lagou.config;

import javax.sql.DataSource;

@Configuration      // 表明这是配置类
@ComponentScan("com.lagou") // 注解扫描
@Import(DataSourceConfig.class)    // 导入DataSource 配置类
public class SpringConfig {

    @Bean("queryRunner")
    public QueryRunner getQueryRunner(@Autowired  DataSource dataSource){
    // 上面的Autowired告诉spring在IOC容器中找到这个对象用来注入

        QueryRunner queryRunner = new QueryRunner(dataSource);
        return  queryRunner;
    }
}

2)编写数据库配置信息类

package com.lagou.config;

@PropertySource("classpath:jdbc.properties")    // 加载jdbc配置文件
public class DataSourceConfig {

    // 上面已经使用PropertySource加载了配置文件
    // 这里可以将jdbc配置文件中的内容存到变量中
    @Value("${jdbc.driverClassName}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource") // 非自定义bean标签, 将返回值存入IOC容器中, 括号内容为key值, 不写括号则key为方法名
    public DataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource; // 直接return就行
    }
}

3)编写测试代码

删掉配置文件进行测试

package com.lagou.test;


public class AccountServiceTest {

    // 每个方法中都有以下语句, 因此提取为成员变量
    //ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    //AccountService accountService = (AccountService) classPathXmlApplicationContext.getBean("accountService");

    // 当前改成了纯注解形式
    AnnotationConfigApplicationContext annotationConfigApplicationContext =   new AnnotationConfigApplicationContext(SpringConfig.class);
    AccountService accountService = (AccountService) annotationConfigApplicationContext.getBean("accountService");

    //测试查询所有
    @Test
    public void testFindAll(){
        List<Account> all = accountService.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
        //classPathXmlApplicationContext.close();
    }
}

八 Spring整合Junit

8.1 普通Junit测试问题

在普通的测试类中,需要开发者手动加载配置文件并创建Spring容器,然后通过Spring相关API获得Bean实例;如果不这么做,那么无法从容器中获得对象。


我们可以让SpringJunit负责创建Spring容器来简化这个操作,开发者可以直接在测试类注入Bean实例;但是需要将配置文件的名称告诉它。

8.2 Spring整合Junit

步骤分析

1)导入spring集成Junit的坐标

<!--此处需要注意的是,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

2)使用@Runwith注解替换原来的运行器

//@RunWith指定junit的运行环境SpringJUnit4ClassRunner是spring提供的作为junit运行环境的类
@RunWith(SpringJUnit4ClassRunner.class)   
public class AccountServiceTest {

}

3)使用@ContextConfiguration指定配置文件或配置类

@RunWith(SpringJUnit4ClassRunner.class) 
// @ContextConfiguration({"classpath:applicationContext.xml"}) // 加载配置文件的话, 使用此种方式
@ContextConfiguration(classes = {SpringConfig.class})    // 有多个配置类, 都可以放在这里
public class AccountServiceTest {

}

4)使用@Autowired注入需要测试的对象

    @Autowired  // 使用此注解注入需要测试的对象
    private AccountService accountService;

5)创建测试方法进行测试

    //测试查询
    @Test
    public void testFindById(){
        Account account = accountService.findById(3);
        System.out.println(account);
    }

猜你喜欢

转载自blog.csdn.net/chengh1993/article/details/110405950