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