任务一:IOC控制反转

任务一:IOC控制反转

任务一课程主要内容:

* spring概念介绍 
* IOC 
* spring快速入门 
* spring相关API介绍 
* Spring配置文件 
* DBUtils 
* spring注解开发 
* spring整合Junit 

Spring概述

1.1 Spring是什么

Spring是分层的 Java SE/EE应用 full-stack(全栈式)轻量级开源框架

  • full-stack(全栈式):说明spring对各种主流技术和框架都进行了整合,同时对三层架构都提供了解决方案
  • 轻量级与重量级划分的主要依据就是看其使用了多少服务,启动时需要加载的资源的多少,以及耦合度等等。

提供了表现层 SpringMVC和持久层 Spring JDBC Template以及 业务层 事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

两大核心:以 IOC(Inverse Of Control:控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核

  • ioc:控制反转:把对象的创建权交给spring,不需要自己创建对象
  • aop:面向切面编程:在不修改源码的情况下,对方法进行增强。aop底层就是动态代理

1.2 Spring发展历程

* EJB
	1997 年,IBM提出了EJB 的思想 
	1998 年,SUN制定开发标准规范 EJB1.0 
	1999 年,EJB1.1 发布 
	2001 年,EJB2.0 发布 
	2003 年,EJB2.1 发布 
	2006 年,EJB3.0 发布 


* Spring 
	Rod Johnson( Spring 之父) 
	改变Java世界的大师级人物 

	2002年编著《Expert one on one J2EE design and development》 
		指出了JavaEE和EJB组件框架中的存在的一些主要缺陷;提出普通java类依赖注入更为简单的解决方案。

	2004年编著《Expert one-on-one J2EE Development without EJB》 
		阐述了JavaEE开发时不使用EJB的解决方式(Spring 雏形) 
		同年4月spring1.0诞生
	
	2006年10月,发布 Spring2.0 
	2009年12月,发布 Spring3.0 
	2013年12月,发布 Spring4.0 
	2017年9月, 发布最新 Spring5.0 通用版(GA)

1.3 Spring优势

1)方便解耦,简化开发
Spring就是一个容器,可以将所有对象创建和关系维护交给Spring管理
什么是耦合度?对象之间的关系,通常说当一个模块(对象)更改时也需要更改其他模块(对象),这就是 耦合,耦合度过高会使代码的维护成本增加。要尽量解耦
在这里插入图片描述

2)AOP编程的支持
Spring提供面向切面编程,方便实现程序进行权限拦截,运行监控等功能。

3)声明式事务的支持
通过配置完成事务的管理,无需手动编程

4)方便测试,降低JavaEE API的使用
Spring对Junit4支持,可以使用注解测试

5)方便集成各种优秀框架
不排除各种优秀的开源框架,内部提供了对各种优秀框架的直接支持

1.4 Spring体系结构

在这里插入图片描述

二 初识IOC

2.1 概述

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

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

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

控制反转一句话总结就是将对象的控制权限交给spring容器

举个栗子

* 传统方式 
	之前我们需要一个userDao实例,需要开发者自己手动创建 new UserDao(); 

* IOC方式 
	现在我们需要一个userDao实例,直接从spring的IOC容器获得,对象的创建权交给了spring控制

在这里插入图片描述

2.2 自定义IOC容器

2.2.1 介绍

需求

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

步骤分析

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

  2. 编写Dao接口和实现类

  3. 编写Service接口和实现类

  4. 编写测试代码

2.2.2 实现

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

pom.xml文件

    <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接口和实现类

IUserDao接口

public interface IUserDao {
    
    public void save();
}

UserDaoImpl实现类

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

3)编写Service接口和实现类

IUserService接口

public interface IUserService {

    public void save();
}

UserServiceImpl实现类

public class UserServiceImpl implements IUserService {
    public void save() {
        System.out.println("service层执行了");
        
        //调用dao层方法,传统方式
        IUserDao userDao = new UserDaoImpl();
        userDao.save();
    }
}

4)编写测试代码

public class SpringTest {

    /*在传统方式下,使用此测试类充当web层来调用service层*/
    @Test
    public void test1(){
        //获取业务层对象
        IUserService userService = new UserServiceImpl();

        //调用业务层的save方法
        userService.save();
    }
}

5)存在问题

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

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

在这里插入图片描述

2.2.3 问题改良

根据存在的问题将代码进行改良

6)编写beans.xml

把所有需要创建对象的信息定义在配置文件中

<beans>
    <!--id:表示当前bean的唯一标识,   class:值为要生成实例的类的全路径-->
    <bean id="userDao" class=" com.myLagou.dao.impl.UserDaoImpl ">
        
    </bean>
</beans>

7)编写BeanFactory工具类

package com.myLagou.utils;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zhy
 * @create 2022-08-11 15:51
 */
public class BeanFactory {
    //要使在调用这个类的时候就执行相关代码——解析文件,使用反射生成实例对象等,且只执行一次,只有使用静态代码块

    //创建map
    private static Map<String, Object> iocMap = new HashMap<>();

    //程序启动时,初始化对象实例
    static {
        try {
        //1、读取配置文件      通过类加载器来获取资源
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");

        //2、使用dom4j解析xml文件
        SAXReader saxReader = new SAXReader();

            Document document = saxReader.read(resourceAsStream);//document对象就是当前解析的beans.xml文件的文档对象

            //3、编写一个xpath表达式
            String xPath = "//bean";//获取beans.xml文件中的bean标签
            //4、获取到所有的bean标签
            List<Element> list = document.selectNodes(xPath);

            //5、遍历list,并使用反射创建对象实例,存储到map集合(map集合就充当了IOC容器)中
            for (Element element : list) {

                String id = element.attributeValue("id");
                String className = element.attributeValue("class");

                //使用反射来实例化对象
                Object o = Class.forName(className).newInstance();

                //将对象添加到map中   key:获取到的bean标签的id   value:获取到的bean实例对象
                iocMap.put(id, o);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //根据beanId来获取对于的实例对象
    public static Object getBean(String beanId){
        Object o = iocMap.get(beanId);
        return o;
    }
}

8)修改UserServiceImpl实现类

public class UserServiceImpl implements IUserService {
    public void save() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        System.out.println("service层执行了");

        //调用dao层方法,传统方式,耦合度大
//        IUserDao userDao = new UserDaoImpl();

        //使用反射的反射来获取userDao对象
//        Class<?> aClass = Class.forName("com.myLagou.dao.impl.UserDaoImpl");
//        IUserDao userDao = (IUserDao)aClass.newInstance();

        //使用从beans.xml配置文件中获取实例对象的方式
        IUserDao userDao = (IUserDao) BeanFactory.getBean("userDao");
        userDao.save();
    }
}

2.2.4 知识小结

* 其实升级后的BeanFactory就是一个简单的Spring的IOC容器所具备的功能。 
* 之前我们需要一个userDao实例,需要开发者自己手动创建 new UserDao(); 
* 现在我们需要一个userdao实例,直接从spring的IOC容器获得,对象的创建权交给了spring控制 
* 最终目标:代码解耦合

Spring快速入门

3.1 介绍

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

步骤分析

1. 创建java项目,导入spring开发基本坐标 
2. 编写Dao接口和实现类
3. 创建spring核心配置文件 
4. 在spring配置文件中配置 UserDaoImpl 
5. 使用spring相关API获得Bean实例

3.2 实现

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

pom.xml文件

<?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.myLagou</groupId>
    <artifactId>spring_quickStart</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
<!--            <version>5.1.5</version>-->
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

2)编写Dao接口和实现类

IUserDao接口

public interface IUserDao {
    public void save();
}

UserDao实现类

public class UserDao implements IUserDao {
    public void save() {
        System.out.println("dao层被调用了");
    }
}

3)创建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">

</beans>

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

<?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="userDao" class="com.myLagou.dao.impl.UserDao"></bean>

</beans>

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

public class SpringTest {

    /*使用测试类充当service层*/
    @Test
    public void test1(){
        //获取到了spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象, 加载配置文件的同时就创建了bean对象,并且存到了ioc容器中
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        //使用上下文对象直接从ioc容器中获取到已经创建好的bean的实例对象,不是在此时创建bean对象
        //getBean方法中的参数为applicationContext.xml文件中配置的所需要加载的bean组件的id
        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        //调用Dao层的方法
        userDao.save();

    }
}

浅浅分析一波

在这里插入图片描述

3.3 知识小结

Spring的开发步骤

1. 导入坐标 

2. 创建Bean 

3. 创建applicationContext.xml 

4. 在配置文件中进行Bean配置 

5. 创建ApplicationContext对象,执行getBean

Spring相关API

4.1 API继承体系介绍

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

在这里插入图片描述

4.2 BeanFactory

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

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

  @Test
    public void test2(){
        //XmlBeanFactory是BeanFactory接口的实现类
        //BeanFactory是一个核心接口,执行此句代码时,不会创建bean实例对象,在第一次执行getBean()方法时才会创建bean对象
        BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        //在第一次执行getBean()方法时才会创建bean对象,并且保存到容器中
        IUserDao userDao = (IUserDao) xmlBeanFactory.getBean("userDao");

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

4.3 ApplicationContext

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

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

  @Test
    public void test1(){
        //获取到了spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象,加载配置文件的同时就创建了bean对象,并且存到了ioc容器中
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        //使用上下文对象直接从ioc容器中获取到已经创建好的bean的实例对象,不是在此时创建bean对象
        //getBean方法中的参数为applicationContext.xml文件中配置的所需要加载的bean组件的id
        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        //调用Dao层的方法
        userDao.save();
    }
常用实现类
  1. ClassPathXmlApplicationContext
    它是从类的根路径下加载配置文件 推荐使用这种

  2. FileSystemXmlApplicationContext
    它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置

      /*测试ApplicationContext接口的常用类FileSystemXmlApplicationContext*/
        @Test
        public void test3(){
             ApplicationContext fileSystemXmlApplicationContext = new FileSystemXmlApplicationContext("D:\\blog\\test\\applicationContext.xml");
    
             IUserDao userDao = (IUserDao) fileSystemXmlApplicationContext.getBean("userDao");
    
            //调用Dao层的方法
            userDao.save();
        }
    
  3. AnnotationConfigApplicationContext
    当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解

常用方法
  1. Object getBean(String name);

根据配置文件中Bean的id从容器中获得Bean实例,返回是Object,需要强转。

  1. <T> T getBean(Class<T> requiredType);

根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。

  1. <T> T getBean(String name,Class+<T> requiredType);

根据Bean的id和类型获得Bean实例,解决容器中相同类型Bean有多个情况。

/*使用测试类充当service层*/
    @Test
    public void test1(){
        //获取到了spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象,加载配置文件的同时就创建了bean对象,并且存到了ioc容器中
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        //使用上下文对象直接从ioc容器中获取到已经创建好的bean的实例对象,不是在此时创建bean对象
        //1、根据bean的id获取bean对象,getBean方法中的参数为applicationContext.xml文件中配置的所需要加载的bean组件的id
        //IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

        //2、根据bean的类型来从容器中获取bean实例对象
        //当容器中相同类型的Bean有多个时,则此方法会报错。
//        IUserDao userDao = classPathXmlApplicationContext.getBean(IUserDao.class);

        //3、根据Bean的id和类型获得Bean实例,解决容器中相同类型Bean有多个情况。
        IUserDao userDao = classPathXmlApplicationContext.getBean("userDao", IUserDao.class);

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

Spring配置文件

5.1 Bean标签基本配置

<bean id="" class=""></bean> 

* 用于配置对象交由Spring来创建。 一个bean标签就代表spring容器要创建一个bean对象

* 基本属性: 
	id:Bean实例在Spring容器中的唯一标识 
	class:Bean的全限定名 (原因是spring底层是通过反射来创建对象)
  • 默认情况下它调用的是类中的 无参构造函数,如果没有无参构造函数则不能创建成功

5.2 Bean标签范围配置

 <!--使用bean标签在spring配置文件中配置UserDaoImpl类-->
    <!--id:表示这个bean标签的唯一标识   class:表示这个bean组件所配置的类的全类名
        scope="singleton"表示创建的bean对象是单例的,为默认值
        scope="prototype"表示创建的bean对象是多例的,每次从容器中获取bean对象都会创建一个新的bean对象
    -->
    <bean id="userDao" class="com.myLagou.dao.impl.UserDao" scope="prototype" ></bean>

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

取值范围说明
singleton默认值,单例的
prototype多例的
requestWEB项目中,Spring创建一个Bean的对象,将对象存入到request域中
sessionWEB项目中,Spring创建一个Bean的对象,将对象存入到session域中
global sessionWEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession 相当于 session
  • 当scope的取值为singleton时

    • Bean的实例化个数:1个

    • Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

    • Bean的生命周期:

      • 对象创建:当应用加载,创建容器时,对象就被创建了
      • 对象运行:只要容器在,对象一直活着
      • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
      • 当scope的取值为singleton时,对象的生命周期与容器同在
    • 测试

        /*测试scope属性,scope="singleton"的效果*/
          @Test
          public void test4(){
              ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
      
              IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
              IUserDao userDao2 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
      
              //两者的值都一样,为同一个对象
              System.out.println(userDao == userDao2);//true
              System.out.println(userDao);//com.myLagou.dao.impl.UserDao@294425a7
              System.out.println(userDao2);//com.myLagou.dao.impl.UserDao@294425a7
          }
      

在这里插入图片描述

  • 当scope的取值为prototype时

    • Bean的实例化个数:多个

    • Bean的实例化时机:当调用getBean()方法时实例化Bean

    • Bean的生命周期:

      • 对象创建:当使用对象时,创建新的对象实例
      • 对象运行:只要对象在使用中,就一直活着
      • 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
    • 测试

        /*测试scope属性,scope="prototype"的效果*/
          @Test
          public void test5(){
              ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
      
              IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
              IUserDao userDao2 = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
      
              //两者的值不一样,不是同一个对象,而是每次从容器中获取bean对象都会创建一个新的bean对象
              System.out.println(userDao == userDao2);//false
              System.out.println(userDao);//com.myLagou.dao.impl.UserDao@294425a7
              System.out.println(userDao2);//com.myLagou.dao.impl.UserDao@67d48005
          }
      

在这里插入图片描述

5.3 Bean生命周期配置

 <!--使用bean标签在spring配置文件中配置UserDaoImpl类-->
    <!--id:表示这个bean标签的唯一标识   class:表示这个bean组件所配置的类的全类名
        scope="singleton"表示创建的bean对象是单例的,为默认值
        scope="prototype"表示创建的bean对象是多例的,每次从容器中获取bean对象都会创建一个新的bean对象
    -->
    <bean id="userDao" class="com.myLagou.dao.impl.UserDao" scope="prototype" init-method="init" destroy-method="destroy"></bean>


* init-method:指定类中的初始化方法名称 
* destroy-method:指定类中销毁方法名称

接口实现类UserDao中添加是初始化和销毁方法

public class UserDao implements IUserDao {
    public void init(){
        System.out.println("初始化方法执行了!!!");
    }

    public void save() {
        System.out.println("dao层被调用了");
    }

    public void destroy(){
        System.out.println("销毁方法执行了!!!");
    }
}

测试

在这里插入图片描述

5.4 Bean实例化三种方式

  • 无参构造方法实例化

  • 工厂静态方法实例化

  • 工厂普通方法实例化

5.4.1 无参构造方法实例化

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

<bean id="userDao" class="com.myLagou.dao.impl.UserDao"></bean>

5.4.2 工厂静态方法实例化

应用场景

依赖的jar包中有个A类(如StaticFactoryBean),A类中有个静态方法m1(如createUserDao()),m1方法的返回值是一个B对象(如 return new UserDao(); )。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

jar包中的工具类A

public class StaticFactoryBean {
    public static IUserDao createUserDao(){
        return new UserDao();
    }
}

xml文件配置

<!--方式2:通过工厂静态方法实例化bean对象-->
    <!--factory-method="createUserDao"表示把属性所指定的方法的返回值放到容器中-->
    <bean id="userDao3" class="com.myLagou.factory.StaticFactoryBean" factory-method="createUserDao"></bean> 


测试

   /*测试工厂静态方法实例化对象*/
    @Test
    public void test6(){
        //获取到了spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象,加载配置文件的同时就创建了bean对象,并且存到了ioc容器中
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

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

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

5.4.3 工厂普通方法实例化

应用场景

依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,

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

jar包中的工具类A

public class DynamicFactoryBean {
    //普通方法
    public IUserDao createUserDao(){
        return new UserDao();
    }
}

xml文件配置

<!--方式3:通过普通工厂方法实例化bean对象-->
    <!--factory-bean="dynamicFactoryBean"表示引用配置的dynamicFactoryBean-->
    <!--factory-method="createUserDao"表示把属性所指定的方法的返回值放到容器中-->
    <bean id="dynamicFactoryBean" class="com.myLagou.factory.DynamicFactoryBean"></bean>
    <bean id="userDao4"  factory-bean="dynamicFactoryBean" factory-method="createUserDao"></bean>

测试

   /*测试普通工厂方法实例化对象*/
    @Test
    public void test7(){
        //获取到了spring上下文对象,借助上下文对象可以获取到IOC容器中的bean对象,加载配置文件的同时就创建了bean对象,并且存到了ioc容器中
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

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

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

5.5 Bean依赖注入概述

依赖注入 DI(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现

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

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

下面这个举例说明了业务层和持久层的依赖关系是自己手动维护的

dao层

//接口
public interface IUserDao {
    public void save();
}


//实现类
public class UserDao implements IUserDao {

    public void save() {
        System.out.println("dao层被调用了");
    }
}

service层

//接口
public interface IUserService {

    public void save();
}


//实现类
public class UserServiceImpl implements IUserService {
    @Override
    public void save() {
        //调用dao层的save方法
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");
        userDao.save();
    }
}

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

    <!--方式1:通过无参构造方法实例化bean对象-->
    <bean id="userDao" class="com.myLagou.dao.impl.UserDao"></bean>


    <!--配置UserService-->
    <bean id="userService" class="com.myLagou.service.impl.UserServiceImpl"></bean>

</beans>

测试

    @Test
    public void test8(){
        //没有在xml文件中配置UserService的bean对象时的写法
//        IUserService userService = new UserServiceImpl();
//        userService.save();

        //在xml文件中配置了UserService对象时的写法
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

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

5.6 Bean依赖注入方式

5.6.1 构造方法

在UserServiceImpl中创建有参构造

public class UserServiceImpl implements IUserService {
    //声明一个成员变量来接收注入进来的UserDao实例
    private IUserDao userDao;

    public UserServiceImpl(IUserDao userDao) {
        this.userDao = userDao;
    }

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

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

<!--配置UserService-->
<bean id="userService" class="com.myLagou.service.impl.UserServiceImpl">
    <!--<constructor-arg>标签表示在解析xml文件时看到这个标签就说明此bean对象的实例化使用的是有参构造,而不是默认的无参构造-->
    <!--
    index="0"表示给有参构造器中的第一个参数进行赋值
    type表示给第一个参数赋值的类型
    ref表示引入ioc中当前的哪个对象来作为要传递的参数注入到第一个参数中
    -->
<!--    <constructor-arg index="0" type="com.myLagou.dao.IUserDao" ref="userDao"></constructor-arg>-->
    
    
    <!--简化版配置-->
    <!--
    name="userDao":其值要与service的实现类中的有参构造器中的参数名一致
    -->
    <constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

测试

     /*测试bean依赖注入之有参构造*/
    @Test
    public void test9(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

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

5.6.2 set方法

set方式完成依赖注入是最常使用的

在UserServiceImpl中创建set方法

public class UserServiceImpl implements IUserService {
    //声明一个成员变量来接收注入进来的UserDao实例
    private IUserDao userDao;

    //测试依赖注入之set方法
    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;                     
    }

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

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

<!--使用set方法完成依赖注入时所配置的UserService-->
    <bean id="userService" class="com.myLagou.service.impl.UserServiceImpl">
        <!--name="userDao":其值要与service的实现类中的set方法的set之后的首字母小写一致-->
        <property name="userDao" ref="userDao"></property>
    </bean>

测试

 /*测试bean依赖注入之set方法*/
    @Test
    public void test9(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

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

5.6.3 P命名空间注入

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

首先,需要在spring的配置文件中引入P命名空间:

xmlns:p="http://www.springframework.org/schema/p"

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

 <!--使用p命名空间的方法完成依赖注入时所配置的UserService-->
    <bean id="userService" class="com.myLagou.service.impl.UserServiceImpl" p:userDao-ref="userDao">
        <!--p:userDao-ref="userDao":由于已经在上面配置了userDao,所以直接使用p:userDao-ref属性了-->
    </bean>

5.7 Bean依赖注入的数据类型

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

注入数据的三种数据类型

1. 普通数据类型

2. 引用数据类型

3. 集合数据类型

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

5.7.1 注入普通数据类型

dao层接口实现类

public class UserDao implements IUserDao {
    private String username;
    private Integer age;

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

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

    public void save() {
        System.out.println(username);
        System.out.println(age);
        System.out.println("dao层被调用了");
    }
}

xml配置文件

    <bean id="userDao" class="com.myLagou.dao.impl.UserDao">
        <!--value=""用于普通数据类型的依赖注入-->
        <property name="username" value="杨洋"></property>
        <property name="age" value="30"></property>
    </bean>

测试

@Test
    public void test9(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

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

5.7.2 注入集合数据类型

1) List集合注入

user实体类

package com.myLagou.entity;

/**
 * @author zhy
 * @create 2022-08-12 20:31
 */
public class User {
    private String username;
    private Integer age;

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

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

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

dao层接口实现类

public class UserDao implements IUserDao {
    private String username;
    private Integer age;

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

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

    public void save() {
        System.out.println(list);
        System.out.println("dao层被调用了");
    }
}

xml配置文件

    <bean id="userDao" class="com.myLagou.dao.impl.UserDao">
        <!--value=""用于普通数据类型的依赖注入-->
        <property name="username" value="蔡徐坤"></property>
        <property name="age" value="24"></property>

        <!--**注入集合数据类型**-->
        <!--list类型的数据完成依赖注入-->
        <property name="list">
            <!--此list的泛型为Object-->
            <list>
                <!--注入list中的普通数据类型-->
                <value>杨洋最帅</value>
                <!--注入list中的对象数据类型-->
                <ref bean="user"></ref>
            </list>
        </property>
    </bean>

测试

@Test
    public void test9(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

        //调用Dao层的方法
        userService.save();
    }
2) Set集合注入

dao层接口实现类

public class UserDao implements IUserDao {
    //注入set集合数据类型
    private Set<Object> set;

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

    public void save() {
        System.out.println("set注入" + set);
        
        System.out.println("dao层被调用了");
    }
}

xml配置文件

    <bean id="userDao" class="com.myLagou.dao.impl.UserDao">
        
        <!--set类型的数据完成依赖注入-->
        <property name="set">
            <!--此set的泛型为Object-->
            <set>
                <!--注入set中的普通数据类型-->
                <value>张新成</value>
                <!--注入set中的对象数据类型-->
                <ref bean="user"></ref>
            </set>
        </property>
    </bean>

测试

@Test
    public void test9(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

        //调用Dao层的方法
        userService.save();
    }
3) Array数组注入

dao层接口实现类

public class UserDao implements IUserDao {
     //注入array数组数据类型
    private Object[] array;

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

    public void save() {
        System.out.println("array注入" + Arrays.toString(array));//array注入[王鹤棣, User{username='于途', age=30}]
        System.out.println("dao层被调用了");
    }
}

xml配置文件

    <bean id="userDao" class="com.myLagou.dao.impl.UserDao">
        <!--array类型的数据完成依赖注入-->
        <property name="array">
            <!--此array的类型为Object-->
            <array>
                <!--注入array中的普通数据类型-->
                <value>王鹤棣</value>
                <!--注入array中的对象数据类型-->
                <ref bean="user"></ref>
            </array>
        </property>
    </bean>

测试

@Test
    public void test9(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

        //调用Dao层的方法
        userService.save();
    }
4) Map集合注入

dao层接口实现类

public class UserDao implements IUserDao {
    //注入map集合数据类型
    private Map<String, Object> map;

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
    public void save() {
       System.out.println("map注入" + map);
        //map注入{key1=迪丽热巴, key2=User{username='于途', age=30}}
        System.out.println("dao层被调用了");
    }
}

xml配置文件

    <bean id="userDao" class="com.myLagou.dao.impl.UserDao">
          <!--map类型的数据完成依赖注入-->
        <property name="map">
            <map>
                <!--注入map中的普通数据类型-->
                <entry key="key1" value="迪丽热巴">
                    <!--元素 'key' 必须不含字符 [子级], 因为该类型的内容类型为“仅元素”。这样写会报错-->
<!--                    <key>key1</key>-->
<!--                    <value>迪丽热巴</value>-->
                </entry>
                <!--注入map中的对象数据类型-->
                <entry key="key2" value-ref="user"></entry>
            </map>
        </property>
    </bean>

测试

@Test
    public void test9(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

        //调用Dao层的方法
        userService.save();
    }
5) Properties配置注入

dao层接口实现类

public class UserDao implements IUserDao {
    //注入properties数据类型
    private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    
    public void save() {
        System.out.println("properties注入" + properties);//properties注入{k1=杨幂, k2=毛不易, k3=彭晓冉}
        System.out.println("dao层被调用了");
    }
}

xml配置文件

    <bean id="userDao" class="com.myLagou.dao.impl.UserDao">
          <!--properties类型的数据完成依赖注入-->
        <property name="properties">
            <props>
                <!--注入properties中的普通数据类型-->
                <!--properties的key和value都是String类型-->
                <prop key="k1">杨幂</prop>
                <prop key="k2">毛不易</prop>
                <prop key="k3">彭晓冉</prop>
            </props>
        </property>
    </bean>

测试

@Test
    public void test9(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

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

5.8 配置文件模块化

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

  • 拆分原则:
    • 按层级拆分为dao层、service层等
    • 按业务模块来拆分

1)并列的多个配置文件同时加载

@Test
    public void test10(){
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext-service.xml","applicationContext-dao.xml");//同时加载多个配置文件

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

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

2)主从配置文件

<!--在applicationContext.xml配置文件中引入其他配置文件-->

<!--借助import标签引入其他的配置文件-->
    <import resource="classpath:applicationContext-dao.xml"></import>
    <import resource="classpath:applicationContext-service.xml"></import>
    @Test
    public void test10(){
        //同时加载多个配置文件
//        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext-service.xml","applicationContext-dao.xml");
        //使用主从配置文件来加载多个配置文件,先在applicationContext.xml文件中使用import引入其他的配置文件
        ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

        IUserService userService = (IUserService) classPathXmlApplicationContext.getBean("userService");

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

注意:

  • 同一个xml中不能出现相同名称的bean,如果出现会报错

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

5.9 知识小结

Spring的重点配置

  • <bean>标签:创建对象并放到spring的IOC容器

    • id属性:在容器中Bean实例的唯一标识,不允许重复
    • class属性:要实例化的Bean的全限定名
    • scope属性:Bean的作用范围,常用是Singleton(默认)和prototype
  • <constructor-arg>标签:属性注入

    • name属性:属性名称
    • value属性:注入的普通属性值
    • ref属性:注入的对象引用值
  • <property>标签:属性注入

    • name属性:属性名称
    • value属性:注入的普通属性值
    • ref属性:注入的对象引用值
    • <list>
    • <set>
    • <array>
    • <map>
    • <props>
  • <import>标签:导入其他的Spring的分文件

DbUtils(IOC实战)

6.1 DbUtils是什么?

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

核心对象

QueryRunner queryRunner = new QueryRunner(DataSource dataSource);

核心方法

  • int update(); 执行增、删、改语句

  • T query(); 执行查询语句

    ​ ResultSetHandler<T> 这是一个接口,主要作用是将数据库返回的记录封装到实体对象

举个栗子

查询数据库所有账户信息到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案例

步骤分析

1. 准备数据库环境 
2. 创建java项目,导入坐标 
3. 编写Account实体类 
4. 编写AccountDao接口和实现类 
5. 编写AccountService接口和实现类 
6. 编写spring核心配置文件 
7. 编写测试代码

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项目,导入坐标

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

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

3)编写Account实体类

public class Account { 

    private Integer id; 

    private String name; 

    private Double money; 
    
    //get、set、toString方法略

}

4)编写AccountDao接口和实现类

接口

package com.myLagou.dao;

import com.myLagou.entity.Account;

import java.util.List;

/**
 * @author zhy
 * @create 2022-08-12 22:56
 */
public interface AccountDao {

    //增
    public void save(Account account);

    //删
    public void delete(Integer id);
    
    //改
    public void update(Account account);
    
    //查   查询所有
    public List<Account> findAll();

    //根据id进行查询
    public Account findById(Integer id);
}

实现类

package com.myLagou.dao.impl;

import com.myLagou.dao.AccountDao;
import com.myLagou.entity.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.SQLException;
import java.util.List;

/**
 * @author zhy
 * @create 2022-08-12 23:00
 */
public class AccountDaoImpl implements AccountDao {
    //定义全员变量来接收从spring容器中获取的queryRunner对象
    private QueryRunner queryRunner;

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

    public void save(Account account) {
        //编写sql
        String sql = "insert into account values(null, ?, ?)";

        try {
            int save = queryRunner.update(sql, account.getName(), account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void delete(Integer id) {
        //编写sql
        String sql = "delete from account where id = ?";
        try {
            int delete = queryRunner.update(sql, id);
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    public void update(Account account) {
        //编写sql
        String sql = "update account set name = ?, money = ? where id = ?";

        try {
            int update = queryRunner.update(sql, account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

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

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

        return queryList;
    }

    public Account findById(Integer id) {
        Account query = null;
        //编写sql
        String sql = "select * from account where id = ?";

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

5)编写AccountService接口和实现类

接口

package com.myLagou.service;

import com.myLagou.entity.Account;

import java.util.List;

/**
 * @author zhy
 * @create 2022-08-12 23:33
 */
public interface AccountService {
    //增
    public void save(Account account);

    //删
    public void delete(Integer id);

    //改
    public void update(Account account);

    //查   查询所有
    public List<Account> findAll();

    //根据id进行查询
    public Account findById(Integer id);
}

实现类

package com.myLagou.service.impl;

import com.myLagou.dao.AccountDao;
import com.myLagou.entity.Account;
import com.myLagou.service.AccountService;

import java.util.List;

/**
 * @author zhy
 * @create 2022-08-12 23:33
 */
public class AccountServiceImpl implements AccountService {
    //set注入AccountDao对象
    private AccountDao accountDao;

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

    public void save(Account account) {
        accountDao.save(account);
        
    }

    public void delete(Integer id) {
        accountDao.delete(id);
    }

    public void update(Account account) {
        accountDao.update(account);
    }

    public List<Account> findAll() {
        return accountDao.findAll();
    }

    public Account findById(Integer id) {
        return accountDao.findById(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"
       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"
>
    <!--配置数据源dataSource,传递给queryRunner-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///spring_db"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123123"></property>

    </bean>
    <!--queryRunner 传递给accountDao-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <!--有参构造器注入-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <bean id="accountDao" class="com.myLagou.dao.impl.AccountDaoImpl">
        <!--set注入-->
        <property name="queryRunner" ref="queryRunner"></property>
    </bean>

    <bean id="accountService" class="com.myLagou.service.impl.AccountServiceImpl">
        <!--set注入-->
        <property name="accountDao" ref="accountDao"></property>
    </bean>
   
</beans>

7)编写测试代码

package com.myLagou.test;

import com.myLagou.entity.Account;
import com.myLagou.service.AccountService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.annotation.Target;
import java.util.List;

/**
 * @author zhy
 * @create 2022-08-13 11:25
 */
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("杨洋");
        account.setMoney(9999.00);

        accountService.save(account);
        System.out.println(account);
    }

    //测试根据id查询
    @Test
    public void testFindById(){
        Account byId = accountService.findById(2);
        System.out.println(byId);
    }

    //测试查询所有
    @Test
    public void testFindAll(){
        List<Account> list = accountService.findAll();

        for (Account account : list) {
            System.out.println(account);
        }
    }

    //测试更新
    @Test
    public void testUpdate(){
        Account account = new Account();
        account.setId(1);
        account.setName("jack");
        account.setMoney(8000.00);
        accountService.update(account);

        System.out.println(account);
    }

    //测试删除
    @Test
    public void testDelete(){

        accountService.delete(2);

    }
}

8)抽取jdbc配置文件

applicationContext.xml加载 jdbc.properties配置文件获得连接信息。

首先,需要引入context命名空间和约束路径

  • 命名空间:

    • xmlns:context=“http://www.springframework.org/schema/context”
  • 约束路径:

    • http://www.springframework.org/schema/context
    • http://www.springframework.org/schema/context/spring-context.xsd

jdbc.properties文件

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db
jdbc.username=root
jdbc.password=123123

在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"
       xmlns:p="http://www.springframework.org/schema/p"
       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-->
    <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>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>

    </bean>
    
</beans>

6.3 知识小结

  • DataSource的创建权交由Spring容器去完成

  • QueryRunner的创建权交由Spring容器去完成,使用构造方法传递DataSource

  • Spring容器加载properties文件

    • <context:property-placeholder location=“xx.properties”/>
    • <property name=“” value=“${key}”/>

Spring注解开发

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

7.1 Spring常用注解

7.1.1 介绍

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

注解说明
@Component使用在类上用于实例化Bean
@Controller使用在web层类上用于实例化Bean
@Service使用在service层类上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入,加入所标注的是一个接口类型字段,且spring容器中有多个此接口的实现类,那么将会导致报错
@Qualifier结合@Autowired一起使用,不能单独使用,根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

在这里插入图片描述

示例

public class AccountServiceImpl implements AccountService {

    //使用注解进行注入
    @Autowired//在字段上用于根据类型依赖注入
    @Qualifier("accountDao")//==结合@Autowired一起使用,不能单独使用==,根据名称进行依赖注入
    //@Resource//相当于@Autowired+@Qualifier,按照名称进行注入
    private AccountDao accountDao;

    @Value("于途")//注入普通属性
    private String username;

    public void save(Account account) {
        accountDao.save(account);

    }
     .
     .
     .
}

说明:

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.myLagou"></context:component-scan>

7.1.2 实现

1) Bean实例化(IOC)

 <bean id="accountService" class="com.myLagou.service.impl.AccountServiceImpl">
        <!--set注入-->
        <property name="accountDao" ref="accountDao"></property>
    </bean>

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

@Service(value = "accountService")//使用此注解相对于在xml文件中配置了bean标签,value属性相对于配置了id
public class AccountServiceImpl implements AccountService {
   		。
        。
        。
}

2)属性依赖注入(DI)

 <bean id="accountService" class="com.myLagou.service.impl.AccountServiceImpl">
        <!--set注入-->
        <property name="accountDao" ref="accountDao"></property>
        <property name="username" value="蔡徐坤"></property>
    </bean>

使用@Autowired或者@Autowired+@Qulifier或者@Resource进行accountDao的注入

@Service(value = "accountService")//使用此注解相对于在xml文件中配置了bean标签,value属性相对于配置了id
public class AccountServiceImpl implements AccountService {
    
    //使用注解进行注入
    @Autowired//在字段上用于根据类型依赖注入
    @Qualifier("accountDao")//==结合@Autowired一起使用,不能单独使用==,根据名称进行依赖注入
//    @Resource(name = "accountDao")//相当于@Autowired+@Qualifier,按照名称进行注入
    private AccountDao accountDao;
   		。
        。
        。
} 

3) @Value

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

@Service(value = "accountService")//使用此注解相对于在xml文件中配置了bean标签,value属性相对于配置了id
public class AccountServiceImpl implements AccountService {
    
    //使用注解进行注入
    @Autowired//在字段上用于根据类型依赖注入
    @Qualifier("accountDao")//==结合@Autowired一起使用,不能单独使用==,根据名称进行依赖注入
//    @Resource(name = "accountDao")//相当于@Autowired+@Qualifier,按照名称进行注入
    private AccountDao accountDao;

    @Value("于途")//注入普通属性
    private String username;
    //使用@value注解获取到配置文件中的值
    @Value("${jdbc.driverClassName}")
    private String driver;
   		。
        。
        。
} 

4) @Scope

<bean scope=""/>

使用@Scope标注Bean的范围

@Service(value = "accountService")//使用此注解相对于在xml文件中配置了bean标签,value属性相对于配置了id
//@Scope("prototype")//表示此bean是多例的
@Scope("singleton")//表示此bean是单例的
public class AccountServiceImpl implements AccountService {
   		。
        。
        。
}

5) Bean生命周期

<bean init-method="init" destroy-method="destory" />

使用@PostConstruct标注初始化方法,使用@PreDestroy标注销毁方法

@Service(value = "accountService")//使用此注解相对于在xml文件中配置了bean标签,value属性相对于配置了id
//@Scope("prototype")//表示此bean是多例的
@Scope("singleton")//表示此bean是单例的
public class AccountServiceImpl implements AccountService {
    
       //初始化方法
    @PostConstruct
    public void init(){
        System.out.println("初始化方法。。。");
    }

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

7.2 Spring常用注解整合DbUtils

步骤分析

1. 拷贝xml配置项目,改为注解配置项目 
2. 修改AccountDaoImpl实现类 
3. 修改AccountServiceImpl实现类 
4. 修改spring核心配置文件 
5. 编写测试代码

1)拷贝xml配置项目,改为常用注解配置项目spring_dbUtils_anno

​ 过程略…

2)修改AccountDaoImpl实现类

@Repository//使用此注解相对于在xml文件中配置了bean标签
public class AccountDaoImpl implements AccountDao {
    //定义全员变量来接收从spring容器中获取的queryRunner对象
    @Autowired//使用注解的方式注入依赖
    private QueryRunner queryRunner;


    public void save(Account account) {
        //编写sql
        String sql = "insert into account values(null, ?, ?)";

        try {
            int save = queryRunner.update(sql, account.getName(), account.getMoney());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public void delete(Integer id) {
        //编写sql
        String sql = "delete from account where id = ?";
        try {
            int delete = queryRunner.update(sql, id);
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    public void update(Account account) {
        //编写sql
        String sql = "update account set name = ?, money = ? where id = ?";

        try {
            int update = queryRunner.update(sql, account.getName(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

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

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

        return queryList;
    }

    public Account findById(Integer id) {
        Account query = null;
        //编写sql
        String sql = "select * from account where id = ?";

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

3)修改AccountServiceImpl实现类

@Service(value = "accountService")//使用此注解相对于在xml文件中配置了bean标签,value属性相对于配置了id
public class AccountServiceImpl implements AccountService {

    //使用注解进行注入
    @Autowired//在字段上用于根据类型依赖注入
    @Qualifier("accountDao")//==结合@Autowired一起使用,不能单独使用==,根据名称进行依赖注入
    private AccountDao accountDao;


    public void save(Account account) {
        accountDao.save(account);

    }

    public void delete(Integer id) {
        accountDao.delete(id);
    }

    public void update(Account account) {
        accountDao.update(account);
    }

    public List<Account> findAll() {

        return accountDao.findAll();
    }

    public Account findById(Integer id) {
        return accountDao.findById(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:p="http://www.springframework.org/schema/p"
       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-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!--注解扫描组件,使得配置的主任依赖的注解可以生效-->
    <context:component-scan base-package="com.myLagou"></context:component-scan>

    <!--配置数据源dataSource,传递给queryRunner-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!--queryRunner 传递给accountDao-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <!--有参构造器注入-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

</beans>

5)编写测试代码

 //测试查询所有
    @Test
    public void testFindAll(){
        List<Account> list = accountService.findAll();

        for (Account account : list) {
            System.out.println(account);
        }
    }

//其他的测试略

7.3 Spring新注解

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

  • 非自定义的Bean的配置:<bean>

  • 加载properties文件的配置:context:property-placeholder

  • 组件扫描的配置:context:component-scan

  • 引入其他文件:<import>

上述执行标签都是在applicationContext.xml配置文件中使用,而下面的这些注解都是在被@Configuration注解标注的类中使用

注解说明
@Configuration用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解
@Bean使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource用于加载 properties 文件中的配置
@ComponentScan用于指定 Spring 在初始化容器时要扫描的包
@Import用于导入其他配置类

7.4 Spring纯注解整合DbUtils

7.4.1 介绍

步骤分析

1. 编写Spring核心配置类
2. 编写数据库配置信息类 
3. 编写测试代码

7.4.2 实现

1)编写Spring核心配置类SpringConfig

在没有把数据库类抽离之前,都是写在这个核心配置类中的

package com.myLagou.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

/**
 * @author zhy
 * @create 2022-08-13 16:28
 */
@Configuration//用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan("com.myLagou")//用于指定 Spring 在初始化容器时要扫描的包
@PropertySource(value = "classpath:jdbc.properties")//用于加载 properties 文件中的配置
//@Import(DataSourceConfig.class)//当数据源的配置被单独抽取出去之后,就需要使用此注解导入,该注解用来导入其他配置类
public class SpringConfig {

    @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")//使用在方法上,标注将该方法的返回值存储到 Spring 容器中
    //@Bean("dataSource")中的value值表示给返回的数据源在ioc中起别名,如果不起别名,则默认数据源的名称为方法名
    public DataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);

        return druidDataSource;
    }

    @Bean("queryRunner")
    /*由于QueryRunner中需要传递一个数据源,所以就在参数位置传递,使用注解告诉spring去ioc容器中找数据源*/
    public QueryRunner getQueryRunner(@Autowired DataSource dataSource){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        return queryRunner;
    }

}

2)优化,将数据库配置类抽离出来,编写数据库配置信息类DataSourceConfig

package com.myLagou.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

/**
 * @author zhy
 * @create 2022-08-13 16:54
 */
@PropertySource(value = "classpath:jdbc.properties")
public class DataSourceConfig {
    @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")//使用在方法上,标注将该方法的返回值存储到 Spring 容器中
    //@Bean("dataSource")中的value值表示给返回的数据源在ioc中起别名,如果不起别名,则默认数据源的名称为方法名
    public DataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);

        return druidDataSource;
    }
}

3)编写测试代码

  /*测试纯注解来整合spring和dbutils*/
    @Test
    public void testAnno(){
        List<Account> list = accountService.findAll();
        for (Account account : list) {
            System.out.println(account);
        }
    }

Spring整合Junit

8.1普通Junit测试问题

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

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = applicationContext.getBean(AccountService.class);

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

8.2 Spring整合Junit

步骤分析

1. 导入spring集成Junit的坐标 
2. 使用@Runwith注解替换原来的运行器 
3. 使用@ContextConfiguration指定配置文件或配置类 
4. 使用@Autowired注入需要测试的对象 
5. 创建测试方法进行测试

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(SpringJUnit4ClassRunner.class)//@RunWith()指定junit的运行环境,
// SpringJUnit4ClassRunner.class是spring提供的作为junit运行环境的类
public class AccountServiceTest {
    ....
}

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

@RunWith(SpringJUnit4ClassRunner.class)//@RunWith()指定junit的运行环境,
// SpringJUnit4ClassRunner.class是spring提供的作为junit运行环境的类
//@ContextConfiguration({"classpath:applicationContext.xml"})//用来指定核心配置文件
@ContextConfiguration(classes = {SpringConfig.class})//用来指定核心配置类
public class AccountServiceTest {
       
    ......
}

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

@RunWith(SpringJUnit4ClassRunner.class)//@RunWith()指定junit的运行环境,
// SpringJUnit4ClassRunner.class是spring提供的作为junit运行环境的类
//@ContextConfiguration({"classpath:applicationContext.xml"})//用来指定核心配置文件
@ContextConfiguration(classes = {SpringConfig.class})//用来指定核心配置类
public class AccountServiceTest {
        //spring和junit整合之后,可以直接使用注解的方式来加载accountService,而不需要使用上述两种方式
    @Autowired
    private AccountService accountService;
    ......
}

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

 @Test
    public void testAnno(){
        List<Account> list = accountService.findAll();
        for (Account account : list) {
            System.out.println(account);
        }
    }

idDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);

    return druidDataSource;
}

}




**3)编写测试代码**

```java
  /*测试纯注解来整合spring和dbutils*/
    @Test
    public void testAnno(){
        List<Account> list = accountService.findAll();
        for (Account account : list) {
            System.out.println(account);
        }
    }

Spring整合Junit

8.1普通Junit测试问题

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

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = applicationContext.getBean(AccountService.class);

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

8.2 Spring整合Junit

步骤分析

1. 导入spring集成Junit的坐标 
2. 使用@Runwith注解替换原来的运行器 
3. 使用@ContextConfiguration指定配置文件或配置类 
4. 使用@Autowired注入需要测试的对象 
5. 创建测试方法进行测试

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(SpringJUnit4ClassRunner.class)//@RunWith()指定junit的运行环境,
// SpringJUnit4ClassRunner.class是spring提供的作为junit运行环境的类
public class AccountServiceTest {
    ....
}

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

@RunWith(SpringJUnit4ClassRunner.class)//@RunWith()指定junit的运行环境,
// SpringJUnit4ClassRunner.class是spring提供的作为junit运行环境的类
//@ContextConfiguration({"classpath:applicationContext.xml"})//用来指定核心配置文件
@ContextConfiguration(classes = {SpringConfig.class})//用来指定核心配置类
public class AccountServiceTest {
       
    ......
}

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

@RunWith(SpringJUnit4ClassRunner.class)//@RunWith()指定junit的运行环境,
// SpringJUnit4ClassRunner.class是spring提供的作为junit运行环境的类
//@ContextConfiguration({"classpath:applicationContext.xml"})//用来指定核心配置文件
@ContextConfiguration(classes = {SpringConfig.class})//用来指定核心配置类
public class AccountServiceTest {
        //spring和junit整合之后,可以直接使用注解的方式来加载accountService,而不需要使用上述两种方式
    @Autowired
    private AccountService accountService;
    ......
}

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

 @Test
    public void testAnno(){
        List<Account> list = accountService.findAll();
        for (Account account : list) {
            System.out.println(account);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值