阶段四:热门框架(第一章:Spring框架1)

阶段四:热门框架(第一章:Spring框架1)

1-Spring框架

学习目标

  • 能够说出Spring的体系结构
  • 能够编写IOC入门案例
  • 能够编写DI入门案例
  • 能够配置setter方式注入属性值
  • 能够配置构造方式注入属性值
  • 能够理解什么是自动装配

一、Spring简介

1、Spring课程介绍

问题导入

我们为什么要学习Spring框架?

1.1 为什么要学
  • Spring技术是JavaEE开发必备技能,企业开发技术选型命中率>90%

  • 专业角度

    • 简化开发,降低企业级开发的复杂性
    • 框架整合,高效整合其他技术,提高企业级应用开发与运行效率

在这里插入图片描述

1.2 学什么
  • 简化开发

    • IOC(反转控制)
    • AOP(面向切面编程)
      • 事务处理
  • 框架整合

    • MyBatis
    • MyBatis-plus
    • Struts
    • Struts2
    • Hibernate
    • ……
1.3 怎么学
  • 学习Spring框架设计思想
  • 学习基础操作,思考操作与思想间的联系
  • 学习案例,熟练应用操作的同时,体会思想
    在这里插入图片描述

2、初识Spring

问题导入

目前我们使用的是Spring几版本?

2.1 Spring家族
  • 官网:https://spring.io
  • Spring发展到今天已经形成了一种开发的生态圈,Spring提供了若干个项目,每个项目用于完成特定的功能。
    在这里插入图片描述
2.2 Spring发展史

在这里插入图片描述

3、Spring体系结构

问题导入

通过系统架构图,Spring能不能进行数据层开发?Spring能不能进行web层开发?

3.1、Spring Framework系统架构图
  • Spring Framework是Spring生态圈中最基础的项目,是其他项目的根基
    在这里插入图片描述
    在这里插入图片描述
3.2、Spring Framework课程学习路线

在这里插入图片描述

4 Spring核心概念

问题导入

问题1:目前我们的代码存在什么问题以及怎么解决这些问题?
问题2:请描述什么是IOC,什么是DI?

4.1、目前我们代码存在的问题

在这里插入图片描述

  • 代码书写现状
    • 耦合度偏高
  • 解决方案
    • 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象
4.2、核心概念
  • IOC(Inversion of Control)控制反转
    对象的创建控制权由程序转移到外部,这种思想称为控制反转
      使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。通俗的讲就是“将new对象的权利交给Spring,我们从Spring中获取对象使用即可

  • Spring技术对IoC思想进行了实现

    • Spring提供了一个容器,称为IOC容器,用来充当IoC思想中的“外部
    • IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中统称为Bean
  • DI(Dependency Injection)依赖注入

    • 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入

在这里插入图片描述

  • 目标:充分解耦
    • 使用IoC容器管理bean(IOC)
    • IoC容器内将有依赖关系的bean进行关系绑定(DI)
  • 最终效果
    • 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系

二、IOC和DI入门案例 【重点】

1、IOC入门案例【重点】

IOC:控制反转,将new对象的权利交给Spring,我们从Spring中获取对象使用即可

问题导入

<bean>标签中id属性和class属性的作用是什么?

1.1、入门案例思路分析
  1. 管理什么?(Service与Dao)
  2. 如何将被管理的对象告知IOC容器?(写配置文件)
  3. 被管理的对象交给IOC容器,如何获取到IoC容器?(接口)
  4. IOC容器得到后,如何从容器中获取bean?(接口方法)
  5. 使用Spring导入哪些坐标?(pom.xml)
1.2、实现步骤
【第一步】导入Spring坐标
【第二步】定义Spring管理的类(接口)
【第三步】创建Spring配置文件,配置对应类作为Spring管理的bean对象
【第四步】初始化IOC容器(Spring核心容器/Spring容器),通过容器获取bean对象
1.3、实现代码

【第一步】导入Spring坐标

<dependencies>
    <!--导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>

【第二步】定义Spring管理的类(接口)

  • BookDao接口和BookDaoImpl实现类
package com.itheima.dao;

public interface BookDao {
    public void save();
}
package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}
  • BookService接口和BookServiceImpl实现类
package com.itheima.service;

public interface BookService {
    public void save();
}
package com.itheima.service.impl;

public class BookServiceImpl implements BookService {
    //5.删除业务层中使用new的方式创建的dao对象
    private BookDao bookDao = new BookDaoImpl();

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

【第三步】创建Spring配置文件,配置对应类作为Spring管理的bean对象

  • 定义applicationContext.xml配置文件(在项目——src——resources——New——XML configuration File——Spring Config(点击)下面)并配置BookServiceImpl
<?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.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->

        <!--2.配置bean-->
        <!--bean标签表示配置bean
        id属性标示给bean起名字
        class属性表示给bean定义类型-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    </bean>
</beans>

注意事项:bean定义时id属性在同一个上下文中(IOC容器中)不能重复

【第四步】初始化IOC容器(Spring核心容器/Spring容器),通过容器获取Bean对象
com.itheima包下创建App2.java文件

package com.itheima;

public class App2 {
    public static void main(String[] args) {
        //1.创建IoC容器对象,加载spring核心配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        // BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        // bookDao.save();

        //2 从IOC容器中获取Bean对象(BookService对象)
        BookService bookService = (BookService) ctx.getBean("bookService");
        //3 调用Bean对象(BookService对象)的方法
        bookService.save();

    }
}
1.4、运行结果

在这里插入图片描述

执行错误问题: 运行时出现:错误: 不支持发行版本 5 Language level is invalid or missing in pom.xml. Current project JDK is 17.
原因 :当前JDK17版本不支持
解决办法 :在pom文件里添加

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

以上的代码还存在的问题是在BookServiceImpl.java程序中还存在 private BookDao bookDao= new BookDaoImpl();这种自己创建对象的方式(使用new的方式创建的Dao对象);

2、DI入门案例 【重点】

DI:依赖注入,在容器中建立bean与bean之间的依赖关系的整个过程
项目:spring_01_base_config

问题导入

<property>标签中name属性和ref属性的作用是什么?

property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean

2.1 DI入门案例思路分析
  1. 基于IOC管理bean
  2. Service中使用new形式创建的Dao对象是否保留?(不保留)
  3. Service中需要的Dao对象如何进入到Service中?(提供set方法,由IOC容器调用执行)
  4. Service与Dao间的关系如何描述?(写配置文件)
2.2 实现步骤
【第一步】删除使用new的形式创建对象的代码
【第二步】提供依赖对象对应的setter方法
【第三步】配置service与dao之间的关系
2.3 实现代码

【第一步】删除使用new的形式创建对象的代码(在BookServiceImpl.java中)

public class BookServiceImpl implements BookService {
    private BookDao bookDao;  //【第一步】删除使用new的形式创建对象的代码
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

【第二步】提供依赖对象对应的setter方法(在BookServiceImpl.java中)

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    //【第二步】提供依赖对象对应的setter方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

【第三步】配置service与dao之间的关系

在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">
    <!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->

    <!--2.配置bean-->
    <!--bean标签标示配置bean
    id属性标示给bean起名字
    class属性表示给bean定义类型-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">

        <!--3.配置server与dao的关系-->
        <!--property标签表示配置当前bean的属性
        name属性表示配置哪一个具体的属性
        ref属性表示参照哪一个bean-->
        <property name="bookDao" ref="bookDao"/>      
        
    </bean>

</beans>
2.4 图解演示

在这里插入图片描述

三、Bean的基础配置(下面被Spring注解开发所取代)

项目:spring_02_base_config

问题导入

问题1:在<bean>标签上如何配置别名?
问题2:Bean的默认作用范围是什么?如何修改?

1、Bean基础配置 (被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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->

        <!--2.配置bean-->
        <!--bean标签标示配置bean
        id属性标示给bean起名字
        class属性表示给bean定义类型-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
    </bean>
</beans>
运行结果

> 见《IOC入门案例》运行结果

2、Bean别名配置

配置说明

在这里插入图片描述

代码演示

在这里插入图片描述

打印结果

在这里插入图片描述

3、Bean作用范围配置【重点】

配置说明

在这里插入图片描述

扩展:scope的取值不仅仅只有singleton(单例)和prototype(非单例),还有request、session、application、 websocket ,表示创建出的对象放置在web容器(tomcat)对应的位置。比如:request表示保存到request域中。

代码演示

在这里插入图片描述

打印结果

在这里插入图片描述

最后给大家说明一下:在我们的实际开发当中,绝大部分的Bean是单例的,也就是说绝大部分Bean不需要配置scope属性。

为什么bean默认为单例?

  • 适合交给容器进行管理的bean
    表现层对象
    业务层对象
    数据层对象
    工具对象
  • 不适合交给容器进行管理的bean
    封装实体的域对象

四、Bean的实例化

项目:spring_03_bean_instance

问题导入

Bean的实例化方式有几种?

1、Bean是如何创建的【理解】

bean本质上就是对象,创建bean使用构造方法来完成

2、实例化Bean的三种方式

2.1、构造方法方式 【重点】
  • BookDaoImpl实现类
public class BookDaoImpl implements BookDao {
    public BookDaoImpl() {     //空参构造;
        System.out.println("book dao constructor is running ....");
    }
    public void save() {
        System.out.println("book dao save ...");
    }
}
  • applicationContext.xml配置(与上面说的一样)
<!--方式一:构造方法实例化bean-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
  • AppForInstanceBook测试类
public class AppForInstanceBook {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao = (BookDao) ctx.getBean("bookDao");

        bookDao.save();
    }
}
  • 运行结果
    在这里插入图片描述
    注意:空参构造方法如果不存在,将抛出异常BeanCreationException,无参构造方法无论是公有还是私有都是可以的,带参构造方法就会抛出上面的错误;
2.2、静态工厂方式
  • OrderDao接口和OrderDaoImpl实现类
package com.itheima.dao;

public interface OrderDao {
    public void save();
}
package com.itheima.dao.impl;

public class OrderDaoImpl implements OrderDao {
    public void save() {
        System.out.println("order dao save ...");
    }
}
  • OrderDaoFatory工厂类
package com.itheima.factory;

//静态工厂创建对象
public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        System.out.println("factory setup....");
        return new OrderDaoImpl();
    }
}
  • applicationContext.xml配置
<!--方式二:使用静态工厂实例化bean-->
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>

使用静态工厂实例化bean,创造的实际上是一个工厂对象,所以还要指定factory-method="getOrderDao"创造的是哪个对象;
在这里插入图片描述

  • AppForInstanceOrder测试类
package com.itheima;

public class AppForInstanceOrder {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");

        orderDao.save();
    }
}
  • 运行结果
    在这里插入图片描述
2.3、实例工厂方式
  • UserDao接口和UserDaoImpl实现类
package com.itheima.dao;

public interface UserDao {
    public void save();
}
package com.itheima.dao.impl;

public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("user dao save ...");
    }
}
  • UserDaoFactory工厂类
package com.itheima.factory;

//实例工厂创建对象
public class UserDaoFactory {
    public UserDao getUserDao(){
        return new UserDaoImpl();
    }
}
  • applicationContext.xml配置
<!--方式三:使用实例工厂实例化bean-->
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>

<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>

在这里插入图片描述

  • AppForInstanceUser测试类
package com.itheima;

public class AppForInstanceUser {
    public static void main(String[] args) {
        //        //创建实例工厂对象
        //        UserDaoFactory userDaoFactory = new UserDaoFactory();
        //        //通过实例工厂对象创建对象
        //        UserDao userDao = userDaoFactory.getUserDao();
        //        userDao.save();
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) ctx.getBean("userDao");  //默认是单例的;
        userDao.save();
    }
}
  • 运行结果
    在这里插入图片描述
2.4、实现FactoryBean<T>方式 【扩展,掌握】
  • 定义UserDaoFactoryBean实现FactoryBean<UserDao>

UserDaoFactoryBean中实例化什么类型的对象,泛型就是该类型。

//FactoryBean创建对象
package com.itheima.factory;

public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法;
    //得到Bean实例的;
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }
     //得到Bean类型的;
    public Class<?> getObjectType() {
        return UserDao.class;
    }
    //Bean对象的单例、双例也在此指定,而不再xml配置了,但由于是默认的,所以可以不写
}
  • applicationContext.xml配置
<!--方式四:使用FactoryBean实例化bean-->
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>

使用之前的AppForInstanceUser测试类去运行看结果就行了。注意配置文件中id="userDao"是否重复。

五、Bean的生命周期【了解】

问题导入

问题1:多例的Bean能够配置并执行销毁的方法?
问题2:如何做才执行Bean销毁的方法?

1、生命周期相关概念介绍

  • 生命周期:从创建到消亡的完整过程
  • bean生命周期:bean从创建到销毁的整体过程
  • bean生命周期控制:在bean创建后到销毁前做一些事情

2、代码演示

2.1 Bean生命周期控制(方式一:通过配置)
  • 提供生命周期控制方法
package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    //表示bean初始化对应的操作
    public void init(){
        System.out.println("init...");
    }
    //表示bean销毁前对应的操作
    public void destory(){
        System.out.println("destory...");
    }
}
  • applicationContext.xml配置
<!--init-method:设置bean初始化生命周期回调函数,此处填写init方法名-->
<!--destroy-method:设置bean销毁生命周期回调函数,仅适用于单例对象,此处填写destory方法名-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
  • 测试类
package com.itheima;

public class AppForLifeCycle {
    public static void main( String[] args ) {
        //此处需要使用实现类类型,接口类型没有close方法
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
        
        //注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
        //ctx.registerShutdownHook();
        
        //关闭容器,执行销毁的方法
        ctx.close();
    }
}

运行结果:
在这里插入图片描述

2.2 Bean生命周期控制(方式二:通过接口)

在2.1的基础上,我们对BookServiceImpl进行生命周期控制;

  • BookServiceImpl中实现InitializingBean, DisposableBean接口
package com.itheima.service.impl;

public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao) {
        System.out.println("set .....");
        this.bookDao = bookDao;
    }
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    public void destroy() throws Exception {
        System.out.println("service destroy");
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("service init");
    }
}
  • 测试类
package com.itheima;

public class AppForLifeCycle {
    public static void main( String[] args ) {
        //此处需要使用实现类类型,接口类型没有close方法
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
        
        //注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
        //ctx.registerShutdownHook();
        
        //关闭容器,执行销毁的方法
        ctx.close();
    }
}

运行结果:
在这里插入图片描述

3、Bean销毁时机

  • 容器关闭前触发bean的销毁
  • 关闭容器方式:
    • 手工关闭容器
      ConfigurableApplicationContext接口close()操作
    • 注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
      ConfigurableApplicationContext接口registerShutdownHook()操作
package com.itheima;

public class AppForLifeCycle {
    public static void main( String[] args ) {
        //此处需要使用实现类类型,接口类型没有close方法
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
        //注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
        ctx.registerShutdownHook();
        //关闭容器
        //ctx.close();
    }
}

六、依赖注入(DI配置) (被Spring注解开发所取代)

1、依赖注入方式 【重点】

问题导入

依赖注入有几种方式?

1.1、依赖注入的两种方式
  • setter注入
    简单类型
    引用类型(很常用)
  • 构造器注入
    简单类型
    引用类型
1.2、setter方式注入

项目spring_05_di_autoware

问题导入

setter方式注入使用什么子标签?

引用类型

在这里插入图片描述
上面是引用了一个,也可以引用多个:
BookDaoImplUserDaoImpl的Dao代码

package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}
package com.itheima.dao.impl;

public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("user dao save ...");
    }
}

BookServiceImpl代码

package com.itheima.service.impl;

public class BookServiceImpl implements BookService{
    private BookDao bookDao;
    private UserDao userDao;
    //setter注入需要提供要注入对象的set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    //setter注入需要提供要注入对象的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
        userDao.save();
    }
}

更改配置:

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

    <!--注入引用类型-->
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--ref属性:设置注入引用类型bean的id或name-->
        <property name="bookDao" ref="bookDao"/>
        <property name="userDao" ref="userDao"/>
    </bean>

运行结果:
在这里插入图片描述

简单类型

需要有set方法
在这里插入图片描述
BookDaoImpl.java代码

package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {

    private String databaseName;
    private int connectionNum;
    //setter注入需要提供要注入对象的set方法
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    //setter注入需要提供要注入对象的set方法
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }

    public void save() {
        System.out.println("book dao save ..."+databaseName+","+connectionNum);
    }
}

配置文件:

<!--注入简单类型-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--value属性:设置注入简单类型数据值-->
        <property name="connectionNum" value="100"/>
        <property name="databaseName" value="mysql"/>
    </bean>

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

    <!--注入引用类型-->
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <!--property标签:设置注入属性-->
        <!--name属性:设置注入的属性名,实际是set方法对应的名称-->
        <!--ref属性:设置注入引用类型bean的id或name-->
        <property name="bookDao" ref="bookDao"/>
        <property name="userDao" ref="userDao"/>
    </bean>

运行结果:
在这里插入图片描述

1.3 构造方式注入

项目spring_06_di_autoware

构造器注入就不要set方法了;

问题导入

构造方式注入使用什么子标签?

引用类型

UserDaoImpl.javaBookDaoImpl.java代码

package com.itheima.dao.impl;

public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("user dao save ...");
    }
}
package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {
    private String databaseName;
    private int connectionNum;

    public void save() {
        System.out.println("book dao save ...");
    }
}

BookServiceImpl.java代码

package com.itheima.service.impl;

public class BookServiceImpl implements BookService{
    private BookDao bookDao;
    private UserDao userDao;
//构造方法
    public BookServiceImpl(BookDao bookDao, UserDao userDao) {
        this.bookDao = bookDao;
        this.userDao = userDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
        userDao.save();
    }
}

配置文件

    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>

结果
在这里插入图片描述
在这里插入图片描述

简单类型

UserDaoImpl.javaBookDaoImpl.java代码

package com.itheima.dao.impl;

public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("user dao save ...");
    }
}
package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {
    private String databaseName;
    private int connectionNum;

    public BookDaoImpl(String databaseName, int connectionNum) {
        this.databaseName = databaseName;
        this.connectionNum = connectionNum;
    }
    public void save() {
        System.out.println("book dao save ..."+databaseName+","+connectionNum);
    }
}

BookServiceImpl.java代码

package com.itheima.service.impl;

public class BookServiceImpl implements BookService{
    private BookDao bookDao;
    private UserDao userDao;
//构造方法
    public BookServiceImpl(BookDao bookDao, UserDao userDao) {
        this.bookDao = bookDao;
        this.userDao = userDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
        userDao.save();
    }
}

构造代码

<!--    标准书写-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--        根据构造方法参数名称注入-->
        <constructor-arg name="connectionNum" value="10"/>
        <constructor-arg name="databaseName" value="mysql"/>
    </bean>
    <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>

运行结果:
在这里插入图片描述
在这里插入图片描述

参数适配【了解】

上面简单类型中的BookDaoImpl.java中的形参改名了怎么办,那就配置中不写名了:
直接写类型:
直接写位置:
在这里插入图片描述
上面这两种方式都是可以的;

1.4、依赖注入方式选择
  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
  2. 可选依赖使用setter注入进行,灵活性强
  3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
  5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  6. 自己开发的模块推荐使用setter注入

2、依赖自动装配【理解】

项目spring_07_di_autoware

问题导入

如何配置按照类型自动装配?

2.1、自动装配概念
  • IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
  • 自动装配方式
    按类型(常用)
    按名称
    按构造方法
    不启用自动装配
2.2、自动装配——按类型
依赖自动装配

配置中使用bean标签autowire属性设置自动装配的类型

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>

BookDaoImpl.java程序

package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}

BookServiceImpl.java程序,还是使用构造的方式

package com.itheima.service.impl;

public class BookServiceImpl implements BookService{
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

配置文件

    <bean class="com.itheima.dao.impl.BookDaoImpl"/>
    <!--autowire属性:开启自动装配,通常使用按类型装配-->
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>

执行结果:
在这里插入图片描述

依赖自动装配特征
  1. 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  2. 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  3. 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
  4. 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

3、集合注入

3.1、注入数组类型数据
<property name="array">
    <array>
        <value>100</value>
        <value>200</value>
        <value>300</value>
    </array>
</property>
3.2、注入List类型数据
<property name="list">
    <list>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>chuanzhihui</value>
    </list>
</property>
3.3、注入Set类型数据
<property name="set">
    <set>
        <value>itcast</value>
        <value>itheima</value>
        <value>boxuegu</value>
        <value>boxuegu</value>
    </set>
</property>
3.4、注入Map类型数据
<property name="map">
    <map>
        <entry key="country" value="china"/>
        <entry key="province" value="henan"/>
        <entry key="city" value="kaifeng"/>
    </map>
</property>
3.5、注入Properties类型数据
<property name="properties">
    <props>
        <prop key="country">china</prop>
        <prop key="province">henan</prop>
        <prop key="city">kaifeng</prop>
    </props>
</property>

说明:property标签表示setter方式注入,构造方式注入constructor-arg,标签内部也可以写<array>、<list>、<set>、<map>、<props>标签

代码演示:
BookDaoImpl.java代码

package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {

    private int[] array;
    private List<String> list;
    private Set<String> set;
    private Map<String,String> map;
    private Properties properties;

    public void setArray(int[] array) {
        this.array = array;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public void setSet(Set<String> set) {
        this.set = set;
    }
    public void setMap(Map<String, String> map) {
        this.map = map;
    }
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    public void save() {
        System.out.println("book dao save ...");

        System.out.println("遍历数组:" + Arrays.toString(array));

        System.out.println("遍历List" + list);

        System.out.println("遍历Set" + set);

        System.out.println("遍历Map" + map);

        System.out.println("遍历Properties" + properties);
    }
}

配置文件:

 <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
        <!--数组注入-->
        <property name="array">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>
        <!--list集合注入-->
        <property name="list">
            <list>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
                <value>chuanzhihui</value>
            </list>
        </property>
        <!--set集合注入-->
        <property name="set">
            <set>
                <value>itcast</value>
                <value>itheima</value>
                <value>boxuegu</value>
                <value>boxuegu</value>
            </set>
        </property>
        <!--map集合注入-->
        <property name="map">
            <map>
                <entry key="country" value="china"/>
                <entry key="province" value="henan"/>
                <entry key="city" value="kaifeng"/>
            </map>
        </property>
        <!--Properties注入-->
        <property name="properties">
            <props>
                <prop key="country">china</prop>
                <prop key="province">henan</prop>
                <prop key="city">kaifeng</prop>
            </props>
        </property>
    </bean>

在这里插入图片描述

七、案例:数据源对象管理(管理第三方的)

管理德鲁伊(druid)连接池对象

1、pom.xml中导入druid坐标,然后刷新maven

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>

2、在配置中管理druid连接池对象
其中数据库名称,同户名,密码要写成自己的;

    <!--管理DruidDataSource对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/>
        <property name="username" value="root"/>
        <property name="password" value="1234"/>
    </bean>

3、编写测试方法App.java

package com.itheima;

public class App {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        DataSource dataSource = (DataSource) ctx.getBean("dataSource");
        System.out.println(dataSource);
    }
}

运行代码,查看结果:
在这里插入图片描述

管理C3P0连接池对象

1、pom.xml中导入C3P0依赖坐标,然后刷新maven

    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>

pom.xml中导入MySql依赖坐标,然后刷新maven

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.16</version>
    </dependency>

2、在配置中管理c3p0的连接池对象

    <!--管理c3p0DataSource对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/>
        <property name="user" value="root"/>
        <property name="password" value="root"/>
        <property name="maxPoolSize" value="1000"/>
    </bean>

3、编写测试方法App.java
与上面一样,不用重复
运行代码,查看结果:运行成功
在这里插入图片描述

八、加载properties文件【重点】

  上面,在配置文件中写数据库的用户名和密码这些配置不太合适,应该单独抽出来;
接下来介绍如何spring加载properties文件中的信息;

目的:将数据库的连接参数抽取到一个单独的文件中,与Spring配置文件解耦

问题导入

问题1:如何解决使用EL表达式读取属性文件中的值结果读取到了系统属性问题?
问题2:加载properties文件写法标准写法该怎么写?

在这里插入图片描述

1、基本用法

【第一步】编写jdbc.properties属性文件
比如要加载jdbc.properties文件中的信息,对应的jdbc.properties如下:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root

【第二步】在applicationContext.xml中开启context命名空间使用context空间加载jdbc.properties属性文件
在这里插入图片描述

小技巧:如果觉得上述复制粘贴方式不好改或者容易改错,其实idea是有提示功能的,注意不要选错就行了。有些版本的idea没有这个提示,那么就按照上面复制粘贴的方式改,改完之后可以做成live template模板,后期直接用。
在这里插入图片描述
使用context空间加载properties文件

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

【第三步】在配置连接池Bean的地方使用EL表达式获取jdbc.properties属性文件中的值(使用属性占位符${}读取properties文件中的属性)

<bean class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

配置完成之后,运行之前的获取Druid连接池代码,可以获取到连接池对象就表示配置成功。

代码如下:

<?xml version="1.0" encoding="UTF-8"?>

<!--    1.开启context命名空间(上面)-->
<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
">
<!--    2.使用context空间加载properties文件-->
    <context:property-placeholder location="jdbc.properties"/>
<!--    3.使用属性占位符${}properties文件中的属性-->
    <bean class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

</beans>

然后书写Dao接口和实现类

package com.itheima.dao;

public interface BookDao {
    public void save();
}
package com.itheima.dao.impl;

public class BookDaoImpl implements BookDao {
    private String name;
    
    public void setName(String name) {
        this.name = name;
    }
   
    public void save() {
        System.out.println("book dao save ..."+name);
    }
}

applicationContext.xml文件中配置

    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>

App运行程序:

package com.itheima;

public class App {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
    }
}

运行程序,已经将name动态的打印出来了:
在这里插入图片描述

2、配置不加载系统属性

问题
如果属性文件中配置的不是jdbc.username,而是username=root666,那么使用${username}获取到的不是root666,而是计算机的名称。

原因
系统属性的优先级比我们属性文件中的高,替换了我们的username=root666。

解决
解决1:换一个名称,例如不叫username,叫jdbc.username。

解决2:使用system-properties-mode="NEVER"属性表示不使用系统属性。

<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>

3、加载properties文件写法

  • 不加载系统属性
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
  • 加载多个properties文件
<context:property-placeholder location="jdbc.properties,msg.properties"/>
  • 加载所有properties文件
<context:property-placeholder location="*.properties"/>
  • 加载properties文件 标准格式
<context:property-placeholder location="classpath:*.properties"/>
  • 从类路径或jar包中搜索并加载properties文件
<context:property-placeholder location="classpath*:*.properties"/>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值