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数据访问层(持久层)的对象是通过newDao/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,代码如下所示该标签用到的属性 解释 idSpring容器中的 bean的唯一标识符,可用来引用特定的bean实例classbean的实现类的全限定名<?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核心容器管理的对象)
-
默认情况下它调用的是类中的无参构造函数 ,若没有无参构造函数则会创建失败
-
基本属性
该标签用到的属性 解释 idSpring容器中的 bean的个唯一的标识符,可用来引用特定的bean实例name定义bean的别名,定义多个别名时可用 ,;或空格分隔。classbean的实现类的全限定名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 自动装配的候选者,默认为 truelazy-init控制bean的延迟加载,默认为 false(非延迟)scope属性取值解释 singleton默认值,单例。即在IOC容器中对应bean的对象只有一个 prototype多例。即在IOC容器中对应bean的对象有多个,每次在使用 app.getBean()方法时都会获得对应bean的新对象requestWeb项目中,Spring创建一个bean的对象,将对象存入到 request域中sessionWeb项目中,Spring创建一个bean的对象,将对象存入到 session域中global sessionWeb项目中,应用在Portlet环境,若没有Portlet环境则 global session相当于sessionautowire属性取值解释 no默认值,默认不使用自动装配 byType通过属性的类型进行自动装配 byName通过属性名称进行自动装配 constructor通过构造函数进行自动装配
name属性——别名配置
-
创建三层架构的包结构,并编写
Dao/Mapper数据访问层(持久层)接口及其实现类。如图所示- Step1: 实现类要写对应架构包的
impl包下

- Step1: 实现类要写对应架构包的
-
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属性 */ @Test public 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"); } }
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" */ @Test public 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 */ @Override public void afterPropertiesSet() throws Exception { System.out.println("Dao init..."); } /** * 销毁方法 * @throws Exception */ @Override public void destroy() throws Exception { System.out.println("Dao destroy..."); } public void save() { System.out.println("UserDao save running..."); } } - 该实现类除了要实现
-
Step2:
Service业务层接口及实现类代码如下-
接口
BookServicepackage 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 */ @Override public void afterPropertiesSet() throws Exception { System.out.println("Service init..."); } /** * 销毁方法 * @throws Exception */ @Override public void destroy() throws Exception { System.out.println("Service destory..."); } @Override public 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创建的对象的类型。如果在创建对象之前类型未知,则返回nullboolean 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 */ @Override public UserDaoImpl getObject() throws Exception { return new UserDaoImpl(); } @Override public 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 */ @Override public UserDaoImpl getObject() throws Exception { return new UserDaoImpl(); } @Override public Class<?> getObjectType() { return UserDaoImpl.class; } /** * 设置bean的作用范围 * @return */ @Override public 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.Stringindex指定构造函数的参数索引位置(以0为第一个参数索引位置) refIOC容器中可获取到依赖对象的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对象给到我们定义的bookDaopackage at.guigu.service.impl; import at.guigu.dao.UserDao; import at.guigu.service.BookService; public class BookServiceImpl implements BookService { private UserDao userDao; @Override public 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=userDaoref属性值: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; } @Override public 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; } @Override public 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 { @Test public 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 { @Test public 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 { @Test public 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 { @Test public 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 { @Test public 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 { @Test public 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 { @Test public 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(); } }
-
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异常 */ @Test public 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数据源对象 */ @Test public 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数据源对象 */ @Test public 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配置文件形式) */ @Test public 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配置文件形式) */ @Test public 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容器的方式) */ @Test public 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容器的方式) */ @Test public 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配置文件利用属性占位符${}调用来的就会默认为系统变量的值
- 配置数据库连接池的规定变量名与系统变量名冲突,若不加
#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: 右键源代码配置文件目录(即资源文件
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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--使用`context`命名空间加载 `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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--使用`context`命名空间加载 `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容器的方式) */ @Test public 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容器的方式) */ @Test public 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注解开发
- Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,它能够代替xml配置文件,可以简化配置,提高开发效率
- Spring注解根据出现时间分类
- Spring原始注解:主要代替
<bean>的配置 - Spring新注解:Spring配置文件中有些配置是Spring原始注解无法替代的,此时就需要新注解,比如:
- 非自定义的Bean的配置,比如Druid、Cp30的bean
- 加载properties配置文件的配置:
<context:property-placeholder location="classpath:jdbc.properties"/> - 组件扫描的配置:
<context:component-scan base-package="at.guigu"></context:component-scan> - 引入其它配置文件的配置:
<import resource="applicationContext-xxx.xml"/>
- Spring原始注解:主要代替
- 注意
- 使用注解开发时,需要在Spring配置文件中配置组件扫描
- 作用:指定哪个包及其子包下的bean需要进行扫描,以便可以识别使用注解配置的类、字段和方法
- 使用注解开发时,需要在Spring配置文件中配置组件扫描
Spring原始注解快速入门
-
不使用注解开发时的代码截图如下(以
setter方法注入为例)
-
使用注解开发的步骤
-
Step1: 在pom.xml文件中导入相应的坐标及插件:Spring坐标、Annotation坐标、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> <parent> <groupId>org.example</groupId> <artifactId>SpringDemo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SpringAnno</artifactId> <packaging>war</packaging> <name>SpringAnno 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> <!--Annotation坐标--> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> </dependencies> <build> <finalName>SpringAnno</finalName> <plugins> <!-- Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build> </project> -
Step2: 给对应的类添加注解
-
UserDaoImpl类代码如下package at.guigu.dao.impl; import at.guigu.dao.UserDao; import org.springframework.stereotype.Repository; // <bean id="userDaoImpl" class="at.guigu.dao.impl.UserDaoImpl"></bean> // 等同于@Component("userDaoImpl") @Repository("userDaoImpl") public class UserDaoImpl implements UserDao { public void save() { System.out.println("UserDao save running..."); } } -
BookServiceImpl类代码如下package at.guigu.service.impl; import at.guigu.dao.UserDao; import at.guigu.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; //<bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImpl"></bean> // 等同于@Component("bookServiceImpl") @Service("bookServiceImpl") public class BookServiceImpl implements BookService { // <property name="userDao" ref="userDaoImpl"/> @Autowired @Qualifier("userDaoImpl") private UserDao userDao; /*public void setUserDao(UserDao userDao) { this.userDao = userDao; }*/ @Override public void save() { System.out.println("BookService save..."); userDao.save(); } }
-
-
Step3: 在Spring配置文件中配置注解的组件扫描,Spring配置文件代码如下
- 配置组件扫描需要在Spring配置文件中引入
context命名空间和context约束路径然后使用context命名空间配置注解的组件扫描即可。- 命名空间:
xmlns:context="http://www.springframework.org/schema/context" - 约束路径:
http://www.springframework.org/schema/context、http://www.springframework.org/schema/context/spring-context.xsd
- 命名空间:
- 配置组件扫描代码:
<context:component-scan base-package="at.guigu"></context:component-scan>base-package:给定一个包,然后会自动扫描该包下的所有内容,以便可以识别使用注解配置的类、字段和方法
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置注解的组件扫描--> <context:component-scan base-package="at.guigu"></context:component-scan> </beans> - 配置组件扫描需要在Spring配置文件中引入
-
Step4: 测试类TestOne代码如下
package at.guigu.web; import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestOne { @Test public void test1() { //1 获取IOC容器 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //2 从IOC容器中获取bean对应的对象 BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl"); bookService.save(); } }
-
-
使用注解开发的与使用配置文件进行依赖注入的对比图例如下

Spring原始注解
| 原始注解 | 解释 |
|---|---|
@Component | 使用在类上,用于实例化bean |
@Controller | 使用在Web层的类上,用于实例化bean |
@Service | 使用在Service层的类上,用于实例化bean |
@Repository | 使用在Dao/Mapper层的类上,用于实例化bean |
@Autowired | 使用在字段上,用于根据类型进行自动依赖注入 |
@Qualifier("beanId") | 必须与@Autowired一起使用,用于根据名称进行依赖注入,从而避免按类型自动装配时的歧义 |
@Resource(name = "beanId") | 相当于@Autowired+@Resource。它既可以按名称注入,也可以按类型注入。它的默认行为是按名称注入,如果找不到匹配的名称,再按类型注入。 |
@Value | 注入普通属性 |
@Scope | 使用在类上,标注bean的作用范围。默认为@Scope("singleton"):单例; @Scope("prototype"):多例 |
@PostConstruct | 使用在方法上,标注该方法是bean的初始化方法 |
@PreDestroy | 使用在方法上,标注该方法是bean的销毁方法 |
-
注意
-
@Repository、@Service、@Controller以上三个注解能用@Component来代替- 因为这三个注解本质上都是
@Component注解的扩展
- 因为这三个注解本质上都是
-
使用
@Autowired、@Qualifier("beanId")、@Resource("beanId")这三个注解进行依赖注入时,可以省略不写setter方法 -
@Value用于替代value,即只能用于基本数据类型与字符串类型 -
@Scope不在进行测试,可自行测试
-
-
@Autowired、@Qualifier注解详解@Autowired是按照数据类型从Spring容器中进行自动匹配的。它会自动匹配对应引用类型的实现类的bean@Qualifier注解必须与@Autowired一起配合使用。它是按照对应bean的id值从Spring容器中进行匹配的- 若只使用
@Autowired,而不与@Qualifier配合使用时:使用@Autowired注解的引用数据类型对应的bean在Spring容器中只能有一个;反之则必须与@Qualifier联合使用
-
@Resource注解详解@Resource相当于@Autowired+@Resource。@Resource(name = "beanId"):如果beanId属性被指定,首先按名称查找;如果没有指定beanId,则按字段或属性名进行查找;如果没有找到匹配的名称,再按类型查找。@Resource注解在代码中必须写为@Resource(name = "beanId")的形式,若不加上name=,则会报错
-
只使用
@Autowired的示例-
使用
@Autowired注解的属性只存在一个对应实现类的bean
-
使用
@Autowired注解的属性存在多个对应实现类的bean- 错误示例

- 正确示例:此时必须与
@Qualifier联合使用
@Autowired、@Qualifier("beanId")联合使用
@Resource代替@Autowired+@Resource
-
@Value注入普通属性
-
简单形式的代码示例图如下

-
与properties配置文件结合给属性赋值的步骤
-
Step1: 在pom.xml文件中导入Spring、Annotation、数据源(连接池)的坐标和数据库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> <parent> <groupId>org.example</groupId> <artifactId>SpringDemo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SpringAnno</artifactId> <packaging>war</packaging> <name>SpringAnno 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> <!--Annotation坐标--> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>SpringAnno</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),以供Spring配置文件可以使用属性占位符${}语法引用这些属性
#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: 右键源代码配置文件目录(即资源文件
resources)→New→XML Configuration File→Spring Config,文件名为applicationContext.xml,然后在该Spring配置文件中引入context命名空间、context约束路径、配置注解的组件扫描,并引入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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置注解的组件扫描--> <context:component-scan base-package="at.guigu"></context:component-scan> <!--使用`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> -
Step4:
BookServiceImpl类属性配置代码如下package at.guigu.service.impl; import at.guigu.dao.UserDao; import at.guigu.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; //<bean id="bookServiceImpl" class="at.guigu.service.impl.BookServiceImpl"></bean> @Service("bookServiceImpl") public class BookServiceImpl implements BookService { // <property name="userDao" ref="userDaoImpl"/> @Resource(name = "userDaoImpl") private UserDao userDao; @Value("${jdbc.driverClassName}") private String driverClass; @Value("${jdbc.username}") private String name; @Value("15") private int age; @Override public void save() { System.out.println(driverClass); System.out.println(name + "===" + age); userDao.save(); } } -
Step5: 测试代码如下
package at.guigu.web; import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestOne { @Test public void test1() { //1 获取IOC容器 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //2 从IOC容器中获取bean对应的对象 BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl"); bookService.save(); } }
-
@PostConstruct、@PreDestroy:初始、销毁方法
-
繁琐配置步骤均省略,可详见之前的步骤,此处只说明重点步骤
-
BookServiceImpl类代码如下
package at.guigu.service.impl; import at.guigu.dao.UserDao; import at.guigu.service.BookService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; //<bean id="bookServiceImpl" scope = "singleton" class="at.guigu.service.impl.BookServiceImpl"></bean> @Service("bookServiceImpl") @Scope("singleton") public class BookServiceImpl implements BookService { // <property name="userDao" ref="userDaoImpl"/> @Resource(name = "userDaoImpl") private UserDao userDao; @PostConstruct public void init() { System.out.println("Service Init..."); } @PreDestroy public void destroy() { System.out.println("Service Destroy..."); } @Override public void save() { System.out.println("BookService running..."); userDao.save(); } } -
测试代码如下
package at.guigu.web; import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestOne { @Test public void test2() { //1 获取IOC容器 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //2 从IOC容器中获取bean对应的对象 BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl"); bookService.save(); } }
-
-
注意:在上述测试代码运行截图中并未运行销毁方法,原因及解决方式请详见本章中的Spring配置文件标签详解一中的初始销毁知识点
Spring新注解
-
作用:配合Spring原始注解完全代替Spring配置文件
-
可替代的配置如下
- 非自定义的Bean的配置,比如Druid、Cp30的bean
- 加载properties配置文件的配置:
<context:property-placeholder location="classpath:jdbc.properties"/> - 组件扫描的配置:
<context:component-scan base-package="at.guigu"></context:component-scan> - 引入其它配置文件的配置:
<import resource="applicationContext-xxx.xml"/>
| 新注解 | 解释 |
|---|---|
@Configuration | 使用在类上,用于指定当前类是Spring的核心配置类。当创建容器时会从该类上加载注解 |
@ComponentScan | 用于指定Spring在初始化容器时要扫描的包。即相当于组件扫描的配置。若扫描一个包,则为:@ComponentScan("包1");若扫描多个包,则为:@ComponentScan({"包1", "包2", ...}) |
@Bean | 使用在方法上,标注将该方法的返回值存储到容器中 |
@PropertySource | 用于加载properties文件的配置 |
@Import | 用于导入其它文件的配置类。若只导入一个文件配置类,则为@Import(Aaa.class);若导入多个,则为@Import({Aaa.class, Bbb.class}) |
Spring新注解代码实现
-
Spring新注解快速入门的
UserDaoImpl类、BookServiceImpl类代码省略,详见Spring原始注解快速入门 -
在Spring原始注解中,利用
@Value()注解注入普通属性时有一个与properties配置文件结合给属性赋值的步骤-
该步骤的Step3中的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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置注解的组件扫描--> <context:component-scan base-package="at.guigu"></context:component-scan> <!--使用`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>
-
-
利用新注解进行配置的步骤如下
-
Step1: 在pom.xml文件中导入Spring、Annotation、数据源(连接池)的坐标和数据库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> <parent> <groupId>org.example</groupId> <artifactId>SpringDemo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SpringAnno</artifactId> <packaging>war</packaging> <name>SpringAnno 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> <!--Annotation坐标--> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>SpringAnno</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),以供Spring配置文件可以使用属性占位符${}语法引用这些属性
#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配置文件中配置的各个属性前必须添加个
-
Step4: 在三层架构包下创建一个
config包,并在该包下创建一个**代替Spring配置文件的类**SpringConfiguration,完整代码如下package at.guigu.config; import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import javax.sql.DataSource; // 该注解代表该类是Spring的核心配置类 @Configuration // 配置注解的组件扫描<context:component-scan base-package="at.guigu"></context:component-scan> @ComponentScan("at.guigu") // 加载properties配置文件<context:property-placeholder location="classpath:jdbc.properties"/> @PropertySource("classpath:jdbc.properties") public class SpringConfiguration { @Value("${jdbc.driverClassName}") private String driverClassName; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * Cp30对应的bean * Spring会将当前方法的返回值以指定的id存储到Spring的IOC容器中 * @return * @throws Exception */ @Bean("dataSourceCp30") public DataSource getCp30DataSource() throws Exception{ // 创建数据源对象 ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 设置数据源基本连接数据 dataSource.setDriverClass(driverClassName); dataSource.setJdbcUrl(url); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } /** * Druid对应的bean * Spring会将当前方法的返回值以指定的id存储到Spring的IOC容器中 * @return * @throws Exception */ @Bean("dataSourceDruid") public DataSource getDruidDataSource() throws Exception{ // 创建数据源对象 DruidDataSource dataSource = new DruidDataSource(); // 设置数据源基本连接数据 dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } } -
测试代码如下
package at.guigu.web; import at.guigu.config.SpringConfiguration; import at.guigu.service.impl.BookServiceImpl; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestOne { @Test public void test3() { //1 获取IOC容器 // 通过Spring配置文件获取:ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 通过Spring类获取 ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class); //2 从IOC容器中获取bean对应的对象 BookServiceImpl bookService = (BookServiceImpl) app.getBean("bookServiceImpl"); bookService.save(); } @Test public void test4() { //1 获取IOC容器 // 通过Spring配置文件获取:ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 通过Spring类获取 ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class); //2 从IOC容器中获取bean对应的对象 ComboPooledDataSource dataSourceCp30 = (ComboPooledDataSource) app.getBean("dataSourceCp30"); System.out.println(dataSourceCp30); } }
-
-
在实际开发中代替Spring配置文件的
SpringConfiguration类中的代码会很多,我们可以将其根据不同种类拆分开,然后在主Spring配置文件对应的类中引入拆分后的类,代码如下-
Step1: 在
config包下创建拆分配置文件对应的拆分类DataSourceConfiguration,代码如下package at.guigu.config; import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; import javax.sql.DataSource; // 分配置文件对应的类不用配置@Configuration以及@ComponentScan注解 // 加载properties配置文件<context:property-placeholder location="classpath:jdbc.properties"/> @PropertySource("classpath:jdbc.properties") public class DataSourceConfiguration { @Value("${jdbc.driverClassName}") private String driverClassName; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * Cp30对应的bean * Spring会将当前方法的返回值以指定的id存储到Spring的IOC容器中 * @return * @throws Exception */ @Bean("dataSourceCp30") public DataSource getCp30DataSource() throws Exception{ // 创建数据源对象 ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 设置数据源基本连接数据 dataSource.setDriverClass(driverClassName); dataSource.setJdbcUrl(url); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } /** * Druid对应的bean * Spring会将当前方法的返回值以指定的id存储到Spring的IOC容器中 * @return * @throws Exception */ @Bean("dataSourceDruid") public DataSource getDruidDataSource() throws Exception{ // 创建数据源对象 DruidDataSource dataSource = new DruidDataSource(); // 设置数据源基本连接数据 dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } } -
Step2: 在Spring主配置文件对应的主类
SpringConfiguration中引入分配置文件对应的拆分类DataSourceConfiguration,代码如下package at.guigu.config; import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.*; import javax.sql.DataSource; // 该注解代表该类是Spring的核心配置类 @Configuration // 配置注解的组件扫描<context:component-scan base-package="at.guigu"></context:component-scan> @ComponentScan("at.guigu") // 引入拆分配置文件<import resource="applicationContext-xxx.xml"/> @Import(DataSourceConfiguration.class) public class SpringConfiguration { }
-
Spring集成Junit
-
原始Junit测试Spring的问题
-
在测试类中均需要以下两行代码,这两行代码若不写的话就会造成空指针异常
//1 获取IOC容器 // 通过Spring配置文件获取:ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 通过Spring类获取 ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class); //2 从IOC容器中获取bean对应的对象 ComboPooledDataSource dataSourceCp30 = (ComboPooledDataSource) app.getBean("dataSourceCp30");
-
-
解决原始Junit测试Spring问题的方案
- 让SpringJunit负责创建Spring容器,但需要将配置文件名称告知,然后将需要进行测试的bean直接在测试类中进行注入
Spring集成Junit代码实现
-
Spring集成Junit步骤
- 导入Spring集成Junit的坐标
- 使用
@Runwith注解替换原来的运行期- 其属性为
SpringRunner.class或SpringJUnit4ClassRunner.class:用于集成 Spring 测试框架
- 其属性为
- 使用
@ContextConfiguration指定Spring配置文件或Spring配置类- 指定Spring配置文件:
@ContextConfiguration("classpath:applicationContext.xml") - 指定Spring配置类:
@ContextConfiguration(classes = {SpringConfiguration.class})
- 指定Spring配置文件:
- 使用
@Autowired注入需要测试的对象 - 创建测试方法进行测试
-
步骤如下
-
该步骤有两种形式
- Spring配置文件的形式:Spring配置文件及与之对应的
UserDaoImpl、BookServiceImpl代码均省略 - Spring配置类的形式:Spring配置类及与之对应的
UserDaoImpl、BookServiceImpl代码均省略,可详见Spring注解开发部分内容
- Spring配置文件的形式:Spring配置文件及与之对应的
-
Step1: 导入Spring集成Junit的坐标,即spring-test坐标
- 除了Junit坐标外还有spring、Annotation、数据源(连接池)的坐标和数据库mysql驱动坐标。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> <parent> <groupId>org.example</groupId> <artifactId>SpringDemo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SpringAnno</artifactId> <packaging>war</packaging> <name>SpringAnno 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> <!--Annotation坐标--> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!--spring-test坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.1.6</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>SpringAnno</finalName> <plugins> <!-- Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build> </project> -
Step2: 创建测试类
TestTwo。代码如下- Spring配置文件的形式
package at.guigu.web; import at.guigu.service.BookService; import at.guigu.service.impl.BookServiceImpl; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; // 1 集成 Spring 测试框架 @RunWith(SpringJUnit4ClassRunner.class) // 2 指定Spring配置文件或Spring配置类 @ContextConfiguration("classpath:applicationContext.xml") public class TestTwo { // 3 注入需要测试的对象 @Autowired private BookServiceImpl bookService; // 3 注入需要测试的对象:若需要测试的bean有多个则与@Qualifier结合指定对应id的bean @Autowired @Qualifier("dataSourceDruid") private DataSource dataSourceDruid; // 3 注入需要测试的对象:若需要测试的bean有多个则与@Qualifier结合指定对应id的bean @Autowired @Qualifier("dataSourceCp30") private DataSource dataSourceCp30; // 4 创建测试方法进行测试 @Test public void test1() { bookServiceImpl.save(); System.out.println(dataSourceDruid); System.out.println(dataSourceCp30); } }- Spring配置类的形式
package at.guigu.web; import at.guigu.config.SpringConfiguration; import at.guigu.service.BookService; import at.guigu.service.impl.BookServiceImpl; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.sql.DataSource; // 1 集成 Spring 测试框架 @RunWith(SpringJUnit4ClassRunner.class) // 2 指定Spring配置文件或Spring配置类 @ContextConfiguration(classes = {SpringConfiguration.class}) public class TestTwo { // 3 注入需要测试的对象 @Autowired private BookService bookServiceImpl; // 3 注入需要测试的对象:若需要测试的bean有多个则与@Qualifier结合指定对应id的bean @Autowired @Qualifier("dataSourceDruid") private DataSource dataSourceDruid; // 3 注入需要测试的对象:若需要测试的bean有多个则与@Qualifier结合指定对应id的bean @Autowired @Qualifier("dataSourceCp30") private DataSource dataSourceCp30; // 4 创建测试方法进行测试 @Test public void test1() { bookServiceImpl.save(); System.out.println(dataSourceDruid); System.out.println(dataSourceCp30); } }
-
Spring集成Web环境
-
即集成
web/Controller表现层步骤-
Step1: 在pom.xml文件中导入servlet和jsp坐标
- 导入servlet和jsp坐标后三层架构中的
web/Controller表现层才会起效 - Spring需要导入的所有坐标:spring、spring-test、Annotation、servlet、jsp坐标。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> <parent> <groupId>org.example</groupId> <artifactId>SpringDemo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SpringWeb</artifactId> <packaging>war</packaging> <name>SpringWeb 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> <!--Annotation坐标--> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!--spring-test坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.1.6</version> <scope>test</scope> </dependency> <!-- servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--jsp--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency> </dependencies> <build> <finalName>SpringWeb</finalName> <plugins> <!-- Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build> </project> - 导入servlet和jsp坐标后三层架构中的
-
Step2: 创建三层架构,并写入对应接口和实现类,截图如下

-
Step3: 配置properties以及Spring配置文件,截图如下

-
Step4: 在
web/Controller表现层下创建类BookServlet,代码如下package at.guigu.web; import at.guigu.service.impl.BookServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; import javax.sql.DataSource; import java.io.IOException; // 此时url不通过@WebServlet注解进行配置,而是在web.xml中进行配置 // @WebServlet("/bookServlet") public class BookServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1 获取IOC容器 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //2 从IOC容器中获取bean对应的对象 BookServiceImpl bookServiceImpl = (BookServiceImpl) app.getBean("bookServiceImpl"); DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid"); bookServiceImpl.save(); System.out.println(dataSourceDruid); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } -
Step5: 在web项目核心目录(即
WEB-INF)下的web.xml文件中进行web配置,代码如下- 利用
<servlet>标签声明Servlet - 利用
<servlet-mapping>标签将URL模式映射到特定的Servlet上 <servlet>标签一定要在<servlet-mapping>标签之前,否则会报错
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--声明一个Servlet--> <servlet> <!--指定要声明的Servlet的类名--> <servlet-name>BookServlet</servlet-name> <!--声明的Servlet的全限定名--> <servlet-class>at.guigu.web.BookServlet</servlet-class> </servlet> <!--将URL模式映射到特定的Servlet上--> <servlet-mapping> <!--指定的Servlet的类名--> <servlet-name>BookServlet</servlet-name> <!--给指定的Servlet设置url,相当于@WebServlet("/bookServlet")--> <url-pattern>/bookServlet</url-pattern> </servlet-mapping> </web-app> - 利用
-
Step6: 利用Tomcat运行该Web项目,运行截图如下

-
ApplicationContext应用上下文获取方式
-
定义:应用上下文对象是通过
new ClassPathXmlApplicationContext("spring配置文件");方式获取的,但是每次从容器中获得bean时都要编写该句代码,这就会造成代码冗余,配置文件加载多次,应用上下文对象创建多次-
解释:在Spring集成web环境中,其中Web层代码是通过Spring容器获取Service层的,即
BookServlet类中的如下两句代码//1 获取IOC容器 (ApplicationContext应用上下文获取方式) ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); //2 从IOC容器中获取bean对应的对象 BookServiceImpl bookServiceImpl = (BookServiceImpl) app.getBean("bookServiceImpl"); DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid"); -
由于
web/Controller表现层在后期会有很多业务(即很多Servlet类),所以就会造成代码冗余,配置文件加载多次,应用上下文对象创建多次
-
-
解决方式
在Web项目中,可以使用
ServletContextListener监听Web应用的启动,我们可在Web应用启动时就加载Spring配置文件,创建Spring的应用上下文对象ApplicationContext,然后将其存储到最大的域servletContext对象中,这样就可以在任意位置从域中获取应用上下文ApplicationContext对象了监听器Listener知识点内容可详见Filter&Listener&AJAX&Axios&JSON
自定义ContextListener
-
步骤
-
Step1: 在
web包下创建listener包,定义一个实现ServletContextListener接口的实现类ContextLoaderListener,代码及步骤如下package at.guigu.web.listener; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; @WebListener public class ContextLoaderListener implements ServletContextListener { public ContextLoaderListener() { } @Override public void contextInitialized(ServletContextEvent sce) { // 1 获取IOC容器,创建应用上下文对象`ApplicationContext` (ApplicationContext应用上下文获取方式) ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 2 获取最大域ServletContext对象 ServletContext servletContext = sce.getServletContext(); // 2 将Spring的应用上下文对象`ApplicationContext`存储到最大的域`servletContext`中 servletContext.setAttribute("app", app); } @Override public void contextDestroyed(ServletContextEvent sce) { } } -
Step2: 在web项目核心目录(即
WEB-INF)下的web.xml文件中进行监听器Listener配置,代码如下<listener>标签一定要在<servlet>标签之前,否则会报错
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <listener> <listener-class>at.guigu.web.listener.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>BookServlet</servlet-name> <servlet-class>at.guigu.web.BookServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>BookServlet</servlet-name> <url-pattern>/bookServlet</url-pattern> </servlet-mapping> </web-app> -
Step3:
web/Controller表现层下创建类BookServlet的代码更改如下package at.guigu.web; import at.guigu.service.impl.BookServiceImpl; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.*; import javax.servlet.http.*; import javax.sql.DataSource; import java.io.IOException; public class BookServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1 获取最大域ServletContext对象 ServletContext servletContext = request.getServletContext(); // 2 通过最大域ServletContext对象获取Spring的应用上下文对象,即IOC容器 ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app"); // 3 获取IOC容器中的bean BookServiceImpl bookServiceImpl = (BookServiceImpl) app.getBean("bookServiceImpl"); DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid"); bookServiceImpl.save(); System.out.println(dataSourceDruid); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } -
然后利用Tomcat运行该Web项目即可
-
-
在上述过程中,其中监听器
ContextLoaderListener类中创建应用上下文对象ApplicationContext的那句代码需要传入Spring配置文件,这会造成耦合问题,因为我们可能会修改Spring配置文件的名称,为降低耦合,我们可将其进一步优化,步骤如下-
Step1:在web项目核心目录(即
WEB-INF)下的web.xml文件中添加一个<context-param>标签用于定义全局初始化参数,此处用于定义Spring配置文件供监听器使用,web.xml文件代码如下- 后期若Spring配置文件名字更改,我们就只需要更改web.xml文件中
<context-param>标签中的子标签<param-value>的参数即可
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--全局初始化参数--> <context-param> <!--定义参数的名称,必须是唯一的--> <param-name>contextConfigLocation</param-name> <!--定义参数的值--> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--监听器--> <listener> <!--监听器类的全限定名--> <listener-class>at.guigu.web.listener.ContextLoaderListener</listener-class> </listener> <!--声明一个Servlet--> <servlet> <!--声明的Servlet的类名--> <servlet-name>BookServlet</servlet-name> <!--声明的Servlet的全限定名--> <servlet-class>at.guigu.web.BookServlet</servlet-class> </servlet> <!--将URL模式映射到特定的Servlet上--> <servlet-mapping> <!--指定的Servlet的类名--> <servlet-name>BookServlet</servlet-name> <!--给指定的Servlet设置url,相当于@WebServlet("/bookServlet")--> <url-pattern>/bookServlet</url-pattern> </servlet-mapping> </web-app> - 后期若Spring配置文件名字更改,我们就只需要更改web.xml文件中
-
Step2: 实现
ServletContextListener监听器接口的实现类ContextLoaderListener代码更改如下package at.guigu.web.listener; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; @WebListener public class ContextLoaderListener implements ServletContextListener { public ContextLoaderListener() { } @Override public void contextInitialized(ServletContextEvent sce) { // 1 获取最大域`servletContext`对象 ServletContext servletContext = sce.getServletContext(); // 2 通过最大域`servletContext`对象获取web.xml文件中的全局参数 String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation"); // 3 获取IOC容器,创建应用上下文对象`ApplicationContext` (ApplicationContext应用上下文获取方式) ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation); // 4 将应用上下文对象`ApplicationContext`存入最大域中 servletContext.setAttribute("app", app); } @Override public void contextDestroyed(ServletContextEvent sce) { // 释放资源 } }
-
-
在上述优化后仍具有耦合,因为应用上下文对象的对象名是由开发人员在监听器对应的类中决定的,在Web层使用最大域对象来获取应用上下文对象时必须知道监听器类中所设置的对象名,这就具有耦合,为了解耦合,我们可以设置一个工具类,专门来返回应用上下文对象
ApplicationContext在最大域中的键名,步骤如下-
Step1: 在util包下创建工具类
ApplicationContextUtil,该类代码如下package at.guigu.util; import org.springframework.context.ApplicationContext; import javax.servlet.ServletContext; public class ApplicationContextUtil { public static ApplicationContext getApplicationContext(ServletContext servletContext) { return (ApplicationContext) servletContext.getAttribute("app"); } } -
Step2:
web/Controller表现层下创建类BookServlet的代码更改如下package at.guigu.web; import at.guigu.service.impl.BookServiceImpl; import at.guigu.util.ApplicationContextUtil; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.context.ApplicationContext; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class BookServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1 获取最大域ServletContext对象 ServletContext servletContext = request.getServletContext(); // 2 获取应用上下文对象(即IOC容器) ApplicationContext app = ApplicationContextUtil.getApplicationContext(servletContext); //3 获取bean BookServiceImpl bookServiceImpl = app.getBean(BookServiceImpl.class); //等同于BookServiceImpl bookServiceImpl = (BookServiceImpl) app.getBean("bookServiceImpl"); DruidDataSource dataSourceDruid = app.getBean(DruidDataSource.class); //等同于DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid"); bookServiceImpl.save(); System.out.println(dataSourceDruid); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
优化后完整步骤方式一(使用自己创建的监听器类)
-
Step1: 在pom.xml文件中导入坐标:spring、Annotation、spring-test、servlet、jsp以及Tomcat插件
-
若需要mysql及数据源(数据库连接池),则导入坐标:mysql、druid、c3p0
-
所有坐标代码如下
<!--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> <!--Annotation坐标--> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!--spring-test坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.1.6</version> <scope>test</scope> </dependency> <!-- servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--jsp--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</scope> </dependency>
-
-
Step2: 创建三层架构,并写入对应接口和实现类,截图如下

-
Step3: 配置properties以及Spring配置文件,截图如下

-
**Step4:**在util包下创建工具类
ApplicationContextUtil,该类代码如下package at.guigu.util; import org.springframework.context.ApplicationContext; import javax.servlet.ServletContext; public class ApplicationContextUtil { public static ApplicationContext getApplicationContext(ServletContext servletContext) { return (ApplicationContext) servletContext.getAttribute("app"); } } -
Step5: 在web包下创建listener包,并在listener包下创建实现
ServletContextListener接口的监听器类ContextLoaderListener,代码如下package at.guigu.web.listener; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.annotation.*; @WebListener public class ContextLoaderListener implements ServletContextListener { public ContextLoaderListener() { } @Override public void contextInitialized(ServletContextEvent sce) { // 1 获取最大的域`servletContext`对象 ServletContext servletContext = sce.getServletContext(); // 2 利用`servletContext`对象读取web.xml文件中的全局参数 String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation"); // 3 获取IOC容器:获取应用上下文对象`ApplicationContext` 并将其存储到最大域中 servletContext.setAttribute("app", new ClassPathXmlApplicationContext(contextConfigLocation)); /* 等同于 ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation); servletContext.setAttribute("app", app); */ } @Override public void contextDestroyed(ServletContextEvent sce) { // 释放资源 } } -
Step6: 在
web/Controller表现层下创建类BookServlet,代码如下package at.guigu.web; import at.guigu.service.impl.BookServiceImpl; import at.guigu.util.ApplicationContextUtil; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.context.ApplicationContext; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class BookServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1 获取最大域ServletContext对象 ServletContext servletContext = request.getServletContext(); // 2 获取应用上下文对象(即IOC容器) ApplicationContext app = ApplicationContextUtil.getApplicationContext(servletContext); //3 获取bean BookServiceImpl bookServiceImpl = app.getBean(BookServiceImpl.class); //等同于BookServiceImpl bookServiceImpl = (BookServiceImpl) app.getBean("bookServiceImpl"); DruidDataSource dataSourceDruid = app.getBean(DruidDataSource.class); //等同于DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid"); bookServiceImpl.save(); System.out.println(dataSourceDruid); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } } -
Step7: 在web项目核心目录(即
WEB-INF)下的web.xml文件中进行:全局初始化参数、监听器Listener配置、web配置。完整代码如下<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--全局初始化参数--> <context-param> <!--定义参数的名称,必须是唯一的--> <param-name>contextConfigLocation</param-name> <!--定义参数的值--> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--监听器--> <listener> <!--监听器类的全限定名--> <listener-class>at.guigu.web.listener.ContextLoaderListener</listener-class> </listener> <!--声明一个Servlet--> <servlet> <!--声明的Servlet的类名--> <servlet-name>BookServlet</servlet-name> <!--声明的Servlet的全限定名--> <servlet-class>at.guigu.web.BookServlet</servlet-class> </servlet> <!--将URL模式映射到特定的Servlet上--> <servlet-mapping> <!--指定的Servlet的类名--> <servlet-name>BookServlet</servlet-name> <!--给指定的Servlet设置url,相当于@WebServlet("/bookServlet")--> <url-pattern>/bookServlet</url-pattern> </servlet-mapping> </web-app> -
Step8: Tomcat运行该Web项目即可
优化后完整步骤方式二(使用Spring提供的监听器类)
完整步骤方式二,已上传到Gitee,可自行下载
Spring配置文件的形式
-
在优化后完整步骤方式一中使用的是我们自己创建的继承
ServletContextListener接口的监听器类ContextLoaderListener -
此处我们使用Spring所提供的监听器
ContextLoaderListener-
该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到最大域
ServletContext中 -
它提供了一个客户端工具类
WebApplicationContextUtils供使用者获取应用上下文对象 -
使用该监听器的步骤
-
在web项目核心目录(即
WEB-INF)下的web.xml文件中配置ContextLoaderListener监听器(前提是要先导入spring-web 坐标)<!--必须为5.2.X.RELEASE版本,否则会报错使用WebApplicationContextUtils获取应用上下文对象会报错:需要的是Jakarta Servlet而不是Java Servlet--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.25.RELEASE</version> </dependency> -
使用客户端工具类
WebApplicationContextUtils获取应用上下文对象ApplicationContext
-
-
-
实现步骤
-
Step1: 在pom.xml文件中导入坐标:spring、spring-web、spring-test、Annotation、servlet、jsp以及Tomcat插件
- 若需要mysql及数据源(数据库连接池),则导入坐标:mysql、druid、c3p0
- 若需要MyBatis,则导入:spring-jdbc、mybatis-spring、mybatis
<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> <parent> <groupId>org.example</groupId> <artifactId>SpringDemo</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>SpringWeb</artifactId> <packaging>war</packaging> <name>SpringWeb 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相关坐标=======================--> <!--spring坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.1.6</version> </dependency> <!--spring-web --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.2.25.RELEASE</version> </dependency> <!--spring-test坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.1.6</version> <scope>test</scope> </dependency> <!--Annotation坐标--> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!-- servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--jsp--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> <scope>provided</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> <!--=====================MyBatis相关坐标=========================--> <!--spring-jdbc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>6.1.10</version> </dependency> <!--mybatis-spring--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>3.0.3</version> </dependency> <!--MyBatis坐标--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.16</version> </dependency> </dependencies> <build> <finalName>SpringWeb</finalName> <plugins> <!-- Tomcat插件 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> </plugins> </build> </project> -
Step2: 创建三层架构,并写入对应接口和实现类,截图如下

-
Step3: 配置properties以及Spring配置文件,截图如下

-
Step4: 在web项目核心目录(即
WEB-INF)下的web.xml文件中进行:全局初始化参数、配置Spring所提供的ContextLoaderListener监听器、web配置。完整代码如下<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--全局初始化参数--> <context-param> <!--定义参数的名称,必须是唯一的--> <param-name>contextConfigLocation</param-name> <!--定义参数的值--> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--监听器--> <!--配置Spring所提供的`ContextLoaderListener` 监听器--> <listener> <!--监听器类的全限定名--> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--声明一个Servlet--> <servlet> <!--声明的Servlet的类名--> <servlet-name>BookServlet</servlet-name> <!--声明的Servlet的全限定名--> <servlet-class>at.guigu.web.BookServlet</servlet-class> </servlet> <!--将URL模式映射到特定的Servlet上--> <servlet-mapping> <!--指定的Servlet的类名--> <servlet-name>BookServlet</servlet-name> <!--给指定的Servlet设置url,相当于@WebServlet("/bookServlet")--> <url-pattern>/bookServlet</url-pattern> </servlet-mapping> </web-app> -
Step5: 在
web/Controller表现层下创建类BookServlet,代码如下package at.guigu.web; import at.guigu.service.impl.BookServiceImpl; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class BookServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // 1 获取最大域ServletContext对象 ServletContext servletContext = request.getSession().getServletContext(); // 2 获取应用上下文对象(即IOC容器) ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext); //3 获取bean BookServiceImpl bookServiceImpl = app.getBean(BookServiceImpl.class); //等同于BookServiceImpl bookServiceImpl = (BookServiceImpl) app.getBean("bookServiceImpl"); DruidDataSource dataSourceDruid = app.getBean(DruidDataSource.class); //等同于DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid"); bookServiceImpl.save(); System.out.println(dataSourceDruid); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
-
Spring注解开发配置类的形式
-
Step1: 在pom.xml文件中导入坐标:spring、spring-web、spring-test、Annotation、servlet、jsp以及Tomcat插件
- 若需要mysql及数据源(数据库连接池),则导入坐标:mysql、druid、c3p0
- 若需要MyBatis,则导入:spring-jdbc、mybatis-spring、mybatis
- pom.xml代码详见Spring配置文件的形式
-
Step2: 创建三层架构,并写入对应接口和实现类,并使用Spring注解开发截图如下

-
Step3: 右键源代码配置文件目录(即资源文件
resources)→New→File,创建properties配置文件,博主文件名为jdbc.properties,该配置文件代码如下- 注意: properties配置文件中配置的各个属性前必须添加个
id.(即id.属性,比如:属性url就设置为id.url,博主设置的为jdbc.url),以供Spring配置文件可以使用属性占位符${}语法引用这些属性
#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配置文件中配置的各个属性前必须添加个
-
Step4: 在三层架构包下创建一个
config包,并在该包下创建一个代替Spring配置文件的类SpringConfiguration,完整代码如下package at.guigu.config; import com.alibaba.druid.pool.DruidDataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import javax.sql.DataSource; // 该注解代表该类是Spring的核心配置类 @Configuration // 配置注解的组件扫描<context:component-scan base-package="at.guigu"></context:component-scan> @ComponentScan("at.guigu") // 加载properties配置文件<context:property-placeholder location="classpath:jdbc.properties"/> @PropertySource("classpath:jdbc.properties") public class SpringConfiguration { @Value("${jdbc.driverClassName}") private String driverClassName; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; /** * Cp30对应的bean * Spring会将当前方法的返回值以指定的id存储到Spring的IOC容器中 * @return * @throws Exception */ @Bean("dataSourceCp30") public DataSource getCp30DataSource() throws Exception{ // 创建数据源对象 ComboPooledDataSource dataSource = new ComboPooledDataSource(); // 设置数据源基本连接数据 dataSource.setDriverClass(driverClassName); dataSource.setJdbcUrl(url); dataSource.setUser(username); dataSource.setPassword(password); return dataSource; } /** * Druid对应的bean * Spring会将当前方法的返回值以指定的id存储到Spring的IOC容器中 * @return * @throws Exception */ @Bean("dataSourceDruid") public DataSource getDruidDataSource() throws Exception{ // 创建数据源对象 DruidDataSource dataSource = new DruidDataSource(); // 设置数据源基本连接数据 dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } } -
Step5: 在web项目核心目录(即
WEB-INF)下的web.xml文件中进行:全局初始化参数、配置Spring所提供的ContextLoaderListener监听器、web配置。完整代码如下<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--配置Spring配置类的全局初始化参数--> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <context-param> <!--定义参数的名称,必须是唯一的--> <param-name>contextConfigLocation</param-name> <!--定义参数的值--> <param-value>at.guigu.config.SpringConfiguration</param-value> </context-param> <!--监听器--> <!--配置Spring所提供的`ContextLoaderListener` 监听器--> <listener> <!--监听器类的全限定名--> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--声明一个Servlet--> <servlet> <!--声明的Servlet的类名--> <servlet-name>UserServlet</servlet-name> <!--声明的Servlet的全限定名--> <servlet-class>at.guigu.web.UserServlet</servlet-class> </servlet> <!--将URL模式映射到特定的Servlet上(即UserServlet)--> <servlet-mapping> <!--指定的Servlet的类名--> <servlet-name>UserServlet</servlet-name> <!--给指定的Servlet设置url,相当于@WebServlet("/userServlet")--> <url-pattern>/userServlet</url-pattern> </servlet-mapping> </web-app> -
Step6: 在web层下创建
UserServlet类,代码如下package at.guigu.web; import at.guigu.service.impl.UserServiceImpl; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1 获取最大域ServletContext对象 ServletContext servletContext = request.getServletContext(); //等同于ServletContext servletContext = request.getSession().getServletContext(); // 2 获取应用上下文对象(即IOC容器) ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext); //3 获取bean UserServiceImpl userServiceImpl = app.getBean(UserServiceImpl.class); //等同于BookServiceImpl bookServiceImpl = (BookServiceImpl) app.getBean("bookServiceImpl"); DruidDataSource dataSourceDruid = app.getBean(DruidDataSource.class); //等同于DataSource dataSourceDruid = (DataSource) app.getBean("dataSourceDruid"); userServiceImpl.save(); System.out.println(dataSourceDruid); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }Tomcat运行该Web项目后截图如下所示










![【ROS2】高级:解锁 Fast DDS 中间件的潜力 [社区贡献]](https://img-blog.csdnimg.cn/img_convert/0913e1ab977a79cb45f4e722dd25d82c.png)








