SSM框架01_Spring IoC DI bean配置 自动装配
有一个效应叫知识诅咒:自己一旦知道了某事,就无法想象这件事在未知者眼中的样子。
00-Spring课程介绍
01-初识Spring
今天所学的Spring其实是Spring家族中的Spring Framework;
Spring Fra是Spring家族中其他框架的底层基础,学好Spring可以为其他Spring框架的学习打好基础
02-Spring系统架构
(1)核心层
-
Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块
(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来完成单元测试和集成测试
03-核心概念
在Spring核心概念这部分中主要包含IOC/DI、IOC容器和Bean,那么问题就来了,这些都是什么呢?
我们想,如果能把框中的内容给去掉,不就可以降低依赖么,但是又会引入新的问题,去掉以后能运行么?
答案肯定是不行,因为bookDao没有赋值为Null,强行运行就会出空指针异常。
所以现在的问题就是,业务层不想new对象,运行的时候又需要这个对象,该咋办呢?
针对这个问题,Spring就提出了一个解决方案:
-
使用对象时,在程序中不要主动用new产生对象,转换为由外部提供对象
介绍完Spring的IOC和DI的概念后,我们会发现这两个概念的最终目标就是:充分解耦
04-loC入门案例
(1)Spring是使用容器来管理bean对象的,那么管什么?
-
主要管理项目中所使用到的类对象,比如(Service和Dao)
(2)如何将被管理的对象告知IOC容器?
-
使用配置文件
(3)被管理的对象交给IOC容器,要想从容器中获取对象,就先得思考如何获取到IOC容器?
-
Spring框架提供相应的接口
(4)IOC容器得到后,如何从容器中获取bean?
-
调用Spring框架提供对应接口中的方法
(5)使用Spring导入哪些坐标?
-
用别人的东西,就需要在pom.xml添加对应的依赖
步骤1:创建Maven项目
步骤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>
步骤3:添加案例中需要的 类/接口
创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类
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的配置
步骤5:在配置文件applicationContext.xml中完成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.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"/>
</beans>
注意事项:bean定义时id属性在同一个上下文中(配置文件)不能重复
步骤6:获取IOC容器
步骤7:从容器中获取Bean对象进行方法调用
public class App {
public static void main(String[] args) {
//步骤6:获取IOC容器,使用Spring提供的接口完成IOC容器的创建,创建App类,编写main方法
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//步骤7 ctx.getBean("bookDao"); //括号里的是配置文件里的Bean.id
// BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// bookDao.save();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
步骤8:运行程序
测试结果为:
Spring的IOC入门案例已经完成,但是在BookServiceImpl的类中依然存在BookDaoImpl对象的new操作,它们之间的耦合度还是比较高,这块该如何解决,就需要用到下面的DI:依赖注入。
05-DI入门案例
(1)要想实现依赖注入,必须要基于IOC管理Bean
-
DI的入门案例要依赖于前面IOC的入门案例
(2)Service中使用new形式创建的Dao对象是否保留?
-
需要删除掉,最终要使用IOC容器中的bean对象
(3)Service中需要的Dao对象如何进入到Service中?
-
在Service中提供方法,让Spring的IOC容器可以通过该方法传入bean对象
(4)Service与Dao间的关系如何描述?
-
使用配置文件
步骤1: 去除代码中的new
在BookServiceImpl类中,删除业务层中使用new的方式创建的dao对象
步骤2:为属性提供setter方法
在BookServiceImpl类中,为BookDao提供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:修改配置完成注入
在配置文件中添加依赖注入的配置
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性 在.java文件那个
ref属性表示参照哪一个bean 上面那个bean.id
-->
<property name="bookDao" ref="bookDao"/>
</bean>
注意:配置中的两个bookDao的含义是不一样的==
-
name="bookDao"中bookDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的setBookDao()方法进行对象注入
-
ref="bookDao"中bookDao的作用是让Spring能在IOC容器中找到id为bookDao的Bean对象给bookService进行注入
-
综上所述,对应关系如下:
06-bean基础配置
前面提过为bean设置id时,id必须唯一,但是如果由于命名习惯而产生了分歧后,该如何解决?
4.1.2 bean的name属性 别名配置
步骤1:配置别名
打开spring的配置文件applicationContext.xml
<!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
<bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
<!--scope:为bean设置作用范围,可选值为单例singloton,非单例prototype-->
<bean id="bookDao" name="dao" class="com.itheima.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();
}
}
注意事项:
-
bean依赖注入的ref属性指定bean,必须在容器中存在
如果不存在,则会报错,如下:
获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException
4.1.3 bean作用范围scope配置
4.1.3.1 验证IOC容器中对象是否为单例
验证思路
同一个bean获取两次,将对象打印到控制台,看打印出的地址值是否一致。
4.1.3.2 配置bean为非单例
在Spring配置文件中,配置scope属性来实现bean的非单例创建
将scope设置为prototype (非单例)
4.1.3.3 scope使用后续思考
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
07-bean实例化一一构造方法
Spring的报错信息如何阅读:
-
错误信息从下往上依次查看,因为上面的错误大都是对下面错误的一个包装,最核心错误是在最下面
-
Caused by: java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.<init>()
-
Caused by 翻译为引起,即出现错误的原因
-
java.lang.NoSuchMethodException:抛出的异常为没有这样的方法异常
-
com.itheima.dao.impl.BookDaoImpl.<init>():哪个类的哪个方法没有被找到导致的异常,<init>()指定是类的构造方法,即该类的无参构造方法
如果最后一行错误获取不到错误信息,接下来查看第二层:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.itheima.dao.impl.BookDaoImpl.<init>()
-
nested:嵌套的意思,后面的异常内容和最底层的异常是一致的
-
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.itheima.dao.impl.BookDaoImpl]: No default constructor found;
-
Caused by: 引发
-
BeanInstantiationException:翻译为bean实例化异常
-
No default constructor found:没有一个默认的构造函数被发现
08-bean实例化一一静态工厂
4.2.4 .2静态工厂实例化
这就要用到Spring中的静态工厂实例化的知识了,具体实现步骤为:
(1)在spring的配置文件application.properties中添加以下内容:
<beanid="orderDao"class="com.itheima.factory.OrderDaoFactory"factory-method="getOrderDao"/>
class:工厂类的类全名 ;factory-mehod:具体工厂类中创建对象的方法名
对应关系如下图:
(2)在AppForInstanceOrder运行类,使用从IOC容器中获取bean的方法进行运行测试
public class AppForInstanceOrder {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao= (OrderDao) ctx.getBean("orderDao");
orderDao.save();
}
}
看到这,可能有人会问了,你这种方式在工厂类中不也是直接new对象的,和我自己直接new没什么太大的区别,而且静态工厂的方式反而更复杂,这种方式的意义是什么?
主要的原因是:
-
在工厂的静态方法中,我们除了new对象还可以做其他的一些业务操作,这些操作必不可少,如:
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");//模拟必要的业务操作
return new OrderDaoImpl();
}}
介绍完静态工厂实例化后,这种方式一般是用来兼容早期的一些老系统,所以了解为主
09-bean实例化-实例工厂与Factory
4.2.3.2 实例工厂实例化
具体实现步骤为:
(1)在spring的配置文件中添加以下内容:
<bean id="userFactory"class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao"factory-method="getUserDao"factory-bean="userFactory"/>
实例化工厂运行的顺序是:
-
创建实例化工厂对象,对应的是第一行配置
-
调用对象中的方法来创建bean,对应的是第二行配置
-
factory-bean:工厂的实例对象
-
factory-method:工厂对象中的具体创建对象的方法名,对应关系如下:
factory-mehod:具体工厂类中创建对象的方法名
(2)在AppForInstanceUser运行类,使用从IOC容器中获取bean的方法进行运行测试
publicclassAppForInstanceUser {
publicstaticvoidmain(String[] args) {
ApplicationContextctx=new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDaouserDao= (UserDao) ctx.getBean("userDao");
userDao.save();
}}
实例工厂实例化的方式就已经介绍完了,配置的过程还是比较复杂,所以Spring为了简化这种配置方式就提供了一种叫FactoryBean的方式来简化开发。
4.2.3.3 FactoryBean的使用
具体的使用步骤为:
(1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
这种方式在Spring去整合其他框架的时候会被用到,所以这种方式需要大家理解掌握。
查看源码会发现,FactoryBean接口其实会有三个方法,分别是:
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
方法一:getObject(),被重写后,在方法中进行对象的创建并返回
方法二:getObjectType(),被重写后,主要返回的是被创建类的Class对象
方法三:没有被重写,因为它已经给了默认值,从方法名中可以看出其作用是设置对象是否为单例,默认true,从意思上来看,我们猜想默认应该是单例,如何来验证呢?
思路很简单,就是从容器中获取该对象的多个值,打印 查看是否为同一个对象.验证,会发现默认是单例
4.2.6 bean实例化小结
通过这一节的学习,需要掌握:
(1)bean是如何创建的呢? 构造方法
(2)Spring的IOC实例化对象的三种方式分别是:
-
构造方法(常用)
-
静态工厂(了解)
-
实例工厂(了解)
-
FactoryBean(实用)
这些方式中,重点掌握构造方法和FactoryBean即可。
需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在使用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。
10-bean的生命周期
-
首先理解下什么是生命周期?
-
从创建到消亡的完整过程,例如人从出生到死亡的整个过程就是一个生命周期。
-
bean生命周期是什么?
-
bean对象从创建到销毁的整体过程。
-
bean生命周期控制是什么?
-
在bean创建后到销毁前做一些事情。
4.3.2 生命周期设置
接下来,在上面这个环境中来为BookDao添加生命周期的控制方法,具体的控制有两个阶段:
-
bean创建之后,想要添加内容,比如用来初始化需要用到资源
-
bean销毁之前,想要添加内容,比如用来释放用到的资源
步骤1:添加初始化和销毁方法
针对这两个阶段,我们在BooDaoImpl类中分别添加两个方法,方法名任意
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...");
}
}
步骤2:配置生命周期
在配置文件添加配置,如下:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
从结果中可以看出,init方法执行了,但是destroy方法却未执行,这是为什么呢?
-
Spring的IOC容器是运行在JVM中
-
运行main方法后,JVM启动,Spring加载配置文件生成IOC容器,从容器获取bean对象,然后调方法执行
-
main方法执行完后,JVM退出,这个时候IOC容器中的bean还没有来得及销毁就已经结束了
-
所以没有调用对应的destroy方法
知道了出现问题的原因,具体该如何解决呢?
4.3.3 close关闭容器
-
ApplicationContext中没有close方法
-
需要将ApplicationContext更换成ClassPathXmlApplicationContext
ClassPathXmlApplicationContextctx=new
ClassPathXmlApplicationContext("applicationContext.xml");
-
调用ctx的close()方法
ctx.close();
-
运行程序,就能执行destroy方法的内容
4.3.4 注册钩子关闭容器
-
在容器未关闭之前,提前设置好回调函数,让JVM在退出之前回调此函数来关闭容器
-
调用ctx的registerShutdownHook()方法
ctx.registerShutdownHook();
注意:registerShutdownHook在ApplicationContext中也没有
两种方式介绍完后,close和registerShutdownHook选哪个?
相同点:这两种都能用来关闭容器
不同点:close()是在调用的时候关闭,registerShutdownHook()是在JVM退出前调用关闭。
分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较多也比较乱。
Spring提供了两个接口来完成生命周期的控制,(了解即可)
好处是可以不用再进行配置init-method和destroy-method
接下来在BookServiceImpl完成这两个接口的使用:
修改BookServiceImpl类,添加两个接口InitializingBean, DisposableBean并实现接口中的两个方法afterPropertiesSet和destroy
4.3.5 bean生命周期小结
(1)关于Spring中对bean生命周期控制提供了两种方式:
-
在配置文件中的bean标签中添加init-method和destroy-method属性
-
类实现InitializingBean与DisposableBean接口,这种方式了解下即可。
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
-
初始化容器
-
1.创建对象(内存分配)
-
2.执行构造方法
-
3.执行属性注入(set操作)
-
4.执行bean初始化方法
-
使用bean
-
1.执行业务操作
-
关闭/销毁容器
-
1.执行bean销毁方法
(3)关闭容器的两种方式:
-
ConfigurableApplicationContext是ApplicationContext的子类
-
close()方法 手工关闭
-
registerShutdownHook()方法 注册失败钩子,先关闭容器再退出虚拟机
11-setter注入(重要)
12-构造器注入
介绍完两种参数的注入方式,具体我们该如何选择呢?
-
强制依赖使用构造器进行,使用setter注入有概率不进行注入(忘记)导致null对象出现
-
强制依赖指对象在创建的过程中必须要注入指定的参数
-
可选依赖使用setter注入进行,灵活性强
-
可选依赖指对象在创建过程中注入的参数可有可无
-
Spring框架倡导使用构造器,
第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
-
如果有必要可以两者同时使用,
使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
-
实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
-
自己开发的模块推荐使用setter注入!
13-自动装配
5.3.1 什么是依赖自动装配?
-
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
5.3.2 自动装配方式有哪些?
-
按类型(常用)
-
按名称
-
按构造方法
5.3.4 完成自动装配的配置
自动装配只需要修改applicationContext.xml配置文件即可:
(1)将<property>标签删除
(2)在<bean>标签中添加autowire属性
首先来实现按照类型注入的配置
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
注意事项:
-
需要注入属性的类中对应属性的setter方法不能省略
-
被注入的对象必须要被Spring的IOC容器管理(必须在配置文件里)
<bean id="bookDao"(byType可以不起id) class="com.itheima.dao.impl.BookDaoImpl"/>
-
按照类型在Spring的IOC容器中如果找到多个对象,会报NoUniqueBeanDefinitionException
一个类型在IOC中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为:
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>
注意事项:
-
按照名称注入中的名称指的是什么?
-
bookDao是private修饰的,外部类无法直接访问
-
外部类只能通过属性的set方法进行访问
-
对外部类来说,setBookDao方法名,去掉set后首字母小写是其属性名
-
为什么是去掉set首字母小写?
-
这个规则是set方法生成的默认规则,set方法的生成是把属性名首字母大写前面加set形成的方法名
-
所以按照名称注入,其实是和对应的set方法有关,但是如果按照标准起名称,属性名和set对应的名是一致的
-
如果按照名称去找对应的bean对象,找不到则注入Null
-
当某一个类型在IOC容器中有多个对象,按照名称注入只找其指定名称对应的bean对象,不会报错
两种方式介绍完后,以后用的更多的是按照类型注入。
对于依赖注入,需要注意一些其他的配置特征:
-
自动装配用于引用类型依赖注入,不能对简单类型(reason:int 1 杂写bean?)进行操作
-
使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
-
使用按名称装配时(byName)必须保障容器中具有指定名称的bean,变量名与配置耦合,不推荐使用
-
自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
14-集合注入
5.4.2 注入数组类型数据
<propertyname="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
5.4.3 注入List类型数据
<propertyname="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
5.4.4 注入Set类型数据
<propertyname="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
5.4.5 注入Map类型数据
<propertyname="map">
<map>
<entrykey="country"value="china"/>
<entrykey="province"value="henan"/>
<entrykey="city"value="kaifeng"/>
</map>
</property>
5.4.6 注入Properties类型数据
<propertyname="properties">
<props>
<propkey="country">china</prop>
<propkey="province">henan</prop>
<propkey="city">kaifeng</prop>
</props>
</property>
说明:
-
property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array>、<list>、<set>、<map>、<props>标签
-
List的底层也是通过数组实现的,所以<list>和<array>标签是可以混用
-
集合中要添加引用类型,只需要把<value>标签改成<ref>标签,这种方式用的比较少
正常开发很少自己写集合注入
15-案例: 数据源对象管理
在这一节中,我们通过一个案例来学习对于第三方bean该如何进行配置管理。
以后我们会很多第三方的bean,本次案例使用前面提过的数据源Druid(德鲁伊)和C3P0来配置学习。
1.1.3 实现Druid管理
步骤1:导入druid的依赖
pom.xml中添加依赖
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
步骤2:配置第三方bean
在applicationContext.xml配置文件中添加DruidDataSource的配置
<!--管理DruidDataSource对象-->
<bean 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="root"/>
</bean>
说明:
-
driverClassName:数据库驱动
-
url:数据库连接地址
-
username:数据库连接用户名
-
password:数据库连接密码
-
数据库连接的四要素要和自己使用的数据库信息一致。
步骤3:从IOC容器中获取对应的bean对象
1.1.4 实现C3P0管理
注意:
-
数据连接池在配置属性的时候,除了可以注入数据库连接四要素外还可以配置很多其他的属性,具体都有哪些属性用到的时候再去查,一般配置基础的四个,其他都有自己的默认值
-
Druid和C3P0在没有导入mysql驱动包的前提下,
一个没报错一个报错,说明Druid在初始化的时候没有去加载驱动,而C3P0刚好相反
-
Druid程序运行虽然没有报错,但是
当调用DruidDataSource的getConnection()方法获取连接的时候,也会报找不到驱动类的错误
16-加载properties文件
1.2.1 第三方bean属性优化
步骤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命名空间namespace
<?xmlversion="1.0" encoding="UTF-8"?>
<beansxmlns ="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>
步骤3:加载properties配置文件
在配置文件中使用context命名空间下的标签来加载properties配置文件
<context: property-placeholderlocation="jdbc.properties"/>
步骤4:完成属性注入
使用${key}来读取properties配置文件中的内容并完成属性注入
//上面跟步骤二一样
http://www.springframework.org/schema/context/spring-context.xsd">
<context: property-placeholderlocation="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配置文件中的内容就已经完成。
1.2.2.3 注意事项
至此,读取properties配置文件中的内容就已经完成,但是在使用的时候,有些注意事项:
问题一:键值对的key为username引发的问题
在properties中配置键值对的时候,如果key设置为username
出现问题的原因是<context:property-placeholder/>标签会加载系统的环境变量,
而且环境变量的值会被优先加载,如何查看系统的环境变量?
public static void main (String[] args) throws Exception {
Map<String, String> env =System.getenv();
System.out.println(env);
}
大家可以自行运行,在打印出来的结果中会有一个USERNAME=XXX[自己电脑的用户名称];
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
system-properties-mode:设置为NEVER,表示不加载系统属性,就可以解决上述问题。
问题二:当有多个properties配置文件需要被加载,该如何配置?
-
调整下配置文件的内容,在resources下添加jdbc.properties,jdbc2.properties
-
修改applicationContext.xml
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配置文件
17-容器
2.2.1 容器的创建方式
案例中创建ApplicationContext的方式为:
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
这种方式翻译为: 加载类路径下的XML配置文件
除了上面这种方式,Spring还提供了另外一种创建方式为:
ApplicationContext ctx=new FileSystemXmlApplicationContext("applicationContext.xml");
这种方式翻译为: 文件系统下的XML配置文件
使用这种方式 运行,会出现如下错误:
从错误信息中能发现,这种方式是从项目路径下开始查找applicationContext.xml配置文件的,所以需要将其修改为(绝对路径):
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\workspace\\spring\\spring_10_container\\src\\main\\resources\\applicationContext.xml");
当项目的位置发生变化后,代码也需要跟着改,耦合度较高,不推荐使用
18-核心容器总结
本文来自博客园,作者:软工菜鸡,转载请注明原文链接:https://www.cnblogs.com/SElearner/p/17676691.html