Spring(1)Spring从零到入门 - Spring特点,系统架构简介,两个核心概念IoC与DI(涉及管理第三方bean)

发布时间:2023年12月21日

Spring(1)Spring从零到入门 - Spring特点,系统架构简介,两个核心概念IoC与DI(涉及管理第三方bean)

引入:单体服务器

"单体服务器的开发"通常指的是在一个单一的服务器上构建和部署整个应用程序或系统的开发过程。在这种模式下,所有的应用程序组件和服务都运行在同一台服务器上,与其他服务器相对孤立。这与分布式系统的开发模式形成对比,分布式系统将应用程序的不同部分分布在多台服务器上,通过网络进行通信和协同工作。

随着云计算和微服务架构的兴起,许多开发团队逐渐转向使用分布式系统和微服务的方式,以更好地应对复杂性和提高系统的可伸缩性。微服务架构将应用程序拆分成小的、自治的服务,每个服务都可以独立部署、扩展和维护,从而提高灵活性和可靠性。

文章目录

1 前言

1.1 Spring特点

Spring框架主要的优势是在简化开发框架整合

  • 简化开发: Spring框架中提供了两个大的核心技术:

    • IOC
    • AOP
      • 事务处理

    1 Spring的简化操作都是基于这两块内容,所以这也是Spring学习中最为重要的两个知识点。

    2 事务处理属于Spring中AOP的具体应用,可以简化项目中的事务管理,也是Spring技术中的一大亮点。

  • 框架整合: Spring在框架整合这块已经做到了极致,它可以整合市面上几乎所有主流框架,比如:

    • MyBatis
    • MyBatis-plus
    • Struts
    • Struts2
    • Hibernate
    • ……

    Hibernate

    Hibernate是一个用于Java平台的对象关系映射(ORM)框架。它提供了一种将Java应用程序中的对象与关系型数据库中的表之间进行映射的方法。通过Hibernate,开发者可以使用面向对象的方式来处理数据库操作,而不必直接编写SQL语句

    MyBatis-Plus

    MyBatis-Plus在MyBatis的基础上提供了更多的便利性和功能,可以减少一些重复性工作,简化开发流程,同时提高了代码的可读性

    Struts

    Struts是一个用于构建基于Java的Web应用程序的开源框架。它提供了一种模型-视图-控制器(MVC)的架构,用于将应用程序的不同部分分离开来,以促进代码的组织、维护和扩展

    (Struts 2是Struts框架的继任者,Struts 2在设计和功能上进行了一些重要的改进和扩展)

1.2 学习路线

IOC → 整合Mybatis(IOC的具体应用) → AOP → 声明式事务(AOP的具体应用)

image-20231216152845472

2 Spring相关概念

2.1 Spring全家桶

  1. Spring能用以开发web、微服务以及分布式系统等,光这三块就已经占了JavaEE开发的九成多

    image-20231216151954264
  2. Spring发展到今天已经形成了一种开发的生态圈, Spring提供了若干个项目,每个项目用于完成特定的功能

  3. 这些项目和模块共同构成了Spring全家桶,为Java开发者提供了全面的解决方案,涵盖了从基本的核心框架到分布式系统开发的各个方面

    image-20231216150028920
  4. 在这些框架中,需要重点关注Spring FrameworkSpringBootSpringCloud

    image-20231216152400073
    • Spring Framework: Spring框架,是Spring中最早最核心的技术,也是所有其他技术的基础

      image-20231216143655854
    • SpringBoot: Spring是来简化开发,而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发

    • SpringCloud: 这个是用来做分布式之微服务架构的相关开发。

2.2 Spring系统架构

  • 历史发展版本

image-20231216152612507

  • 最新的是4版本,所以接下来主要研究的是4的架构图

    image-20231216152715881

    (1)核心层

    • Core Container: 核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块。容器需要填装东西,而对于Java来说,就是填装对象

    (2)AOP层

    • AOP: 面向切面编程,它依赖核心层容器,目的是在不改变原有代码的前提下对其进行功能增强
    • Aspects: AOP是思想,Aspects是对AOP思想的具体实现

    (3)数据层

    • Data Access: 数据访问,Spring全家桶中有对数据访问的具体实现技术
    • Data Integration: 数据集成,Spring支持整合其他的数据层解决方案,比如Mybatis
    • Transactions: 事务,Spring中事务管理是Spring AOP的一个具体实现,也是后期学习的重点内容

    (4)Web层

    • 这一层的内容将在SpringMVC框架具体学习

    (5)Test层

    • Spring主要整合了Junit来完成单元测试和集成测试
  • 学习路线

    image-20231216152845472

2.3 Spring核心概念

主要包含**IOC/DIIOC容器和Bean**

2.3.1 核心概念
  • 在程序中不要主动使用new产生对象,转换为由外部提供对象(达到解耦效果)

    创建控制权发生变化,由内部变为外部(IoC 控制反转)

image-20231216154541069
  • 对象与对象之间存在一定的**依赖关系,如上面的Service对象中有Dao对象,两者存在一定的绑定**

    容器建立的对象与对象之间需要绑定关系(DI 依赖注入)

    这样,在使用Service对象的时候,可以直接使用里面存在的Dao

    image-20231216155602029
  • 概念小结

  • 什么IOC/DI思想?

    • IOC: 控制反转,控制反转的是对象的创建权
    • DI: 依赖注入,绑定对象与对象之间的依赖关系
  • 什么是IOC容器?

    Spring创建了一个容器用来存放所创建的对象,这个容器就叫IOC容器

  • 什么是Bean?

    容器中所存放的一个个对象就叫Bean或Bean对象

2.3.2 IOC

IOC →(Inversion of Control)控制反转

  • Spring提供了一个容器,称为**IOC容器,用来充当IOC思想中的外部**
  • IOC思想中的**别人[外部]指的就是Spring的IOC容器**
  • IOC容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象
  • 被创建或被管理的对象在IOC容器中统称为Bean
  • IOC容器中放的就是一个个的Bean对象
  • 使用Spring提供的接口获取IoC容器
  • 使用接口方法从容器中获取Bean
2.3.3 IOC入门案例

步骤1:创建Maven项目

image-20231216203738339

步骤2:添加Spring的依赖jar包

在pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

添加spring-context才可以得到配置文件的相关结构

image-20231216203944613

其初始化基础结构

image-20231216204044751

步骤3:添加案例中需要的类

创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类

注意这个时候还没有添加注入,在BookServiceImpl中**仍采用new的方法创建了一个对象**

public interface BookDao {
    public void save();
}
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}
public interface BookService {
    public void save();
}
public class BookServiceImpl implements BookService {
    private BookDao bookDao = new BookDaoImpl();
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

步骤4:添加spring配置文件

resources下添加spring配置文件applicationContext.xml,并完成bean的配置

image-20231216203944613

步骤5:在配置文件中完成bean的配置

  • **bean标签**标示配置bean
  • **id属性**标示给bean起名字
  • **class属性**表示给bean定义类型
<?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标签标示配置bean
       id属性标示给bean起名字
       class属性表示给bean定义类型
   -->
    <bean id="bookDao" class="com.baidu.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.baidu.service.impl.BookServiceImpl"/>

</beans>

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

步骤6:获取IOC容器

使用Spring提供的接口完成IOC容器的创建,创建App类,编写main方法

通过.xml文件名完成文件的创建

public class App {
    public static void main(String[] args) {
        //获取IOC容器
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); 
    }
}

步骤7:从容器中获取对象进行方法调用

通过id名获取具体的Bean对象

public class App {
    public static void main(String[] args) {
        //获取IOC容器
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); 
//        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//        bookDao.save();
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

步骤8:运行程序

测试结果为:

image-20231216205600636

采用DI:依赖注入可以进一步降低耦合度,除去new的操作

2.3.4 DI

DI(Dependency Injection)依赖注入

当IOC容器中创建好service和dao对象后,程序能正确执行么?

  • 不行,因为service运行需要依赖dao对象
  • IOC容器中虽然有service和dao对象,但是service对象和dao对象没有任何关系,需要把dao对象交给service,也就是说要绑定service和dao对象之间的关系
  • 这就需要相关的**配置文件**说明情况

什么是依赖注入?

  • 业务层要用数据层的类对象,以前是自己new的
  • 现在自己不new了,靠**别人[外部其实指的就是IOC容器]**来给注入进来
  • 怎样注入呢?需求者提供**一个方法**让外部容器把需要的对象注入进来
  • 需要相关的**配置文件**说明依赖情况
  • 这种思想就是依赖注入

IOC容器中哪些bean之间要建立依赖关系呢?

这个**需要程序员根据业务需求提前建立好关系**,如业务层需要依赖数据层,service就要和dao建立依赖关系

2.3.5 DI入门案例

步骤1: 去除代码中的new

在BookServiceImpl类中,删除业务层中使用new的方式创建的dao对象

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

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

步骤2:为属性提供setter方法

在BookServiceImpl类中,为BookDao提供setter方法

不需要new方法而是通过配置文件注入

这个setter方法可以让外界为之绑定对象

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

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
    //提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

步骤3:修改配置完成注入

在配置文件中添加依赖注入的配置

  • **property标签**表示配置当前bean的属性
  • **name属性**表示配置哪一个具体的属性
  • **ref属性**表示参照哪一个bean
<?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标签标示配置bean
    	id属性标示给bean起名字
    	class属性表示给bean定义类型
	-->
    <bean id="bookDao" class="com.baidu.dao.impl.BookDaoImpl"/>

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

</beans>

注意:配置中的两个bookDao的含义是不一样的

  • name="bookDao"中bookDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的setBookDao()方法进行对象注入
  • ref="bookDao"中bookDao的作用是让Spring能在IOC容器中找到id为bookDao的Bean对象给bookService进行注入
  • 综上所述,对应关系如下:

1629736314989

步骤4:运行程序

运行,测试结果为:

image-20231216220600076

2.4 IOC 相关内容

2.4.1 bean的配置
image-20231217132213496
2.4.1.1 id和class属性

image-20231216221506086

class属性能不能写接口类全名(如BookDao),因为接口是没办法创建对象的

2.4.1.2 bean的别名配置

name: 为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔

步骤1:配置别名

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

    <!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
    <bean id="bookService" name="service service4 bookEbi" class="com.baidu.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>

    <bean id="bookDao" name="dao" class="com.baidu.dao.impl.BookDaoImpl"/>
</beans>

说明:Ebi全称Enterprise Business Interface,翻译为企业业务接口

步骤2:根据名称容器中获取bean对象

public class AppForName {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //此处根据bean标签的id属性和name属性的任意一个值来获取bean对象
        BookService bookService = (BookService) ctx.getBean("service4");
        bookService.save();
    }
}

步骤3:运行程序

测试结果为:

image-20231216222233758
2.4.1.3 bean的作用范围配置重要

image-20231216222050454

scope:为bean设置作用范围,可选值为单例singloton,非单例prototype

spring默认是创建单例(singleton),即容器中对于同一对象创建的**多个实例在内存中的地址是一样的**,即获得的都是同一个对象

演示如下:

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

        BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
        BookDao bookDao2 = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao1);
        System.out.println(bookDao2);
    }
}

输出结果:

image-20231217131631673
<!--scope:为bean设置作用范围,可选值为单例singloton,非单例prototype-->
    <bean id="bookDao" name="dao" class="com.baidu.dao.impl.BookDaoImpl" scope="prototype"/>

输出结果:

image-20231217131806616
  • 为什么bean默认为单例?
    • bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
    • bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
  • bean在容器中是单例的,会不会产生线程安全问题?
    • 如果对象是**有状态对象,即该对象有成员变量可以用来存储数据的**,因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
    • 如果对象是**无状态对象,即该对象没有成员变量没有进行数据存储的**,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
  • 哪些bean对象适合交给容器进行管理?(这些对象都可以反复用
    • 表现层对象
    • 业务层对象
    • 数据层对象
    • 工具对象
  • 哪些bean对象不适合交给容器进行管理?
    • 封装实例的域对象,因为会引发线程安全问题,所以不适合。
2.4.2 bean的实例化
2.4.2.1 bean是如何创建的重要

利用构造方法来创建bean的(底层用的是反射,能访问到类中的私有构造方法)

需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在使用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下,不然会报错:

java.lang.NoSuchMethodException:某个类的构造方法<init>() 抛出的异常为:没有这样的方法异常

2.4.2.2 实例化bean的三种方法
1 构造方法

参考上面 bean是如何创建的

2 静态工厂

创建静态工厂的核心代码:

//静态工厂创建对象
public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        return new OrderDaoImpl();
    }
}
public class AppForInstanceOrder {
    public static void main(String[] args) {
        //通过静态工厂创建对象
        OrderDao orderDao = OrderDaoFactory.getOrderDao();
        orderDao.save();
    }
}

配置文件:

<bean id="orderDao" class="com.baidu.factory.OrderDaoFactory" factory-method="getOrderDao"/>

class: 工厂类的类全名

factory-mehod: 具体工厂类中创建对象的方法名

对应关系:

image-20231217133540394

从容器中获取对象:

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

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

        orderDao.save();

    }
}

实用场景:

在创建对象之前添加其它业务内容

public class OrderDaoFactory {
    public static OrderDao getOrderDao(){
        System.out.println("factory setup....");//模拟必要的业务操作
        return new OrderDaoImpl();
    }
}
3 实例工厂及FactoryBean

示例工厂的核心代码:

//没有static关键字!!
public class UserDaoFactory {
    public UserDao getUserDao(){
        return new UserDaoImpl();
    }
}
public class AppForInstanceUser {
    public static void main(String[] args) {
        //创建实例工厂对象
        UserDaoFactory userDaoFactory = new UserDaoFactory();
        //通过实例工厂对象创建对象
        UserDao userDao = userDaoFactory.getUserDao();
        userDao.save();
}

配置文件:

<bean id="userFactory" class="com.baidu.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>

factory-bean: 工厂的实例对象

factory-method: 工厂对象中的具体创建对象的方法名

运行顺序及对应关系:

  1. 创建实例化工厂对象(第一行配置)

  2. 调用对象中的方法来创建bean(第二行配置)

    image-20231217134530791

从容器中获取对象:

public class AppForInstanceUser {
    public static void main(String[] args) {
        ApplicationContext ctx = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) ctx.getBean("userDao");
        userDao.save();
    }
}

Spring为了简化上述配置方式提供了FactoryBean的方式来简化开发

3 factoryBean重要

步骤:

(1) 创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法

这两个重写的方法为:

方法一:getObject(),被重写后,在方法中进行对象的创建并返回

方法二:getObjectType(), 被重写后,主要返回的是被创建类的Class对象

public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }
    //返回所创建类的Class对象
    public Class<?> getObjectType() {
        return UserDao.class;
    }
}

(2) 在Spring的配置文件中进行配置

<bean id="userDao" class="com.baidu.factory.UserDaoFactoryBean"/>

(3) AppForInstanceUser运行类不用做任何修改,直接运行

public class AppForInstanceUser {
    public static void main(String[] args) {
        ApplicationContext ctx = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) ctx.getBean("userDao");
        userDao.save();
    }
}

这种方式在Spring去整合其他框架的时候会被用到,所以这种方式需要大家理解掌握。

查看源码会发现,FactoryBean接口其实会有三个方法,分别是:

T getObject() throws Exception;

Class<?> getObjectType();

default boolean isSingleton() {
		return true;
}

方法三: boolean isSingleton() 没有被重写,因为它已经给了默认值,从方法名中可以看出其作用是**设置对象是否为单例默认true**

2.4.2.3 bean的生命周期
  • bean生命周期是什么?
    • bean对象从创建到销毁的整体过程。
  • bean生命周期控制是什么?
    • 在bean创建后到销毁前做一些事情。
1 配置生命周期
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...");
    }
}

配置文件中

<bean id="bookDao" class="com.baidu.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>

运行方法

public class AppForLifeCycle {
    public static void main( String[] args ) {
        ApplicationContext ctx = new 
        	ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
    }
}

输出结果

image-20231217151104778

这里为什么没有执行destory方法?

  • Spring的IOC容器是运行在JVM中,运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行
  • main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了
2 关闭容器的两种方法

close关闭容器

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.close();

注意:

  • ApplicationContext中没有close方法

  • 需要将ApplicationContext更换成**ClassPathXmlApplicationContext**

运行结果:

image-20231217151556230

注册钩子关闭容器

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.close();

注意: registerShutdownHook在ApplicationContext中也没有,需要将ApplicationContext更换成ClassPathXmlApplicationContext

运行结果:

image-20231217151701509

两种方法的对比

  • 相同点: 这两种都能用来关闭容器

  • 不同点: close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭

3 使用接口配置生命周期

添加两个接口InitializingBeanDisposableBean并实现接口中的两个方法afterPropertiesSetdestroy

public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao) {
        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");
    }
}

运行代码

public class AppForLifeCycle {
    public static void main( String[] args ) {
        ApplicationContext ctx = new 
        	ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
    }
}

运行结果:

image-20231217152107741

小细节:

对于InitializingBean接口中的afterPropertiesSet方法,翻译过来为属性设置之后,从方法名分析,setBookDao方法先执行后,即属性设置完成之后,在进行初始方法,即afterPropertiesSet

bookDao的注入,仍是通过配置文件实现的

<bean id="bookService" class="com.baidu.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
</bean>
4 总结

(1) 关于Spring中对bean生命周期控制提供了两种方式:

  • 在配置文件中的bean标签中添加**init-method和destroy-method属性**
  • 类实现**InitializingBean与DisposableBean接口**(了解)。

(2) 对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:

  • 初始化容器
    • 1.创建对象(内存分配)
    • 2.执行**构造方法**
    • 3.执行**属性注入**(set操作)
    • 4.执行bean初始化方法(afterPropertiesSet)
  • 使用bean
    • 1.执行业务操作
  • 关闭/销毁容器
    • 1.执行bean销毁方法

(3)关闭容器的两种方式:

  • ConfigurableApplicationContext是ApplicationContext的子类
    • close()方法
    • registerShutdownHook()方法

2.5 DI相关内容

  • 向一个类中传递数据的方式有几种?
    • 普通方法(set方法)
    • 构造方法
  • 依赖注入描述了在容器中建立bean与bean之间的依赖关系的过程,如果bean运行需要的是数字或字符串呢(如 int)?
    • 引用类型
    • 简单类型(基本数据类型与String)

Spring就是基于上面这些知识点,为我们提供了两种注入方式,分别是:

  • setter注入
    • 简单类型
    • 引用类型(前面讲述的)
  • 构造器注入
    • 简单类型
    • 引用类型
2.5.1 setter注入
  • 对于**引用数据**类型使用的是<property name="" ref=""/>
  • 对于**简单数据**类型使用的是<property name="" value=""/>
2.5.1.1 回顾

核心setter代码:

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

核心配置文件:

<bean id="bookService" class="com.baidu.service.impl.BookServiceImpl">
	<property name="bookDao" ref="bookDao"/>
</bean>

<bean id="bookDao" class="com.baidu.dao.imipl.BookDaoImpl"/>
2.5.1.2 注入引用数据类型

步骤1:声明属性并提供setter方法

步骤2:配置文件中进行注入配置

在applicationContext.xml配置文件中使用property标签注入

步骤3:运行程序

2.5.1.3 注入简单数据类型

步骤1:声明属性并提供setter方法

核心示例代码:

public class BookDaoImpl implements BookDao {

    private String databaseName;
    private int connectionNum;

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

步骤2:配置文件中进行注入配置

在applicationContext.xml配置文件中使用property标签注入

value:后面跟的是简单数据类型,对于参数类型,Spring在注入的时候会自动转换(将引号中的内容转换成对应的简单数据类型)

两个property注入标签的顺序可以任意

配置文件示例:

<?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="bookDao" class="com.baidu.dao.impl.BookDaoImpl">
        <property name="databaseName" value="mysql"/>
     	<property name="connectionNum" value="10"/>
    </bean>
    <bean id="userDao" class="com.baidu.dao.impl.UserDaoImpl"/>
    <bean id="bookService" class="com.baidu.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

如果写成类型不匹配,如:

<property name="connectionNum" value="abc"/>

会报错。

2.5.2 构造器注入
2.5.2.1 构造器注入单个引用数据类型

情景:相关的setter方法删除掉,添加带有参数的构造方法

步骤1:删除setter方法并提供构造方法

核心代码:

public class BookServiceImpl implements BookService{
    private BookDao bookDao;

    public BookServiceImpl(BookDao bookDao) {
        this.bookDao = bookDao;
    }

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

步骤2:配置文件中进行配置构造方式注入

标签<constructor-arg>

  • name属性对应的值为**构造函数中方法形参的参数名**,必须要保持一致。

  • ref属性指向的是spring的IOC容器中**其他bean对象**。

<?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="bookDao" class="com.baidu.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.baidu.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
    </bean>
</beans>

步骤3:运行程序

public class AppForDIConstructor {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

image-20231217161741709

2.5.2.2 构造器注入多个引用数据类型

步骤1: 提供多个属性的构造函数(并在相关方法两个属性都调用)

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();
    }
}

步骤2: 配置文件中配置多参数注入

<?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="bookDao" class="com.baidu.dao.impl.BookDaoImpl"/>
    <bean id="userDao" class="com.baidu.dao.impl.UserDaoImpl"/>
    <bean id="bookService" class="com.baidu.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>
</beans>

**说明:**这两个<contructor-arg>的配置顺序可以任意

步骤3: 运行程序

public class AppForDIConstructor {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

运行结果:

image-20231217162545827
2.5.2.3 构造器注入多个简单数据

步骤1:添加多个简单属性并提供构造方法

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);
    }
}

步骤2:配置完成多个属性构造器注入

<?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="bookDao" class="com.baidu.dao.impl.BookDaoImpl">
        <constructor-arg name="databaseName" value="mysql"/>
        <constructor-arg name="connectionNum" value="666"/>
    </bean>
    <bean id="userDao" class="com.baidu.dao.impl.UserDaoImpl"/>
    <bean id="bookService" class="com.baidu.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>
</beans>

**说明:**这两个<contructor-arg>的配置顺序可以任意

步骤3:运行程序

public class AppForDIConstructor {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}

image-20231217163321124

2.5.2.4 其他注入方式(解决耦合)

这两部分的耦合度很高,参数名发生变化后,配置文件中的name属性也需要跟着变

image-20231217163446649

方式一: 删除name属性,添加type属性,按照类型注入

<bean id="bookDao" class="com.baidu.dao.impl.BookDaoImpl">
    <constructor-arg type="int" value="10"/>
    <constructor-arg type="java.lang.String" value="mysql"/>
</bean>
  • 这种方式可以解决构造函数形参名发生变化带来的耦合问题
  • 但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了

方式二: 删除type属性,添加index属性,按照索引下标注入,下标从0开始

<bean id="bookDao" class="com.baidu.dao.impl.BookDaoImpl">
    <constructor-arg index="1" value="100"/>
    <constructor-arg index="0" value="mysql"/>
</bean>
  • 这种方式可以解决参数类型重复问题
  • 但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题
2.5.2.5 小结

两种参数的注入方式,该如何选择呢?

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

回顾

  • setter注入

    • 简单数据类型

      <bean ...>
      	<property name="" value=""/>
      </bean>
      
    • 引用数据类型

      <bean ...>
      	<property name="" ref=""/>
      </bean>
      
  • 构造器注入

    • 简单数据类型

      <bean ...>
      	<constructor-arg name="" index="" type="" value=""/>
      </bean>
      
    • 引用数据类型

      <bean ...>
      	<constructor-arg name="" index="" type="" ref=""/>
      </bean>
      
  • 依赖注入的方式选择上

    • 建议使用setter注入
    • 第三方技术根据情况选择
2.5.3 自动配置

在前面所说的配置的基础上进一步简化操作

IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配

注意:

自动装配用于引用类型依赖注入,不能对简单类型进行操作

自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

2.5.3.1 自动装配的方式
  • 按类型(常用,推荐)
  • 按名称
2.5.3.2 按类型

核心:

(1)将<property>标签删除

(2)在<bean>标签中添加autowire属性

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

注意:

  • 需要注入属性的类中对应属性的setter方法不能省略

    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();
        }
    }
    
  • 被注入的对象必须要被Spring的IOC容器管理

    <bean class="com.baidu.dao.impl.BookDaoImpl"/>
    
  • 按照类型在Spring的IOC容器中如果找到多个对象,会报NoUniqueBeanDefinitionException

2.5.3.3 按名称

一个类型在IOC中有多个对象,就需要按照名称注入,配置方式为:

<?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 class="com.baidu.dao.impl.BookDaoImpl"/>
    <!--autowire属性:开启自动装配,通常使用按类型装配-->
    <bean id="bookService" class="com.baidu.service.impl.BookServiceImpl" autowire="byName"/>

</beans>

注意:

这里的名称指的是什么?

  • 和对应的set方法有关,属性名和set对应的名是一致的,对外部类来说,setBookDao方法名,去掉set后首字母小写是其属性名
  • 如果按照名称去找对应的bean对象,找不到则注入Null
  • 当某一个类型在IOC容器中有多个对象,按照名称注入只找其指定名称对应的bean对象,不会报错
2.5.4 集合注入

除了引入数据类型简单数据类型,**集合**这种数据类型,在Spring中该如何注入呢?

常见集合数据类型:

  • 数组

  • List

  • Set

  • Map

  • Properties

    Properties 类被设计用来处理配置文件,其中键和值都是字符串类型。它提供了一种简单的方式来读取和写入属性文件

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 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);
    }
	//setter....方法省略,自己使用工具生成
}

配置文件:

<?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="bookDao" class="com.baidu.dao.impl.BookDaoImpl">
        
    </bean>
</beans>

下面的所以配置方式,都是在bookDao的bean标签中使用<property>进行注入

2.5.4.1 注入数组类型数据
<property name="array">
    <array>
        <value>100</value>
        <value>200</value>
        <value>300</value>
    </array>
</property>
2.5.4.2 注入List类型数据
<property name="list">
    <list>
        <value>list_v1</value>
        <value>list_v2</value>
        <value>list_v3</value>
        <value>list_v4</value>
    </list>
</property>
2.5.4.3 注入Set类型数据
<property name="set">
    <set>
        <value>set_1</value>
        <value>set_2</value>
        <value>set_3</value>
        <value>set_4</value>
    </set>
</property>
2.5.4.4 注入Map类型数据
<property name="map">
    <map>
        <entry key="country" value="china"/>
        <entry key="province" value="henan"/>
        <entry key="city" value="kaifeng"/>
    </map>
</property>
2.5.4.5 注入Properties类型数据
<property name="properties">
    <props>
        <prop key="country">china</prop>
        <prop key="province">henan</prop>
        <prop key="city">kaifeng</prop>
    </props>
</property>
2.5.4.6 运行
public class AppForDICollection {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
    }
}

运行结果:

image-20231218204817231

2.5.4.7 注意
  • property标签**表示setter方式注入**,构造方式注入constructor-arg标签内部也可以写<array><list><set><map><props>标签

    <bean id="exampleBeanList" class="com.example.ExampleBean">
        <constructor-arg>
            <list>
                <value>item1</value>
                <value>item2</value>
                <value>item3</value>
            </list>
        </constructor-arg>
    </bean>
    
  • List的底层也是通过数组实现的,所以**<list><array>标签是可以混用**

  • 集合中要添加**引用类型**,只需要把<value>标签改成<ref>标签,这种方式用的比较少

    <!-- 定义两个简单的Bean -->
    <bean id="student1" class="com.example.Student">
        <property name="name" value="Alice"/>
    </bean>
    
    <bean id="student2" class="com.example.Student">
        <property name="name" value="Bob"/>
    </bean>
    
    <!-- 构造器注入,注入引用类型的集合 -->
    <bean id="exampleBeanRefCollection" class="com.example.ExampleBean">
        <constructor-arg>
            <list>
                <!-- 使用<ref>标签引用其他Bean -->
                <ref bean="student1"/>
                <ref bean="student2"/>
            </list>
        </constructor-arg>
    </bean>
    

2.6 IOC/DI配置管理第三方bean

管理第三方jar包中的类

2.6.1 对象管理

这里第三方的bean以**数据源Druid(德鲁伊)C3P0**为例

DruidC3P0 都是 Java 中常用的数据库连接池,用于管理数据库连接,提高数据库访问的性能和效率。它们都支持连接池的基本功能,如连接的创建、管理、释放以及一些额外的功能,如连接的监控和统计。

对于技术的坐标的导入

Maven Repository: Search/Browse/Explore (mvnrepository.com)

image-20231219200058803

image-20231219200209662

image-20231219200249884

image-20231219200322892

2.6.1.1 Druid的管理

步骤1:导入druid的依赖

pom.xml

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

步骤2:配置第三方bean

在applicationContext.xml配置文件中添加DruidDataSource的配置

<?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">
    <!--管理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>
</beans>

说明:

  • driverClassName:数据库驱动
  • url:数据库连接地址
  • username:数据库连接用户名
  • password:数据库连接密码
  • 数据库连接的四要素要和自己使用的数据库信息一致。

步骤3:从IOC容器中获取对应的bean对象

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);
    }
}

步骤4:运行程序

打印如下结果: 说明第三方bean对象已经被spring的IOC容器进行管理

image-20231219193702251

思考&回顾:

这里的第三方类是指什么?

DruidDataSource

如何注入数据库连接四要素?

setter注入

如何发现是setter注入的?

Ctrl+Z 查看源码;

image-20231219195042779

找到其构造方法,发现一个为空参数构造,一个只传入了一个参数,不能完成注入操作

image-20231219195307820

Ctrl+F12,查找其setter方法:

image-20231219195402446

定位到这个方法,可以推断,是根据这个方法,完成了注入

image-20231219195454524

同样可以发现其他相关函数:

image-20231219195534347

2.6.1.2 C3P0的管理

使用Spring的IOC容器来管理C3P0连接池对象

步骤1:导入C3P0的依赖

pom.xml中添加依赖

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

步骤2:配置第三方bean

在applicationContext.xml配置文件中添加配置

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

注意:

  • ComboPooledDataSource的属性是通过setter方式进行注入
  • 想注入属性就需要在ComboPooledDataSource类或其上层类中有提供属性对应的setter方法
  • C3P0的四个属性和Druid的四个属性是不一样的
  • Druid在初始化的时候没有去加载驱动com.mysql.jdbc.Driver,而C3P0刚好相反;Druid程序运行虽然没有报错,但是当调用DruidDataSource的getConnection()方法获取连接的时候,也会报找不到驱动类的错误

运行结果:

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);
    }
}

image-20231219200834137

2.6.2 加载properties文件

将一些常量提取到个外部的properties配置文件中

2.6.2.1 配置properties文件

步骤1:准备properties配置文件

resources下创建一个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

步骤2:开启context命名空间

在applicationContext.xml中开**context命名空间**

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

如何开辟新的命名空间?

前面加:

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

下面加:

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

步骤3:加载properties配置文件

在配置文件中使用context命名空间下的标签来加载properties配置文件

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

步骤4:完成属性注入

使用${key}来读取properties配置文件中的内容并完成属性注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
    <context:property-placeholder location="jdbc.properties"/>
    <bean id="dataSource" 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>

至此,读取外部properties配置文件中的内容就已经完成。

2.6.2.2 环境变量问题

问题引入:

如果key设置为username

username=root666

在xml中注入属性时

<bean id="bookDao" class="com.baidu.dao.impl.BookDaoImpl">
    <property name="name" value="${username}"/>
</bean>

运行后,在控制台打印的却不是root666,而是自己电脑的用户名

image-20231219204642936

问题分析:

出现问题的原因是<context:property-placeholder/>标签会加载系统的环境变量,而且**环境变量的值会被优先加载**

查看系统的环境变量

package com.baidu;

import java.util.Map;

public class AppSystemProperties {
 public static void main(String[] args) {
     Map<String, String> env = System.getenv();
     for (Map.Entry<String, String> entry : env.entrySet()) {
         System.out.println(entry.getKey() + " = " + entry.getValue());
     }
 }
}

输出:

USERDOMAIN_ROAMINGPROFILE = OLIVER的LEGIONB
EFC_93204 = 1
PROCESSOR_LEVEL = 6
SESSIONNAME = Console
ALLUSERSPROFILE = C:\ProgramData
PROCESSOR_ARCHITECTURE = AMD64
PSModulePath = C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
SystemDrive = C:
MAVEN_HOME = D:\Maven\apache-maven-3.9.6-bin\apache-maven-3.9.6
USERNAME = Oliver_Cheung  
# 输出的USERNAME↑↑↑↑↑↑↑
ProgramFiles(x86) = C:\Program Files (x86)
CUDA_PATH_V10_2 = D:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2
PATHEXT = .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
DriverData = C:\Windows\System32\Drivers\DriverData
ProgramData = C:\ProgramData
ProgramW6432 = C:\Program Files
HOMEPATH = \Users\Oliver_Cheung
MYSQL_HOME = D:\MySQL_installer\mysql-5.7.24-winx64
PROCESSOR_IDENTIFIER = Intel64 Family 6 Model 186 Stepping 2, GenuineIntel
ProgramFiles = C:\Program Files
PUBLIC = C:\Users\Public
windir = C:\Windows
=:: = ::\
ZES_ENABLE_SYSMAN = 1
LOCALAPPDATA = C:\Users\Oliver_Cheung\AppData\Local
USERDOMAIN = OLIVER的LEGIONB
LOGONSERVER = \\OLIVER的LEGIONB
JAVA_HOME = D:\JDK
OneDrive = C:\Users\Oliver_Cheung\OneDrive
APPDATA = C:\Users\Oliver_Cheung\AppData\Roaming
NODE_PATH = D:\Node_JS\node_global\node_modules
NVTOOLSEXT_PATH = C:\Program Files\NVIDIA Corporation\NvToolsExt\
CommonProgramFiles = C:\Program Files\Common Files
Path = D:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\bin;D:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\libnvvp;D:\JDK\bin;D:\Node_JS;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;D:\Program Files\Bandizip\;D:\Node_JS\node_global\node_modules;C:\Program Files\NVIDIA Corporation\Nsight Compute 2019.5.0\;D:\MySQL_installer\mysql-5.7.24-winx64\bin;D:\python_3.10.4\Scripts\;D:\python_3.10.4\;C:\Users\Oliver_Cheung\AppData\Local\Microsoft\WindowsApps;D:\Node_JS\node_global;D:\Program Files\JetBrains\PyCharm2023.2.5\bin;;D:\Users\Oliver_Cheung\AppData\Local\Programs\Microsoft VS Code\bin;D:\Maven\apache-maven-3.9.6-bin\apache-maven-3.9.6\bin;
NVCUDASAMPLES10_2_ROOT = D:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.2
PyCharm = D:\Program Files\JetBrains\PyCharm2023.2.5\bin;
OS = Windows_NT
COMPUTERNAME = OLIVER的LEGIONB
NVCUDASAMPLES_ROOT = D:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.2
CUDA_PATH = D:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2
PROCESSOR_REVISION = ba02
CommonProgramW6432 = C:\Program Files\Common Files
ComSpec = C:\Windows\system32\cmd.exe
SystemRoot = C:\Windows
TEMP = C:\Users\OLIVER~1\AppData\Local\Temp
HOMEDRIVE = C:
USERPROFILE = C:\Users\Oliver_Cheung
TMP = C:\Users\OLIVER~1\AppData\Local\Temp
CommonProgramFiles(x86) = C:\Program Files (x86)\Common Files
NUMBER_OF_PROCESSORS = 20
IDEA_INITIAL_DIRECTORY = D:\Program Files\JetBrains\IntelliJ IDEA 2023.2.5\bin

Process finished with exit code 0

问题解决:

  • system-properties-mode:设置为**NEVER,表示不加载系统属性**

    <context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
    
  • 当然还有一个解决方案就是避免使用username作为属性的key

2.6.2.3 多个配置文件问题

问题引入:

调整下配置文件的内容,在resources下添加jdbc.properties,jdbc2.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

jdbc2.properties

username=root666

image-20231219205540353

问题解决:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    <!--方式一 -->
    <context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
    <!--方式二-->
    <context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
    <!--方式三 -->
    <context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
    <!--方式四-->
    <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
</beans>	
  • 方式一:可以实现,如果配置文件多的话,每个都需要配置
  • 方式二:*.properties代表所有以properties结尾的文件都会被加载,可以解决方式一的问题,但是不标准
  • 方式三:标准的写法,classpath:代表的是从根路径下开始查找,但是只能查询当前项目的根路径
  • 方式四:不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件

2.7 核心容器

什么为核心容器?

把它简单的理解为ApplicationContext,我们进一步来详细了解这个类

  • 如何创建容器?
  • 创建好容器后,如何从容器中获取bean对象?
  • 容器类的层次结构是什么?
  • BeanFactory是什么?
2.7.1 容器的创建方式

类路径下的XML配置文件

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

文件系统下的XML配置文件

ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\workspace\\spring\\spring_10_container\\src\\main\\resources\\applicationContext.xml");
2.7.2 Bean的三种获取方式

方式一,就是目前案例中获取的方式:

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

进行类型转换

方式二:

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

参数又多加了一个

方式三:

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

类似我们之前所学习依赖注入中的按类型注入。必须要确保IOC容器中该类型对应的bean对象只能有一个

2.7.3 容器类层次结构

在IntelliJ IDEA中,**双击Shift键是用于打开"Search Everywhere"(搜索所有地方)**功能的快捷键

Ctrl+h 打开某个类的类型继承图

2.7.3.1 BeanFactory类

image-20231220133536543

结构层次关系:

image-20231220133610519

容器类也是从无到有根据需要一层层叠加上来的。

2.7.3.2 BeanFactory的使用

使用BeanFactory来创建IOC容器的具体实现方式为:

public class AppForBeanFactory {
    public static void main(String[] args) {
        Resource resources = new ClassPathResource("applicationContext.xml");
        BeanFactory bf = new XmlBeanFactory(resources);
        BookDao bookDao = bf.getBean(BookDao.class);
        bookDao.save();
    }
}

为了更好的看出BeanFactoryApplicationContext之间的区别,在BookDaoImpl添加如下构造函数:

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

如果不去获取bean对象,打印会发现:

  • BeanFactory是**延迟加载**,只有在获取bean对象的时候才会去创建

  • ApplicationContext是立即加载,容器加载的时候就会创建bean对象

  • ApplicationContext要想成为延迟加载,只需要按照如下方式进行配置

    <?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="bookDao" class="com.baidu.dao.impl.BookDaoImpl"  lazy-init="true"/>
    </beans>
    
2.7.4 核心容器总结
2.7.4.1 容器相关
  • BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
  • ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
  • ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
  • ApplicationContext接口常用初始化类
    • ClassPathXmlApplicationContext(常用)
    • FileSystemXmlApplicationContext
2.7.4.2 bean相关

image-20231220134556870

2.7.4.3 依赖注入相关

image-20231220134643940

类型继承图**

2.7.3.1 BeanFactory类

[外链图片转存中…(img-FR2v4pff-1703147141496)]

结构层次关系:

image-20231220133610519

容器类也是从无到有根据需要一层层叠加上来的。

2.7.3.2 BeanFactory的使用

使用BeanFactory来创建IOC容器的具体实现方式为:

public class AppForBeanFactory {
    public static void main(String[] args) {
        Resource resources = new ClassPathResource("applicationContext.xml");
        BeanFactory bf = new XmlBeanFactory(resources);
        BookDao bookDao = bf.getBean(BookDao.class);
        bookDao.save();
    }
}

为了更好的看出BeanFactoryApplicationContext之间的区别,在BookDaoImpl添加如下构造函数:

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

如果不去获取bean对象,打印会发现:

  • BeanFactory是**延迟加载**,只有在获取bean对象的时候才会去创建

  • ApplicationContext是立即加载,容器加载的时候就会创建bean对象

  • ApplicationContext要想成为延迟加载,只需要按照如下方式进行配置

    <?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="bookDao" class="com.baidu.dao.impl.BookDaoImpl"  lazy-init="true"/>
    </beans>
    
2.7.4 核心容器总结
2.7.4.1 容器相关
  • BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
  • ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
  • ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
  • ApplicationContext接口常用初始化类
    • ClassPathXmlApplicationContext(常用)
    • FileSystemXmlApplicationContext
2.7.4.2 bean相关

在这里插入图片描述

2.7.4.3 依赖注入相关

在这里插入图片描述

文章来源:https://blog.csdn.net/qq_55626883/article/details/135134183
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。