Spring简介
-
额外知识点
- 在之前的学习中我们在
Service
业务层创建Dao/Mapper
数据访问层(持久层)的对象是通过工具类来获取对应Dao/Mapper
数据访问层(持久层)的接口代理对象 - 在此处我们不用工具类来获取对应
Dao/Mapper
数据访问层(持久层)的接口代理对象,而是创建一个Dao/Mapper
数据访问层(持久层)的接口的实现类,通过创建实现类的对象进而得到接口对象以供使用- 讲解程序开发原理以及后续IOC和DI示例均是以此形式讲解
- 在之前的学习中我们在
-
定义
- Spring是分层的JavaSE/EE应用full-stack轻量级开源框架,以IOC(Inversion of Control:控制反转) 和AOP(Aspect Oriented Programming:面向切面编程) 为内核
- 它提供了**
web/Controller
表现层SpringMVC** 、Service
业务层Spring 、Dao/Mapper
数据访问层(持久层)MyBatis/Spring JDBCTemplate 等众多企业级应用技术 - 他整合开源了众多第三方框架和类库,它是使用最多的JavaEE企业级应用开源框架
-
优点
- 方便解耦,简化开发,降低企业级开发的复杂性
- 通过Spring提供的IOC容器 ,可将对象间的依赖关系交由Spring进行控制,避免硬编码造成的过度耦合。
- 用户可不必再为单例模式类、属性文件解析等这些底层的需求编写代码,可更专注于上层应用
- 对AOP编程支持
- 通过Spring的AOP功能,方便进行面向切面编程。许多不易用传统OOP实现的功能可以用AOP轻松实现
- 对声明式事务的支持
- 可将开发人员从事务管理代码中解脱出来,通过声明式的方式灵活的进行事务管理,提高开发效率和质量
- 方便程序测试
- 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是可随手做的操作
- 框架整合,集成了各种优秀框架
- 比如:
Strus
、Hibemate
、Hessian
、Quartz
等
- 比如:
- 降低JavaEE API的使用难度
- 它对JavaEE API(eg:JDBC、JavaMail、远程调用等)进行了封装,降低其使用难度
- 方便解耦,简化开发,降低企业级开发的复杂性
-
Spring有一套属于自己的开发生态圈,它提供了若干项目,每个项目对应完成特定的功能,详见官网
-
主要学习
Spring Framework
:Spring技术基础Spring Boot
:提高开发效率Spring Cloud
:分布式开发相关技术
Spring体系结构
-
第一模块:核心容器(
Core Container
)Spring
框架最核心部分
-
第二模块
- 面向切面编程(
AOP
):依赖于核心容器 - AOP思想实现(
Aspects
):是对AOP的一个思想实现
- 面向切面编程(
-
第三模块
- 数据访问(
Data Access/Integration
) - 数据集成(
Data Integration
)
- 数据访问(
-
第四模块:Web开发
-
第五模块:单元测试与集成测试(
Test
)- 该框架整体均可进行测试
-
学习线路
Spring程序开发原理
-
Spring程序开发步骤
-
开发步骤详解
- 在之前的学习中,我们在
Service
业务层创建Dao/Mapper
数据访问层(持久层)的对象是通过new
Dao/Mapper
数据访问层(持久层)的接口实现类实现的,但是这种方式容易造成高耦合问题,所以我们现在舍弃该方式 - 而是通过配置Spring的xml配置文件来获取到
Dao/Mapper
数据访问层(持久层)的接口实现类对象- 方式是:利用id唯一标识标记
Dao/Mapper
数据访问层(持久层)接口实现类的全限定名
- 方式是:利用id唯一标识标记
- 唯一标识后,在
Service
业务层就不需要在newDao/Mapper
数据访问层接口实现类的对象,而是通过Spring客户端来调用Spring客户端提供的getBean("id标识")
方法来获取到Dao/Mapper
数据访问层(持久层)的接口实现类的对象
- 在之前的学习中,我们在
-
开发步骤详解中Spring客户端获取到对象的原理如下:
- Spring会先读取XML配置文件,然后根据id标识获取bean全限定名,Spring框架在获得bean全限定名后会通过反射创建bean对象,然后将该对象返回给开发人员
IOC
(Inversion of Control:控制反转)
-
定义
- 对象的创建控制权由程序内部转移到外部
-
解释
-
使用对象时,由主动new产生对象转换为由外部提供对象,在此过程中对象创建控制权由程序内部转移到外部
-
“外部” 指的是
IOC
容器,它负责象的创建、初始化等一系列工作,被创建或管理的对象在IOC
容器中称为bean
-
示例:
在原来的学习中我们在
Service
业务层时会在成员变量的位置new
一个共有的数据访问层(持久层)的对象,如图一所示,如果数据层有两个实现类,那么我们在业务层就需要针对不同的场景new
不同的对象,只要我们重新new
一次对象,那系统就需要重新编译、重新测试、重新部署和发布,造成高耦合问题为了避免高耦合,我们可以在使用对象的时候,在程序中不主动使用
new
来产生对象,转换为由外部提供对象。Spring就提供了一个Ioc
容器,用来充当Ioc
思想中的”外部“,它可以管理对象的创建和初始化的过程,若想用对象就从IOC
容器中拿出来使用即可,如图二所示。但是此时就算拿出来使用也不会运行,原因详见DI
依赖注入
-
-
IOC
容器- 它负责象的创建、初始化等一系列工作,被创建或管理的对象在
IOC
容器中统称为bean
- 它负责象的创建、初始化等一系列工作,被创建或管理的对象在
-
.注意
IOC
、IOC
容器、bean
是三个不同的概念- IOC(控制反转)能够避免高耦合,但是并不能降低不同架构层的依赖关系,若想降低依赖关系需依靠DI(依赖注入)
Spring的IOC
快速入门
-
开发步骤
-
导入Spring坐标
<!--spring坐标--> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.6</version> </dependency>
-
编写
Dao/Mapper
数据访问层(持久层)接口及其实现类 -
创建Spring核心配置文件,文件命名为
applicationContext.xml
-
在Spring配置文件中配置用到的接口的实现类
-
使用Spring的API获取bean实例(即对应接口实现类的对象)
-
-
完整步骤示例
-
Step1: 创建Web项目,导入Spring坐标及Tomcat插件,pom.xml文件如下:(在之后的示例中均需要该步,只是省略了)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>SpringDemo</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><name>SpringDemo Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--spring坐标--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.6</version></dependency></dependencies><build><finalName>SpringDemo</finalName><plugins><!-- Tomcat插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins></build> </project>
-
Step2: 创建三层架构的包结构,并编写
Dao/Mapper
数据访问层(持久层)接口及其实现类。如图所示- 实现类要写对应架构包的
impl
包下
- 实现类要写对应架构包的
-
Step3: 右键源代码配置文件目录(即资源文件
resources
)→New
→XML Configuration File
→Spring Config
,文件名为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>
-
Step4: 配置用到的接口的实现类:在
applicationContext.xml
文件中利用<bean>
标签配置bean
,代码如下所示该标签用到的属性 解释 id
Spring容器中的 bean
的唯一标识符,可用来引用特定的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="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><!--等同于<bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"></bean>--> </beans>
-
Step5: 使用Spring的API获取bean的实例:创建测试类代码TestOne,代码及使用IOC容器步骤如下:
- 测试类代码是在测试目录test下进行的测试
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestOne {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao = (UserDaoImpl) app.getBean("userDaoImpl");//3 方法运行userDao.save();} }
-
-
普通获取IOC容器两种方式
- 在以上测试代码中我们获取IOC容器是通过加载类路径下的配置文件来获取的。即
new ClassPathXmlApplicationContext("applicationContext.xml");
- 若我们从硬盘上获取则改为
new FileSystemXmlApplicationContext("F:\\node\\idea\\ssm\\SpringDemo\\SpringWebTwo\\src\\main\\resources\\applicationContext.xml");
- 若想获取多个
- 在以上测试代码中我们获取IOC容器是通过加载类路径下的配置文件来获取的。即
-
从IOC容器中获取bean对应的对象
- 使用bean的
id
或name
获取(需要强转):(UserDaoImpl) app.getBean("userDaoImpl");
- 使用bean的
id
或name
获取时并指定类型(不需要强转):app.getBean("userDaoImpl", UserDaoImpl.class);
- 按类型进行自动装配时(必须保证对应的bean唯一,否则失效):
app.getBean(UserDaoImpl.class);
- 使用bean的
-
ApplicationContext
接口及其相关的接口和类如图所示ApplicationContext
接口的最顶级父类为BeanFactory
接口BeanFactory
接口为IOC容器的顶层接口,初始化BeanFactory
对象时所有的bean均为延迟加载ApplicationContext
接口是Spring容器的核心接口,初始化ApplicationContext
对象时所有的bean均为立即加载ApplicationContext
接口提供基础的bean操作的相关方法,通过其它接口拓展其功能ApplicationContext
的子接口ConfigurableApplicationContext
拓展了一个ApplicationContext
接口没有的colse()
方法(即关闭容器的功能ConfigurableApplicationContext
下有常用的初始化实现类ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
Spring配置文件applicationContext.xml
<bean>
标签详解一
-
作用:用于配置对象并交由Spring来创建(即定义Spring核心容器管理的对象)
-
默认情况下它调用的是类中的无参构造函数 ,若没有无参构造函数则会创建失败
-
基本属性
该标签用到的属性 解释 id
Spring容器中的 bean
的个唯一的标识符,可用来引用特定的bean
实例name
定义bean的别名,定义多个别名时可用 ,
;
或空格分隔。class
bean
的实现类的全限定名scope
指定对象的作用范围 init-method
指定类中初始化方法的名称 destory-method
指定类中销毁方法的名称 factory-bean
使用指定的工厂bean实例来调用工厂方法创建指定的bean实例 factory-method
使用指定工厂bean的方法来指定的bean实例 p:propertyName="propertyValue"
P命名空间依赖注入时使用的属性,此属性形式只适用于基本数据类型、字符串类型。 propertyName
: bean 中要设置的属性的名称。propertyValue
:是要为该属性设置的值p:propertyName-ref="propertyValue"
P命名空间依赖注入时使用的属性,此属性形式只适用于引用类型,其中 ref
代表是引用类型。propertyName
: bean 中要设置的属性的名称。propertyValue
:是要为该属性设置的值autowire
自动装配 bean 的依赖关系 autowire-candidate
用于控制一个 bean 是否可以作为其他 bean 自动装配的候选者,默认为 true
lazy-init
控制bean的延迟加载,默认为 false
(非延迟)scope
属性取值解释 singleton
默认值,单例。即在IOC容器中对应bean的对象只有一个 prototype
多例。即在IOC容器中对应bean的对象有多个,每次在使用 app.getBean()
方法时都会获得对应bean的新对象request
Web项目中,Spring创建一个bean的对象,将对象存入到 request
域中session
Web项目中,Spring创建一个bean的对象,将对象存入到 session
域中global session
Web项目中,应用在Portlet环境,若没有Portlet环境则 global session
相当于session
autowire
属性取值解释 no
默认值,默认不使用自动装配 byType
通过属性的类型进行自动装配 byName
通过属性名称进行自动装配 constructor
通过构造函数进行自动装配
name
属性——别名配置
-
创建三层架构的包结构,并编写
Dao/Mapper
数据访问层(持久层)接口及其实现类。如图所示- Step1: 实现类要写对应架构包的
impl
包下
- Step2:
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"><!--配置bean--><bean id="userDaoImpl" name="userDao1 userDao2" class="at.guigu.dao.impl.UserDaoImpl"/><!--等同于<bean id="userDaoImpl" name="userDao1 userDao2" class="at.guigu.dao.impl.UserDaoImpl"></bean>--> </beans>
- Step3: 在测试目录下创建测试类TestTwo,代码如下:
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestTwo {/*** 测试name属性*/@Testpublic void Test2() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象//利用name属性定义别名后getBean方法的参数不仅可以是id(即对应bean的唯一标识),还可以是对应bean的别名UserDaoImpl userDao1 = (UserDaoImpl) app.getBean("userDaoImpl");//等同于UserDaoImpl userDao2 = (UserDaoImpl) app.getBean("userDao1");//等同于UserDaoImpl userDao3 = (UserDaoImpl) app.getBean("userDao2");} }
- Step1: 实现类要写对应架构包的
scope
属性——作用范围配置
-
scope = "singleton"
示例(以Spring的IOC快速入门代码为例进行讲解)-
Step1: 创建三层架构的包结构,并编写
Dao/Mapper
数据访问层(持久层)接口及其实现类。如图所示- 实现类要写对应架构包的
impl
包下
- 实现类要写对应架构包的
-
Step2:
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"><!--配置bean--><bean id="userDaoImpl" scope="singleton" class="at.guigu.dao.impl.UserDaoImpl"/><!--等同于<bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"></bean>--> </beans>
-
Step3: 在测试目录下创建测试类TestTwo,代码如下:
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestTwo {/*** 测试scope = "singleton"*/@Testpublic void Test1() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao1 = (UserDaoImpl) app.getBean("userDaoImpl");UserDaoImpl userDao2 = (UserDaoImpl) app.getBean("userDaoImpl");//3 对象地址打印看是否是同一个地址System.out.println(userDao1);System.out.println(userDao2);} }
运行该测试用例后,截图如下
-
-
scope = "prototype"
示例(以Spring的IOC快速入门代码为例进行讲解)-
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"><!--配置bean--><bean id="userDaoImpl" scope="prototype" class="at.guigu.dao.impl.UserDaoImpl"/></beans>
-
测试类TestTwo代码不变,运行结果如下所示
-
-
注意:
scope = "singleton"
与scope = "prototype"
创建bean对象的时机不同- 当
scope = "singleton"
时(即默认情况下),bean对象随着这句代码ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
的执行就会立即创建并保存到IOC容器中 - 当
scope = "prototype"
时,bean对象会随着getBean()
方法的执行被创建
- 当
-
scope = "singleton"
与scope = "prototype"
异同scope = "singleton"
- bean的实例化个数:1
- bean的实例化时机:当Spring核心文件被加载时
- bean的生命周期
- 对象创建:当应用加载,创建容器时对象就被创建了
- 对象运行:只要容器存在,对象就一直存活
- 对象销毁:当应用卸载,销毁容器时,对象就会被销毁
scope = "prototype"
- bean的实例化个数:多个
- bean的实例化时机:当调用
getBean()
方法时 - bean的生命周期
- 对象创建:当使用对象时,就会创建新的对象实例
- 对象运行:只要对象在使用中就一直存活
- 对象销毁:当对象长时间不用时,就会被Java的垃圾回收器回收
-
为什么IOC容器默认为单例?
- Spring容器管理的对象为单例对象,也就是我们可以复用的对象,这次用完以后下次还会用这个对象,这样可以提高性能和资源利用效率,避免重复创建对象导致的资源浪费,同时简化开发和管理,确保应用中的一致性和数据共享。
-
适合为单例对象的bean
Dao/Mapper
数据层(持久层)对象Service
业务对象Web/Controller
表现层对象- util包的工具对象
- 以上的bean适合交给Spring的IOC容器管理
-
不适合为单例对象的bean
- 在pojo包下封装实体的域对象:因为这种对象每次的属性值都不同
init-method
、destory-method
属性——初始、销毁
init-method
:指定类中初始化方法的名称destory-method
:指定类中销毁方法的名称- 容器关闭前会触发bean的销毁
初始、销毁方式一
-
步骤如下
-
Step1: 将
Dao/Mapper
数据访问层(持久层)接口的实现类UserDaoImpl
代码更改如下:package at.guigu.dao.impl;import at.guigu.dao.UserDao;public class UserDaoImpl implements UserDao {public UserDaoImpl () {System.out.println("UserDaoImpl被创建...");}// bean初始化时对应的操作public void init() {System.out.println("初始化方法");}// bean销毁前对应的操作public void destroy() {System.out.println("销毁方法");}public void save() {System.out.println("UserDao save running...");} }
注意:init()和destroy()只是我们定义的两个方法,我们还需要在Spring配置文件中给其进行配置后,这两个方法才是真正的初始化方法和销毁方法
-
Step2: 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"><!--配置bean--><bean id="userDaoImpl" init-method="init" destroy-method="destroy" class="at.guigu.dao.impl.UserDaoImpl"/></beans>
-
Step3: 在测试目录下创建测试类
TestThree
,代码如下package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestThree {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao = (UserDaoImpl) app.getBean("userDaoImpl");System.out.println(userDao);} }
运行该测试用例后,截图如下
-
-
注意: 在上述运行截图中可看出该单元测试没有执行
destroy()
方法原因是:我们现在运行的程序是运行在Java虚拟机上的,当程序执行完毕后虚拟机会立即退出,并不会给bean销毁的机会。所以我们可以通过两种方式来销毁bean-
方式一: 在虚拟机退出之前手动释放容器资源,代码做如下更改:
- 注意:
ApplicationContex
接口没有释放资源的close()
方法,而我们所使用的其子接口的实现类ClassPathXmlApplicationContext
有释放资源的close()
方法,所以我们要进行强制转换
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestThree {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao = (UserDaoImpl) app.getBean("userDaoImpl");System.out.println(userDao);//方式一: 手动关闭((ClassPathXmlApplicationContext) app).close();} }
运行该测试用例后,截图如下
- 注意:
-
方式二: 设置关闭钩子 ,即允许在 JVM虚拟机 关闭之前执行一些清理操作,如释放资源、保存状态等。代码更改如下
- 注意:
registerShutdownHook()
方法是AbstractApplicationContext
接口的方法,所以需要先强制转换在调用
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestThree {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//方式二: 设置关闭钩子((AbstractApplicationContext) app).registerShutdownHook();//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao = (UserDaoImpl) app.getBean("userDaoImpl");System.out.println(userDao);/*方式一:手动关闭((ClassPathXmlApplicationContext) app).close();*/} }
运行该测试用例后,截图如下
- 注意:
-
两种执行销毁方法的方式区别
- 手动释放容器资源必须在放在最后,属于暴力手段
- 设置关闭钩子可在任意位置,属于柔软手段
-
初始、销毁方式二
-
初始、销毁方式一比较复杂,因为当类比较多时就需要重复配置配置文件,所以就有了初始、销毁方式二
-
步骤如下
-
Step1:
Dao/Mapper
数据访问层(持久层)接口的实现类UserDaoImpl
代码如下:- 该实现类除了要实现
UserDao
接口外还要实现Initializingbean
,和Disposablebean
两个接口,并分别重写这两个接口中的afterPropertiesSet()
方法和destroy()
方法
package at.guigu.dao.impl;import at.guigu.dao.UserDao; import at.guigu.pojo.User; import org.springframework.beans.factory.Disposablebean; import org.springframework.beans.factory.Initializingbean;import java.util.List; import java.util.Map; import java.util.Properties;public class UserDaoImpl implements UserDao, Initializingbean, Disposablebean {public UserDaoImpl () {System.out.println("UserDaoImpl被创建...");}/*** 初始化方法* @throws Exception*/@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Dao init...");}/*** 销毁方法* @throws Exception*/@Overridepublic void destroy() throws Exception {System.out.println("Dao destroy...");}public void save() {System.out.println("UserDao save running...");} }
- 该实现类除了要实现
-
Step2:
Service
业务层接口及实现类代码如下-
接口
BookService
package at.guigu.service;public interface BookService {public void save(); }
-
接口
BookService
的实现类BookServiceImpl
该实现类除了要实现
BookService
接口外还要实现Initializingbean
,和Disposablebean
两个接口,并分别重写这两个接口中的afterPropertiesSet()
方法和destroy()
方法package at.guigu.service.impl;import at.guigu.dao.UserDao; import at.guigu.dao.impl.UserDaoImpl; import at.guigu.service.BookService; import org.springframework.beans.factory.Disposablebean; import org.springframework.beans.factory.Initializingbean;public class BookServiceImpl implements BookService, Initializingbean, Disposablebean {private UserDao userDao;/*** 属性设置* @param userDao*/public void setUserDao(UserDaoImpl userDao) {System.out.println("Service对userDao属性设置");this.userDao = userDao;}/*** 初始化方法* @throws Exception*/@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Service init...");}/*** 销毁方法* @throws Exception*/@Overridepublic void destroy() throws Exception {System.out.println("Service destory...");}@Overridepublic void save() {System.out.println("BookService save...");userDao.save();}}
-
-
Step3: 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"><!--1 配置userDao bean--><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><!--2 配置bookService bean--><bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImpl"><!--3 绑定依赖关系--><property name="userDao" ref="userDaoImpl"></property><!--等同于<property name="userDao"><ref bean="userDaoImpl"/></property>--></bean></beans>
-
Step4:在测试目录下创建测试类
TestThree
,代码如下package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestThree {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");// 设置关闭钩子((AbstractApplicationContext) app).registerShutdownHook();//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");System.out.println(bookService);} }
-
-
注意
- 在使用实现
Initializingbean
,和Disposablebean
两个接口来进行初始化和销毁时,其中初始化方法afterPropertiesSet()
在属性设置(即setXXX
方法)之后执行,可详见上述运行截图
- 在使用实现
-
使用实现接口的方式来执行初始化和销毁时的bean生命周期
- 初始化容器
- 创建对象
- 执行构造方法
- 执行属性注入(set操作)
- 执行bean初始化方法
- 使用bean
- 执行业务操作
- 关闭/销毁容器
- 执行bean销毁方法
- 初始化容器
bean实例化的三种方式(factory-bean
、factory-method
)
-
factory-bean
:使用指定的工厂bean实例来调用工厂方法创建指定的bean实例 -
factory-method
:使用指定工厂bean的方法来指定的bean实例 -
bean实例化的三种方式
- 无参构造方法实例化
- 工厂静态方法实例化
- 工厂实例方法实例化
- 注意:
- 默认情况下使用第一种方式时,若无参构造方法不存在则会抛出
beanCreationException
异常 - 默认情况下用的都是第一种,通过配置可使用后两种实例化方法,此处主要解释后两种
- 默认情况下使用第一种方式时,若无参构造方法不存在则会抛出
工厂静态方法实例化
-
工厂静态方法实例化步骤
-
Step1: 创建三层架构的包结构,并编写
Dao/Mapper
数据访问层(持久层)接口及其实现类。如图所示- 实现类要写对应架构包的
impl
包下
- 实现类要写对应架构包的
-
Step2: 在工具类包
util
下创建工厂类(博主创建名为:StaticFactoryUtils
),代码如下package at.guigu.util;import at.guigu.dao.UserDao; import at.guigu.dao.impl.UserDaoImpl;public class StaticFactoryUtil {//此为静态的public static UserDao getUserDao() {return new UserDaoImpl();}}
-
Step3: 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"><!--配置bean--><bean id="userDaoImpl" factory-method="getUserDao" class="at.guigu.util.StaticFactoryUtil"/></beans>
注意:工厂静态方法实例化时,由于
StaticFactoryUtils
类中实例化的方法为静态方法,所以直接由类名调用静态方法即可创建UserDao
对象 -
Step4: 创建测试类
TestFour
测试,代码如下package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestFour {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao = (UserDaoImpl) app.getBean("userDaoImpl");System.out.println(userDao);} }
-
工厂实例方法实例化
-
工厂实例方法实例化方式一,步骤如下
-
Step1: 创建三层架构的包结构,并编写
Dao/Mapper
数据访问层(持久层)接口及其实现类。如图所示- 实现类要写对应架构包的
impl
包下
- 实现类要写对应架构包的
-
Step2: 在工具类包
util
下创建工厂类(博主创建名为:DynamicFactoryUtils
),代码如下package at.guigu.util;import at.guigu.dao.UserDao; import at.guigu.dao.impl.UserDaoImpl;public class DynamicFactoryUtils {//此为非静态的public UserDao getUserDao() {return new UserDaoImpl();} }
-
Step3: 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"><!--配置bean--><!--1 创建工厂对象并存入IOC容器--><bean id="dynamicFactoryUtils" class="at.guigu.util.DynamicFactoryUtils"/><!--2 调用IOC容器中的工厂对象中的实例化方法实例化对象--><bean id="userDaoImpl" factory-bean="dynamicFactoryUtils" factory-method="getUserDao"/> </beans>
注意:
DynamicFactoryUtils
类中实例化的方法为非静态方法时,必须先获取工厂类的对象再由工厂类的对象来调用getUserDao()
方法 -
Step4: 创建测试类TestFive测试,代码如下
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestFive {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao = (UserDaoImpl) app.getBean("userDaoImpl");System.out.println(userDao);} }
-
-
在工厂实例方法实例化方式一 中有很大的缺点,如图所示,由此衍生出了第二种工厂实例方法实例化的方式
-
第二种工厂实例化方式的工具类继承自
Factorybean<T>
接口,其源码如下:public interface Factorybean<T> {T getObject() throws Exception;Class<?> getObjectType();boolean isSingleton(); }
方法 解释 T getObject() throws Exception;
Factorybean
接口的核心方法。Spring 容器调用这个方法来获取这个工厂生成的 bean 实例。返回类型T
表示工厂生成的对象类型Class<?> getObjectType();
返回由 Factorybean
创建的对象的类型。如果在创建对象之前类型未知,则返回null
boolean isSingleton();
返回这个由 Factorybean
创建的 bean 是否为单例,默认为ture
(即单例)
-
-
工厂实例方法实例化方式二,步骤如下
-
Step1: 创建三层架构的包结构,并编写
Dao/Mapper
数据访问层(持久层)接口及其实现类。如图所示- 实现类要写对应架构包的
impl
包下
- 实现类要写对应架构包的
-
Step2: 在工具类包
util
下创建实现Factorybean
接口的工厂类(博主创建名为:DynamicFactoryUtilsTwo
),代码如下- 接口
Factorybean<T>
的泛型T为:想通过该工具类获取的对象的类名
package at.guigu.util;import at.guigu.dao.UserDao; import at.guigu.dao.impl.UserDaoImpl; import org.springframework.beans.factory.Factorybean;public class DynamicFactoryUtilsTwo implements Factorybean<UserDaoImpl> {/*** 代替原始实例工厂中创建对象的方法* @return* @throws Exception*/@Overridepublic UserDaoImpl getObject() throws Exception {return new UserDaoImpl();}@Overridepublic Class<?> getObjectType() {return UserDaoImpl.class;} }
-
Step3: 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"><!--配置bean--><!--此时id为接口Factorybean<UserDaoImpl>的泛型,即<UserDaoImpl>--><!--此时class为实例工厂的类的全限定名--><bean id="userDaoImpl" class="at.guigu.util.DynamicFactoryUtilsTwo"/> </beans>
-
Step4: 创建测试类
TestFive
测试,代码如下package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestFive {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao = (UserDaoImpl) app.getBean("userDaoImpl");UserDaoImpl userDao2 = (UserDaoImpl) app.getBean("userDaoImpl");System.out.println(userDao);System.out.println(userDao2);} }
- 接口
-
-
在 工厂实例方法实例化方式二 运行截图中可看出bean作用范围默认为单例,若想改为多例 则有两种方式:
-
方式一: 利用
scope
属性(此处省略,可详见作用范围配置部分) -
方式二: 重写
Factorybean
接口中的isSingleton()
方法,实现Factorybean
接口的工厂类(博主创建名为:DynamicFactoryUtilsTwo
),代码如下package at.guigu.util;import at.guigu.dao.UserDao; import at.guigu.dao.impl.UserDaoImpl; import org.springframework.beans.factory.Factorybean;public class DynamicFactoryUtilsTwo implements Factorybean<UserDaoImpl> {/*** 代替原始实例工厂中创建对象的方法* @return* @throws Exception*/@Overridepublic UserDaoImpl getObject() throws Exception {return new UserDaoImpl();}@Overridepublic Class<?> getObjectType() {return UserDaoImpl.class;}/*** 设置bean的作用范围* @return*/@Overridepublic boolean isSingleton() {return false;} }
此时在运行测试类
TestFive
,截图如下
-
DI
(Dependency Injection:依赖注入)
-
定义
- 在容器中建立bean与bean之间的依赖关系的整个过程就叫做依赖注入
- 它是Spring框架核心IOC的具体实现
-
解释
-
示例:
图二中就算由外部提供对象也不可能成功运行,因为就算得到了
Service
对象,但由于Service
接口的BookServiceImpl
实现类内部需要一个Dao/Mapper
对象供其使用,也就是说Service
的实现类依赖于Dao/Mapper
对象才能运行,因为此时Service
与Dao/Mapper
对象之间并没有建立依赖关系,所以程序无法运行,所以就会报错,如图三所示若想成功运行就需要给两者绑定依赖关系,此时只要
Service
实现类得到Service
对象,就会连带着得到Dao/Mapper
对象,此时即可成功运行
-
-
它是基于IOC管理bean而实现的
DI
,使用DI(依赖注入)后,Spring框架会自动把Dao/Mapper
数据访问层(持久层)对象传入Service
业务层,而不需要我们自己去获取 -
注意
Service
业务层实现类BookServiceImpl
中不能在使用new
的形式创建Dao/Mapper
数据层实现类的对象Service
业务层中需要的Dao/Mapper
数据层对象是通过Service
业务层提供的方法进入到Service
业务层中的Service
业务层与Dao/Mapper
数据层通过配置文件来进行依赖关系的绑定
Spring配置文件applicationContext.xml
<bean>
标签详解二
-
<bean>
标签的内嵌标签<bean>
标签中用到的内嵌标签解释 <property>
在 XML 配置文件中为 bean 的属性赋值,从而完成依赖注入和配置管理 <constructor-arg>
在 XML 配置文件中指定 bean 的构造函数参数。它允许我们在创建 bean 实例时通过构造函数注入依赖,而不是通过 setter 方法进行属性注入。 -
<property>
中用到的属性<property>
标签用到的属性解释 name
指定要设置的 bean 属性的名称 ref
用于指定一个 引用类型 的属性值,引用另一个 bean 的实例 value
用于直接设置基本数据类型或字符串数据类型的属性值 -
<constructor-arg>
中用到的属性<constructor-arg>
标签用到的属性解释 name
指定构造函数的参数名称 type
指定构造函数的参数类型。构造器参数若为对象,则属性值为对象的全类名;若为字符串类型,则为 java.lang.String
index
指定构造函数的参数索引位置(以0为第一个参数索引位置) ref
IOC容器中可获取到依赖对象的bean的id值(即唯一标识) -
<property>
标签的内嵌标签<property>
标签中用到的内嵌标签解释 <value>
设置基本数据类型、字符串类型的属性值 <ref>
引用其他 bean <list>
配置 List
集合的属性值<map>
配置 Map
集合的属性值<prop>
配置 Properties
集合的属性值
Spring的DI
(依赖注入)的三种方式
-
依赖注入方式选择
setter
方法注入- 可选依赖:即该bean对这个属性依赖不强,可有可无,不注入时也不会影响程序的运行
- P命名空间注入
- 有参构造方法注入
- 强制依赖:即该bean必须依赖的属性,若为给该属性进行依赖注入,程序就无法运行
-
注意
- 使用有参构造方法注入时若不进行注入则配置文件就会报错,程序就无法运行
- Spring框架倡导使用构造器注入,第三方框架内部大多采用构造器注入的形式进行数据初始化,这样相对严谨
- 若有必要时
setter
方法注入和有参构造方法注入可同时使用。即使用setter
方法注入完成可选依赖的注入;使用有参构造方法注入完成强制依赖的注入 - 自己开发的模块推荐使用
setter
方法注入 - 实际开发过程中还要根据实际情况分析,若受控对象未提供
setter
方法时就必须使用构造器注入
Spring的DI
快速入门——setter
方法注入
<property>
标签:在 XML 配置文件中为 bean 的属性赋值,从而完成依赖注入和配置管理
<property> 标签用到的属性 | 解释 |
---|---|
name | 指定要设置的 bean 属性的名称 |
ref | 用于指定一个引用类型的属性值,引用另一个 bean 的实例 |
-
set
方法注入步骤如下:-
Step1:
Service
业务层创建接口BookService
及其实现类BookServiceImpl
- 接口
BookService
package at.guigu.service;public interface BookService {public void save(); }
- 接口
-
Step2: 给所依赖的对象提供对应的
setter
方法:即在Service
业务层BookServiceImpl
类中创建一个SetXXX
方法,将BookDaoImpl
对象给到我们定义的bookDao
package at.guigu.service.impl;import at.guigu.dao.UserDao; import at.guigu.service.BookService;public class BookServiceImpl implements BookService {private UserDao userDao;@Overridepublic void save() {System.out.println("BookService save...");userDao.save();}// 给所依赖的对象提供对应的setter方法public void setUserDao(UserDao userDao) {this.this.userDao = userDao;} }
-
Step3: 配置依赖关系:在Spring容器中将
BookDaoImpl
绑定到BookServiceImpl
内部,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"><!--1 配置userDao bean--><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><!--2 配置bookService bean--><bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImpl"><!--3 绑定依赖关系--><property name="userDao" ref="userDaoImpl"></property><!--等同于<property name="userDao"><ref bean="userDaoImpl"/></property>--></bean> </beans>
注意:
name
属性值:Service
业务层中给所依赖的对象提供对应的setXXX方法中的XXX,开头字母变小写即可,比如setUserDao()
,此时name=userDao
ref
属性值:IOC容器中可获取到依赖对象的bean的id值(即唯一标识) -
创建测试类
TestSix
,代码如下:package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import at.guigu.service.BookService; import at.guigu.service.impl.BookServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSix {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
-
Spring的DI
快速入门——P命名空间注入
-
该方式本质上也是setter方法注入,但要比setter方法更方便
-
注意:
- 由于其本质也是setter方法注入,所以也要给所依赖的对象提供对应的setter方法 ,所
BookServiceImpl
类代码与 setter方法注入 中的一样 - P命名空间注入只能用于基本数据类型、字符串类型和引用类型,不支持集合类型(如
List
、Set
、Map
)的配置。对于集合类型,仍需要使用传统的<property>
标签及相关子元素(如<list>
、<set>
、<map>
)进行配置。
- 由于其本质也是setter方法注入,所以也要给所依赖的对象提供对应的setter方法 ,所
-
P命名空间注入步骤如下
-
Step1: 在Spring配置文件
applicationContext.xml
中引入P命名空间,即xmlns:p="http://www.springframework.org/schema/p"
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"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>
-
Step2: 引入P命名空间后Spring配置文件
applicationContext.xml
代码如下<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:p="http://www.springframework.org/schema/p"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="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><bean id="bookServiceImpl" p:userDao-ref="userDaoImpl" class="at.guigu.service.impl.BookServiceImpl"/></beans>
-
测试类
TestSix
代码如下:package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import at.guigu.service.BookService; import at.guigu.service.impl.BookServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSix {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
-
Spring的DI
快速入门——有参构造方法注入
-
注意
- 有参构造方法注入不需要
BookServiceImpl
类中的setter
方法
- 有参构造方法注入不需要
-
有参构造方法注入步骤如下
-
Step1:
Service
业务层BookServiceImpl
类代码如下package at.guigu.service.impl;import at.guigu.dao.UserDao; import at.guigu.dao.impl.UserDaoImpl; import at.guigu.service.BookService;public class BookServiceImpl implements BookService {private UserDao userDao;public BookServiceImpl() {}public BookServiceImpl(UserDaoImpl userDao) {this.userDao = userDao;}@Overridepublic void save() {System.out.println("BookService save...");userDao.save();}}
-
**Step2: ** 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"><!--配置bean--><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImpl"><!--name属性值为:有参构造器的参数名--><!--ref属性值为:IOC容器中可获取到依赖对象的bean的id值(即唯一标识)--><constructor-arg name="userDao" ref="userDaoImpl"></constructor-arg></bean></beans>
注意:
name
属性值:为BookServiceImpl
类中有参构造器的参数名ref
属性值:IOC容器中可获取到依赖对象的bean的id值(即唯一标识) -
测试类
TestSix
代码如下:package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import at.guigu.service.BookService; import at.guigu.service.impl.BookServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSix {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
-
有参构造方法注入弊端
-
若构造器参数名改变 ,对应配置文件中
<constructor-arg>
标签的name
属性值就需要随之改变,则解决办法如下-
用
type
标签代替name
标签 ,此时配置文件中代码如下:<?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="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImpl"><!--type属性值为:有参构造器的参数对象对应的全限定名--><!--ref属性值为:IOC容器中可获取到依赖对象的bean的id值(即唯一标识)--><constructor-arg type="at.guigu.dao.impl.UserDaoImpl" ref="userDaoImpl"></constructor-arg></bean></beans>
-
用
index
标签代替name
标签<?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="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImplTwo"><!--index属性值为:有参构造器参数的索引位置--><!--ref属性值为:IOC容器中可获取到依赖对象的bean的id值(即唯一标识)--><constructor-arg index="0" ref="userDaoImpl"></constructor-arg></bean></beans>
-
-
若构造器参数为基本数据类型和字符串类型
-
BookServiceImpl
代码如下package at.guigu.service.impl;import at.guigu.dao.UserDao; import at.guigu.dao.impl.UserDaoImpl; import at.guigu.service.BookService;public class BookServiceImpl implements BookService {private String aaa;private int bbb;private boolean ccc;public BookServiceImpl() {}public BookServiceImpl(String aaa, int bbb, boolean ccc) {this.aaa = aaa;this.bbb = bbb;this.ccc = ccc;}@Overridepublic void save() {System.out.println(aaa + "===" + bbb + "===" + ccc);}}
-
此时配置文件中代码如下:
-
采用
name
属性配置<?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="bookServiceImpl" class="at.guigu.service.impl.BookServiceImplTwo"><!--name属性值为:有参构造器的参数名--><!--ref属性值为:IOC容器中可获取到依赖对象的bean的id值(即唯一标识)--><constructor-arg name="aaa" value="zhangsan"></constructor-arg><constructor-arg name="bbb" value="2"></constructor-arg><constructor-arg name="ccc" value="false"></constructor-arg></bean></beans>
-
采用
type
属性配置<?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="bookServiceImpl" class="at.guigu.service.impl.BookServiceImplTwo"><!--type属性值为:构造函数的参数类型--><!--ref属性值为:IOC容器中可获取到依赖对象的bean的id值(即唯一标识)--><constructor-arg type="java.lang.String" value="zhangsan"></constructor-arg><constructor-arg type="int" value="2"></constructor-arg><constructor-arg type="boolean" value="false"></constructor-arg></bean></beans>
-
采用
index
属性配置<?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="bookServiceImpl" class="at.guigu.service.impl.BookServiceImplTwo"><!--index属性值为:有参构造器参数的索引位置--><!--ref属性值为:IOC容器中可获取到依赖对象的bean的id值(即唯一标识)--><constructor-arg index="0" value="zhangsan"></constructor-arg><constructor-arg index="1" value="2"></constructor-arg><constructor-arg index="2" value="false"></constructor-arg></bean></beans>
-
-
-
注意
- 当有参构造器的参数中有多个引用型参数时,
name
、type
、index
三个属性均可使用,自行选择(尽量选择低耦合的方式) - 当有参构造器的参数中有多个基本数据类型及字符串类型时
- 若基本数据类型无重复且只有一个字符串类型时,以上三种属性均可使用,自行选择(尽量选择低耦合的方式)
- 若基本数据类型中存在重复情况且字符串类型也有重复,则此时
type
属性就无法使用,只能从name
、index
两个属性中自行选择(尽量选择低耦合的方式)
- 当有参构造器的参数中有多个引用型参数时,
依赖自动装配
- 定义
- IOC容器根据bean所依赖的资源在容器中自动查找并注入到bean的过程即为自动装配
- 自动装配的方式有三种
- 按类型
autowire="byType"
:通过属性的类型进行自动装配 - 按名称
autowire="byName"
:通过属性名称进行自动装配 - 按构造方法
autowire="constructor"
:通过构造函数进行自动装配 - 默认情况下
autowire="no"
:即不适用自动装配
- 按类型
- 注意
- 依赖自动装配只能用于引用类型的依赖注入,不能对基本数据类型以及字符串类型进行操作
- 按类型或名称自动装配时必须有
setter
方法存在,且只能通过setter
方法进行自动装配 - 按构造方法自动装配时必须有有参构造器的存在,且只能通过有参构造器进行自动装配
- 使用按类型自动装配时,必须保障IOC容器中相同类型的bean唯一
- 使用按名称自动装配时,必须保障容器中具有指定名称的bean
- 不推荐使用按名称自动装配,因为变量名与配置完全耦合
- 依赖自动装配优先级低于依赖注入的三种方式 ,若同时出现则依赖自动装配会自动失效
按类型自动装配
-
注意
- 按类型自动装配 必须 有
setter
方法的存在,且只能通过setter
方法进行自动装配 - 使用按类型进行自动装配时,对应bean的
id
唯一标识或name
别名可以不存在- 因为按类型进行自动装配依赖于 Spring 容器中 bean 的类型匹配,而不是依赖于 bean 的
id
或name
- 因为按类型进行自动装配依赖于 Spring 容器中 bean 的类型匹配,而不是依赖于 bean 的
- 按类型自动装配时,必须保障IOC容器中相同类型的bean唯一
- Spring 容器会查找与属性类型匹配的 bean。如果有多个相同类型的 bean,Spring 会抛出
NoUniquebeanDefinitionException
异常,因为它无法确定应该注入哪个 bean。
- Spring 容器会查找与属性类型匹配的 bean。如果有多个相同类型的 bean,Spring 会抛出
- 按类型自动装配 必须 有
-
属性类型对应IOC容器中的bean唯一
-
Dao/Mapper
数据访问层(持久层)、Service
业务层的接口及实现类如下 -
配置文件代码如下
<?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="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImpl" autowire="byType"/></beans>
-
测试代码如下
package at.guigu.service;import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestOne {@Testpublic void test1() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
运行结果如下
-
-
属性类型对应IOC容器中的bean不唯一
-
Dao/Mapper
数据访问层(持久层)、Service
业务层的实现类及配置文件代码如下 -
测试代码如下
package at.guigu.service;import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestOne {@Testpublic void test1() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
运行结果如下
-
-
属性类型对应IOC容器中的bean不唯一的解决方式
- 可以使用
autowire-candidate=falser
将对应的bean设置成不可作为其它bean自动装配的候选者 - 可以使用按名称自动装配
- 可以使用
按名称自动装配
-
注意
- 按名称自动装配必须有
setter
方法的存在,且只能通过setter
方法进行自动装配 - 使用按名称自动装配时,必须保障容器中具有指定名称的bean
- 即要进行自动装配的属性名必须与对应bean的
id
唯一标识或name
别名完全一致
- 即要进行自动装配的属性名必须与对应bean的
- 按名称自动装配必须有
-
未使用别名方式
-
Dao/Mapper
数据访问层(持久层)、Service
业务层的实现类及配置文件代码如下测试代码如下
package at.guigu.service;import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestOne {@Testpublic void test1() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
运行结果如下
-
-
使用别名方式
-
Dao/Mapper
数据访问层(持久层)、Service
业务层的实现类及配置文件代码如下 -
测试代码如下
package at.guigu.service;import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestOne {@Testpublic void test1() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
运行结果如下
-
按构造方法自动装配
-
注意
- 按构造方法自动装配时必须有有参构造器的存在,且只能通过有参构造器进行自动装配
- Spring 的IOC容器会根据构造函数的参数类型来自动装配相应的 bean。
- 确保构造函数参数的类型在容器中有且只有一个匹配的 bean。如果有多个匹配的 bean,Spring 会抛出
NoUniquebeanDefinitionException
异常。
-
构造器参数对象对应IOC容器中的bean唯一
-
Dao/Mapper
数据访问层(持久层)、Service
业务层的实现类及配置文件代码如下 -
测试代码如下
package at.guigu.service;import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestOne {@Testpublic void test1() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
运行结果如下
-
-
构造器参数对象对应IOC容器中的bean不唯一
-
Dao/Mapper
数据访问层(持久层)、Service
业务层的实现类及配置文件代码如下
-
测试代码如下
package at.guigu.service;import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestOne {@Testpublic void test1() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
运行结果如下
-
-
构造器参数类型对应IOC容器中的bean不唯一的解决方式
- 可以使用
autowire-candidate=falser
将对应的bean设置成不可作为其它bean自动装配的候选者 - 可以使用按名称自动装配
- 可以使用
bean的依赖注入的数据类型
-
在之前的学习中都是注入的引用bean、除了对象的引用可以注入,普通数据类型的注入以及集合等都可以在容器中进行注入
-
注入数据的三种数据类型
- 普通数据类型(即基本数据类型与字符串类型)
- 引用数据类型(此处不在举例,之前均为对象的引用注入,可详见依赖注入的三种方式的示例)
- 集合数据类型
普通数据类型注入
-
setter
方法进行注入 ——其它两种绑定依赖注入的方式可自行练习-
Step1: 重新编写
Dao/Mapper
数据访问层(持久层)接口及其实现类,并在Dao/Mapper
数据访问层(持久层)的UserDaoImpl
实现类中添加普通数据类型的setter方法,如图所示 -
Step2: 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"><!--配置bean--><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"><property name="name" value="zhangsan"/><property name="age" value="18"/><!--等同于<property name="name"><value>zhangsan</value></property>--></bean></beans>
-
Step3: 创建测试用例
TestSeven
,代码如下package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import at.guigu.service.impl.BookServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestSeven {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDaoImpl = (UserDaoImpl) app.getBean("userDaoImpl");userDaoImpl.save();} }
-
数组类型注入
-
setter
方法进行注入 ——其它两种绑定依赖注入的方式可自行练习-
Step1: 重新编写
Dao/Mapper
数据访问层(持久层)接口及其实现类,并在Dao/Mapper
数据访问层(持久层)的UserDaoImpl
实现类中添加数组数据类型的setter方法,如图所示 -
Step2: 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"><bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImpl"><property name="array"><array><value>100</value><value>200</value><value>300</value></array></property></bean> </beans>
-
Step3:测试代码如下
package at.guigu.service;import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestOne {@Testpublic void test1() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl");bookService.save();} }
-
集合数据类型注入
-
setter
方法进行注入 ——其它两种绑定依赖注入的方式可自行练习-
Step1: 重新编写
Dao/Mapper
数据访问层(持久层)接口及其实现类,并在Dao/Mapper
数据访问层(持久层)的UserDaoImpl
实现类中添加集合数据类型的setter方法,如图所示- 注意:Map集合传入了User对象,所以此处需要一个User实体类
-
Step2: 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"><!--配置bean--><bean id="user1" class="at.guigu.pojo.User"><property name="name" value="Tom"/><property name="addr" value="美国"/></bean><bean id="user2" class="at.guigu.pojo.User"><property name="name" value="Jerry"/><property name="addr" value="英国"/></bean><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"><property name="strList"><list><value>aaa</value><value>bbb</value><value>ccc</value></list></property><property name="userMap"><map><entry key="userr1" value-ref="user1"></entry><entry key="userr2" value-ref="user2"></entry></map></property><property name="properties"><props><prop key="p1">ppp1</prop><prop key="p2">ppp2</prop></props></property></bean></beans>
-
Step4: 创建测试用例
TestEight
,代码如下package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestEight {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDaoImpl = (UserDaoImpl) app.getBean("userDaoImpl");userDaoImpl.save();} }
-
-
P命名空间注入步骤
-
有参构造方法注入步骤
Spring配置文件中引入其它配置文件
引入Spring拆分配置文件applicationContext-xxx.xml
-
在实际开发中,Spring的配置内容很多,这会导致Spring配置很繁琐且体积偏大。所以我们可以将部分配置拆解到其它配置文件中,然后在Spring主配置文件中通过
<import>
标签进行加载 -
拆解方式
- 可根据三层架构进行拆解:比如拆解为
Service
、Dao/Mapper
、Web
- 也可根据不同类型进行拆解:比如拆解为用户和商品
- 可根据三层架构进行拆解:比如拆解为
-
引入其它配置文件的代码格式:
<import resource="applicationContext-xxx.xml"/>
-
示例:此处以集合数据类型注入 中的配置文件为例
-
原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"><!--配置bean--><bean id="user1" class="at.guigu.pojo.User"><property name="name" value="Tom"/><property name="addr" value="美国"/></bean><bean id="user2" class="at.guigu.pojo.User"><property name="name" value="Jerry"/><property name="addr" value="英国"/></bean><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"><property name="strList"><list><value>aaa</value><value>bbb</value><value>ccc</value></list></property><property name="userMap"><map><entry key="userr1" value-ref="user1"></entry><entry key="userr2" value-ref="user2"></entry></map></property><property name="properties"><props><prop key="p1">ppp1</prop><prop key="p2">ppp2</prop></props></property></bean></beans>
-
将User拆解出来以后,分配置文件名为
applicationContext-user.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"><!--配置bean--><bean id="user1" class="at.guigu.pojo.User"><property name="name" value="Tom"/><property name="addr" value="美国"/></bean><bean id="user2" class="at.guigu.pojo.User"><property name="name" value="Jerry"/><property name="addr" value="英国"/></bean></beans>
-
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"><!--引入拆解后的分配置文件--><import resource="applicationContext-user.xml"/><!--配置bean--><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"><property name="strList"><list><value>aaa</value><value>bbb</value><value>ccc</value></list></property><property name="userMap"><map><entry key="userr1" value-ref="user1"></entry><entry key="userr2" value-ref="user2"></entry></map></property><property name="properties"><props><prop key="p1">ppp1</prop><prop key="p2">ppp2</prop></props></property></bean></beans>
-
引入properties配置文件
- 详见Spring通过配置文件配置数据源(连接池)
引入多个配置文件(以properties配置文件为例)
-
加载一个properties配置文件:
<context:property-placeholder location="jdbc1.properties"/>
-
加载多个properties配置文件(只能读取当前工程目录下的配置文件),如图所示
- 方式一:
<context:property-placeholder location="jdbc1.properties, jdbc2.properties, ..."/>
- 方式二:
<context:property-placeholder location="*.properties"/>
- 方式三(标准格式):
<context:property-placeholder location="classpath:*.properties"/>
- 方式一:
-
加载多个properties配置文件(读取所有的配置文件):
<context:property-placeholder location="classpath*:*.properties"/>
-
注意:加载配置文件时可根据需要选择使用
classpath:
还是classpath*:
Spring相应API
ApplicationContext
的实现类
ApplicationContext 接口的实现类 | 使用情形 |
---|---|
ClassPathXmlApplicationContext | 从类的根路径下加载配置文件(即) |
FileSystemXmlApplicationContext | 从磁盘路径上加载配置文件 |
AnnotationConfigApplicationContext | 当使用注解配置容器对象时,为了能够读取注解,就需要使用该类来创建Spring容器。 |
-
前两种使用方式如下,第三种实现类后续会详细讲解
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext;public class TestEight {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");// 等同于FileSystemXmlApplicationContext app2 = new FileSystemXmlApplicationContext("F:\\node\\idea\\ssm\\SpringDemo\\src\\main\\resources\\applicationContext.xml");} }
getBean()
方法的使用
getBean() 方法 | 解释 |
---|---|
public Object getBean(String name) throws beansException | 从 Spring 容器中获取 bean 实例,传入的参数为Spring配置文件中对应bean的id (即唯一标识) |
public <T> T getBean(Class<T> requiredType) throws beansException | 从 Spring 容器中获取 bean 实例,传入的参数为对应bean的类类对象即bean.Class |
- 第一种getBean方法需要类型强制转换,而第二种不需要,如下所示
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;public class TestEight {public static void main(String[] args) {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");// 等同于FileSystemXmlApplicationContext app2 = new FileSystemXmlApplicationContext("F:\\node\\idea\\ssm\\SpringDemo\\src\\main\\resources\\applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDaoImpl = (UserDaoImpl) app.getBean("userDaoImpl");// 等同于UserDaoImpl userDaoImpl2 = app.getBean(UserDaoImpl.class);}
}
-
若对应的bean在Spring配置文件中为单例(即只有一个)时,则以上两种
getBean
方法都可使用(建议使用第二种);若在Spring配置文件中有多个时,则使用第一个getBean
方法-
最好不同情况用不同的
getBean
方法,这样更能区分开 -
对应的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"><!--配置bean--><bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"/><!--等同于<bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"></bean>--> </beans>
-
对应的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"><!--配置bean--><bean id="userDaoImpl1" class="at.guigu.dao.impl.UserDaoImpl"/><bean id="userDaoImpl2" class="at.guigu.dao.impl.UserDaoImpl"></bean></beans>
-
-
注意:使用第一种方法来获取对应bean的对象时,若无法获取到将会抛出
NoSuchbeanDefinitionException
异常,即NoSuchbeanDefinitionException: No bean named 'userDaoImpll' available
-
抛出该异常的原因:
getBean
方法参数与id
(即唯一标识)或name
(即别名)不一致 -
代码示例
package at.guigu.dao;import at.guigu.dao.impl.UserDaoImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestTwo {/*** 测试NoSuchbeanDefinitionException异常*/@Testpublic void Test3() {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象UserDaoImpl userDao1 = (UserDaoImpl) app.getBean("userDaoImpll");System.out.println(userDao1);} }
-
数据源(连接池)
-
定义
- 数据源(
DataSource
)是一个抽象的概念,主要用于提供对数据库的连接(可详见JDBC部分内容)
- 数据源(
-
作用
- 提高程序性能
- 事先实例化数据源,初始化部分连接资源
- 使用连接资源时可从数据源中获取,使用完毕后将连接资源归还给数据源
-
常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等
数据源(连接池)配置步骤
-
数据源(连接池)的配置步骤
- 在pom.xml文件中导入数据源(连接池)的坐标和数据库驱动坐标
- 创建数据源对象并设置数据源的基本连接数据
- 使用数据源获取连接资源和归还资源
-
完整代码示例
-
Step1: 在pom.xml文件中导入数据源(连接池)的坐标和数据库mysql驱动坐标,代码如下
- 博主测试C3P0、Druid两个数据库连接池,所以它俩的坐标均会导入
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>SpringDemoo</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><name>SpringDemoo Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--mysql坐标--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!--druid坐标--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.18</version></dependency><!--c3p0坐标--><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.5</version></dependency><!--spring坐标--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.6</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency></dependencies><build><finalName>SpringDemoo</finalName><plugins><!-- Tomcat插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins></build> </project>
-
Step2: 创建数据源对象并设置数据源的基本连接数据。然后使用数据源获取连接资源和归还资源
package at.guigu.datasource;import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test;import java.sql.Connection;public class TestOne {/*** 测试CP30数据源对象*/@Testpublic void test1() throws Exception {// 创建数据源对象ComboPooledDataSource dataSource = new ComboPooledDataSource();// 设置数据源基本连接数据dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");dataSource.setUser("root");dataSource.setPassword("123456");// 使用数据源获取数据库连接资源Connection connection = dataSource.getConnection();System.out.println(connection);// 使用数据库资源,代码省略//归还数据库连接资源connection.close();}/*** 测试Druid数据源对象*/@Testpublic void test2() throws Exception {// 创建数据源对象DruidDataSource dataSource = new DruidDataSource();// 设置数据源基本连接数据dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis");dataSource.setUsername("root");dataSource.setPassword("123456");// 使用数据源获取数据库连接资源Connection connection = dataSource.getConnection();System.out.println(connection);// 使用数据库资源,代码省略//归还数据库连接资源connection.close();} }
-
通过加载配置文件配置数据源(连接池)
-
若使用 数据源开发步骤 中的代码来获取数据库连接资源的话则会造成高耦合问题,为了降低耦合度我们也利用properties配置文件进行数据源(连接池)的配置
-
步骤如下
-
Step1: 在pom.xml文件中导入数据源(连接池)的坐标和数据库mysql驱动坐标,代码省略
- 博主测试C3P0、Druid两个数据库连接池,所以它俩的坐标均会导入
-
Step2: 右键源代码配置文件目录(即资源文件
resources
)→New
→File
,创建properties配置文件,博主文件名为jdbc.properties
,该配置文件代码如下- properties配置文件中配置的各个属性前最好添加个
id.
(即id.属性
,比如:属性url
就设置为id.url
,博主设置的为jdbc.url
),防止系统中有与properties文件中的属性名一致的情况
#driverClassName代表数据库驱动,后跟驱动全类名(在MySQL驱动jar包下的META-INF下的services文件夹下的java.sql.Driver文件内) jdbc.driverClassName=com.mysql.cj.jdbc.Driver # 数据库连接URL jdbc.url=jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai # 数据库用户名 jdbc.username=root # 数据库密码 jdbc.password=123456 # 初始化连接数量---即容器中初始的数据库连接数量 jdbc.initialSize=5 # 最大活跃连接数量---容器中初始为5个,但若5个用完了,此时可以在申请5个数据库连接数量 #也就是说容器中最多存放10个数据库连接 jdbc.maxActive=10 # 获取连接时的最大等待时间,单位:毫秒。---与数据库进行连接时若超过3s仍未连接成功,则会报错 jdbc.maxWait=3000 #最小空闲连接数量---minIdle=5 # 配置检测连接是否有效的SQL,可以是一个查询语句,如果不指定则默认为"SELECT 1"---validationQuery=SELECT 1 # 是否开启自动提交事务---defaultAutoCommit=true
- properties配置文件中配置的各个属性前最好添加个
-
Step3: 读取properties配置文件并创建数据源对象,设置数据源的基本连接数据。然后使用数据源获取连接资源和归还资源
package at.guigu.datasource;import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test;import java.sql.Connection; import java.util.ResourceBundle;public class TestTwo {/*** 测试CP30数据源对象 (通过加载properties配置文件形式)*/@Testpublic void test1() throws Exception {// 读取配置文件ResourceBundle rb = ResourceBundle.getBundle("jdbc");String driverClassName = rb.getString("jdbc.driverClassName");String url = rb.getString("jdbc.url");String username = rb.getString("jdbc.username");String password = rb.getString("jdbc.password");// 创建数据源对象ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setDriverClass(driverClassName);dataSource.setJdbcUrl(url);dataSource.setUser(username);dataSource.setPassword(password);// 使用数据源获取数据库连接资源Connection connection = dataSource.getConnection();System.out.println(connection);// 使用数据库资源,代码省略//归还数据库连接资源connection.close();}/*** 测试Druid数据源对象 (通过加载properties配置文件形式)*/@Testpublic void test2() throws Exception {// 读取配置文件ResourceBundle rb = ResourceBundle.getBundle("jdbc");String driverClassName = rb.getString("jdbc.driverClassName");String url = rb.getString("jdbc.url");String username = rb.getString("jdbc.username");String password = rb.getString("jdbc.password");// 创建数据源对象DruidDataSource dataSource = new DruidDataSource();// 设置数据源基本连接数据dataSource.setDriverClassName(driverClassName);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);// 使用数据源获取数据库连接资源Connection connection = dataSource.getConnection();System.out.println(connection);// 使用数据库资源,代码省略//归还数据库连接资源connection.close();} }
-
-
ResourceBundle
类是专门用来通过键值对的形式存储和检索本地化的资源(如消息、标签等)。它的主要作用是根据不同的语言和区域设置加载相应的资源文件,从而实现应用程序的多语言支持。- 它所能加载的资源文件只有两种:即以
.class
或.properties
结尾的文件 - 利用
ResourceBundle
类中的静态方法getBundle()
加载配置文件时,它会自动在源代码配置文件目录(即资源文件resources
)下查找所需的配置文件- 注意:使用该方法时,其参数只需给出配置文件的基名即可,它会自动匹配对应的后缀
- 它所能加载的资源文件只有两种:即以
Spring配置数据源(连接池)
-
以上两种数据源配置方法耦合度还是很高,所以引入了Spring配置数据源(连接池)
-
步骤如下
-
Step1: 在pom.xml文件中导入Spring、数据源(连接池)的坐标和数据库mysql驱动坐标。pom.xml文件完整代码如下
- 博主测试C3P0、Druid两个数据库连接池,所以它俩的坐标均会导入
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>SpringDemoo</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><name>SpringDemoo Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--mysql坐标--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!--druid坐标--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.18</version></dependency><!--c3p0坐标--><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.5</version></dependency><!--spring坐标--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.6</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency></dependencies><build><finalName>SpringDemoo</finalName><plugins><!-- Tomcat插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins></build> </project>
-
Step2: 右键源代码配置文件目录(即资源文件
resources
)→New
→XML Configuration File
→Spring Config
,文件名为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>
-
Step3: 配置数据源(连接池)的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"><!--Cp30对应的bean--><bean id="dataSourceCp30" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.cj.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/><property name="user" value="root"/><property name="password" value="123456"/></bean><!--Druid对应的bean--><bean id="dataSourceDruid" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis"/><property name="username" value="root"/><property name="password" value="123456"/></bean> </beans>
-
Step4: 测试代码如下
package at.guigu.datasource;import at.guigu.service.impl.BookServiceImpl; import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;import javax.sql.DataSource; import java.sql.Connection;public class TestThree {/*** 测试CP30数据源对象(通过Spring容器的方式)*/@Testpublic void test1() throws Exception {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象(即数据源对象)DataSource dataSourceCp30 = (DataSource) app.getBean("dataSourceCp30");//等同于ComboPooledDataSource bean = app.getBean(ComboPooledDataSource.class);// 使用数据源获取数据库连接资源Connection connection = dataSourceCp30.getConnection();System.out.println(connection);// 使用数据库资源,代码省略//归还数据库连接资源connection.close();}/*** 测试Druid数据源对象(通过Spring容器的方式)*/@Testpublic void test2() throws Exception {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象(即数据源对象)DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid");//等同于ComboPooledDataSource bean = app.getBean(DruidDataSource.class);// 使用数据源获取数据库连接资源Connection connection = dataSourceDruid.getConnection();System.out.println(connection);// 使用数据库资源,代码省略//归还数据库连接资源connection.close();} }
-
Spring通过配置文件配置数据源(连接池)
-
利用以上三种方式配置数据源(连接池)时耦合度仍然较高,所以就有了最优方案
-
步骤如下所示
-
Step1: 在pom.xml文件中导入Spring、数据源(连接池)的坐标和数据库mysql驱动坐标。pom.xml文件完整代码如下
- 博主测试C3P0、Druid两个数据库连接池,所以它俩的坐标均会导入
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>SpringDemoo</artifactId><packaging>war</packaging><version>1.0-SNAPSHOT</version><name>SpringDemoo Maven Webapp</name><url>http://maven.apache.org</url><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--mysql坐标--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!--druid坐标--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.18</version></dependency><!--c3p0坐标--><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.5</version></dependency><!--spring坐标--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.6</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency></dependencies><build><finalName>SpringDemoo</finalName><plugins><!-- Tomcat插件 --><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version></plugin></plugins></build> </project>
-
Step2: 右键源代码配置文件目录(即资源文件
resources
)→New
→File
,创建properties配置文件,博主文件名为jdbc.properties
,该配置文件代码如下- 注意: properties配置文件中配置的各个属性前必须添加个
id.
(即id.属性
,比如:属性url
就设置为id.url
,博主设置的为jdbc.url
)的原因- 配置数据库连接池的规定变量名与系统变量名冲突,若不加
id.
时:Spring配置文件利用属性占位符${}
调用来的就会默认为系统变量的值
- 配置数据库连接池的规定变量名与系统变量名冲突,若不加
- 注意: properties配置文件中配置的各个属性前必须添加个
#driverClassName代表数据库驱动,后跟驱动全类名(在MySQL驱动jar包下的META-INF下的services文件夹下的java.sql.Driver文件内) jdbc.driverClassName=com.mysql.cj.jdbc.Driver # 数据库连接URL jdbc.url=jdbc:mysql://localhost:3306/test02?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai # 数据库用户名 jdbc.username=root # 数据库密码 jdbc.password=123456 # 初始化连接数量---即容器中初始的数据库连接数量 jdbc.initialSize=5 # 最大活跃连接数量---容器中初始为5个,但若5个用完了,此时可以在申请5个数据库连接数量 #也就是说容器中最多存放10个数据库连接 jdbc.maxActive=10 # 获取连接时的最大等待时间,单位:毫秒。---与数据库进行连接时若超过3s仍未连接成功,则会报错 jdbc.maxWait=3000 #最小空闲连接数量---minIdle=5 # 配置检测连接是否有效的SQL,可以是一个查询语句,如果不指定则默认为"SELECT 1"---validationQuery=SELECT 1 # 是否开启自动提交事务---defaultAutoCommit=true
-
Step3: 右键源代码配置文件目录(即资源文件
resources
)→New
→XML Configuration File
→Spring Config
,文件名为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>
-
Step4: 在Spring配置文件中引入
context
命名空间和context
约束路径然后使用context
命名空间加载 properties 文件(加载 properties 文件成功后,Spring配置文件就可以使用属性占位符${}
语法来引用这些属性了)。Spring配置文件代码如下- 命名空间:
xmlns:context="http://www.springframework.org/schema/context"
- 约束路径:
http://www.springframework.org/schema/context
、http://www.springframework.org/schema/context/spring-context.xsd
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--使用`context`命名空间加载 `properties` 文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--若properties配置文件中没有加`id.`,则需变为如下形式使其系统属性失效<context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="NEVER"/>--></beans>
- 命名空间:
-
Step5: 引入properties配置文件后配置数据源(连接池)的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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--使用`context`命名空间加载 `properties` 文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--Cp30对应的bean--><bean id="dataSourceCp30" class="com.mchange.v2.c3p0.ComboPooledDataSource"><!--使用属性占位符`${}`语法引用properties文件中的属性--><property name="driverClass" value="${jdbc.driverClassName}"/><property name="jdbcUrl" value="${jdbc.url}"/><property name="user" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><!--Druid对应的bean--><bean id="dataSourceDruid" class="com.alibaba.druid.pool.DruidDataSource"><!--使用属性占位符`${}`语法引用properties文件中的属性--><property name="driverClassName" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean></beans>
-
Step6: 测试代码如下
package at.guigu.datasource;import at.guigu.service.impl.BookServiceImpl; import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;import javax.sql.DataSource; import java.sql.Connection;public class TestThree {/*** 测试CP30数据源对象(通过Spring容器的方式)*/@Testpublic void test1() throws Exception {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象(即数据源对象)DataSource dataSourceCp30 = (DataSource) app.getBean("dataSourceCp30");//等同于ComboPooledDataSource bean = app.getBean(ComboPooledDataSource.class);// 使用数据源获取数据库连接资源Connection connection = dataSourceCp30.getConnection();System.out.println(connection);// 使用数据库资源,代码省略//归还数据库连接资源connection.close();}/*** 测试Druid数据源对象(通过Spring容器的方式)*/@Testpublic void test2() throws Exception {//1 获取IOC容器ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");//2 从IOC容器中获取bean对应的对象(即数据源对象)DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid");//等同于ComboPooledDataSource bean = app.getBean(DruidDataSource.class);// 使用数据源获取数据库连接资源Connection connection = dataSourceDruid.getConnection();System.out.println(connection);// 使用数据库资源,代码省略//归还数据库连接资源connection.close();} }
-
-