IOC/DI配置管理第三方bean
前面所讲的知识点都是基于我们自己写的类,现在如果有需求让我们去管理第三方jar包中的类,该如何管理?
15-数据源对象管理
本次案例将使用数据源Druid来配置学习
环境准备
- 创建一个Maven项目
- 在pom.xml添加依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
- resources下添加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>
- 编写一个运行类App
public class APP {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
在上述环境下,我们来对数据源进行配置管理,先来分析下思路
需求:使用Spring的IOC容器来管理Druid连接池对象
- 使用第三方的技术,需要在pom.xml添加依赖
- 在配置文件中将第三方的类制作成一个bean,让IOC容器进行管理
- 数据库连接需要基础的四要素驱动、连接、用户名和密码,如何注入到对应的bean中
- 从IOC容器中获取对应的bean对象,将其打印到控制台查看结果
思考:
第三方的类指的是什么?
如何注入数据库连接四要素?
实现Druid管理
带着这两个问题,把下面的案例实现下
- 导入druid依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
- 配置第三方bean
那么到底是使用setter注入还是构造器注入呢?
这个就需要我们来看看第三方类给我们提供了什么
通过查看源码,我们发现DruidDataSource只给我们提供了两个构造器如下
public DruidDataSource()
public DruidDataSource(boolean fairLock)
显然这两个构造器不能满足我们的需求,因为我们需要注入数据库连接的四要素,构造器的参数中没有提供
那么我们继续来看看给我们提供了什么setter方法
public void setUsername(String username) {
if (!StringUtils.equals(this.username, username)) {
if (this.inited) {
throw new UnsupportedOperationException();
} else {
this.username = username;
}
}
}
public void setPassword(String password) {
if (!StringUtils.equals(this.password, password)) {
if (this.inited) {
LOG.info("password changed");
}
this.password = password;
}
}
···
通过查看源码,我们发现已经给我们提供了许多的setter方法,其中包括了连接四要素,所以这里我们需要使用setter注入
在applicationContext.xml配置文件中添加DruidDataSource的配置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_db?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
- 从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);
}
}
- 运行程序
打印如下结果: 说明第三方bean对象已经被spring的IOC容器进行管理
做完案例后,我们来解答一下上面的思考
第三方的类指的是什么?
- DruidDataSource
如何注入数据库连接四要素?
- setter注入
16-加载properties文件
刚刚我们完成了druid数据源的配置,但其中包含了一些问题,我们来分析一下:
- 这两个数据源中都用到了一些固定的常量(如数据库连接四要素),把这些值加载到Spring的配置文件中,不利于后期的维护
那我们现在就需要将这些值提取到一个外部的properties配置文件中,在之前我们也是这样做的
那么如何在Spring框架中读取配置文件来进行配置,就是我们接下来要解决的问题
第三方bean属性优化
需求:将数据库连接四要素提取到properties配置文件,spring来加载配置信息并使用这些信息来完成属性注入。
- 在resources下创建一个jdbc.properties(文件的名称可以任意)
- 将数据库连接四要素配置到配置文件中
- 在Spring的配置文件中加载properties文件
- 使用加载到的值实现属性注入
-
准备properties配置文件
resources下创建一个jdbc.properties文件,并添加对应的属性键值对 -
开启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>
- 加载properties配置文件
在配置文件中使用context命名空间下的标签来加载properties配置文件
<context:property-placeholder location="jdbc.properties"/>
- 完成属性注入
使用${key}
来读取properties配置文件中的内容并完成属性注入
<!--加载properties配置文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--使用属性占位行${}读取properties文件中的属性-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
至此,读取外部properties配置文件中的内容就已经完成。
读取单个属性
对于上面的案例,我们看不出实际的效果,那我们试着读取properties中的属性,然后输出在控制台看看
需求:从properties配置文件中读取key为name的值,并将其注入到BookDao中并在save方法中进行打印。
- 在项目中添加BookDao和BookDaoImpl类
- 为BookDaoImpl添加一个name属性并提供setter方法
- 在jdbc.properties中添加数据注入到bookDao中打印方便查询结果
- 在applicationContext.xml添加配置完成配置文件加载、属性注入(${key})
步骤一:在项目中添对应的类
BookDao和BookDaoImpl类,并在BookDaoImpl类中添加name属性与setter方法
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的配置管理、读取外部properties、依赖注入
<bean id="bookDao" class="com.yolo.dao.impl.BookDaoImpl">
<property name="name" value="${jdbc.driverClass}"/>
</bean>
步骤三:运行程序
在App类中,从IOC容器中获取bookDao对象,调用方法,查看值是否已经被获取到并打印控制台
public class APP {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}
运行结果如下,说明已经成功获取到了值
加载properties文件步骤总结
至此,读取properties配置文件中的内容就已经完成,但是在使用的时候,有些注意事项
- 当有多个properties配置文件需要被加载,该如何配置?
修改applicationContext.xml
<!--方式一 -->
<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"/>
说明:
方式一:可以实现,如果配置文件多的话,每个都需要配置
方式二:*.properties代表所有以properties结尾的文件都会被加载,可以解决方式一的问题,但是不标准
方式三:标准的写法,classpath:代表的是从根路径下开始查找,但是只能查询当前项目的根路径
方式四:不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件(推荐使用)
17-容器
容器的创建方式
- 案例中创建ApplicationContext的方式如下
- 这种方式翻译为:类路径下的XML配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
- 除了上面这种方式,Spring还提供了另外一种创建方式
- 这种方式翻译为:文件系统下的XML配置文件,路径需要写绝对路径
- 这种方式虽能实现,但是当项目的位置发生变化后,代码也需要跟着改,耦合度高,不推荐使用。
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\xxx/xxx\applicationContext.xml");
获取bean的三种方式
方式一,就是我们之前用的方式
这种方式存在的问题是每次获取的时候都需要进行类型转换,有没有更简单的方式呢?
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
方式二
这种方式可以解决类型强转问题,但是参数又多加了一个,相对来说没有简化多少。
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
方式三
这种方式就类似我们之前所学习依赖注入中的按类型注入。必须要确保IOC容器中该类型对应的bean对象只能有一个。
BookDao bookDao = ctx.getBean(BookDao.class);
BeanFactory的使用
容器的最上级的父接口为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();
}
}
为了更好的看出BeanFactory和ApplicationContext之间的区别,在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要想成为延迟加载,只需要将lazy-init设为true
<?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.blog.dao.impl.BookDaoImpl" lazy-init="true"/>
</beans>
18-核心容器总结
容器相关
- BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
- ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
- ApplicationContext接口常用初始化类
- ClassPathXmlApplicationContext(常用)
- FileSystemXmlApplicationContext