目录
- Spring
- 简介
- 发展史
- Spring Framework系统架构
- spring 核心概念
- IOC、IOC容器、Bean、DI
- IOC快速入门
- DI快速入门
- IOC
- Bean基础配置
- id与class属性
- name属性
- scope属性
- Bean的实例化
- 构造方法
- 静态工厂
- 实例工厂
- FactoryBean的使用(工厂实例的简化)
- Bean的生命周期
- 添加初始化和销毁方法
- close关闭容器
- 注册钩子关闭容器
- 关闭方式的区别
- 通过接口来简化关闭容器
- DI
- setter注入
- 注入引用数据类型
- 注入简单数据类型
- 构造器注入
- 构造器注入多个数据类型
- 构造器注入多个简单数据类型
- 解决构造函数中参数名紧耦合
- 自动装配
- 什么是依赖自动装配
- 自动装配的方式有哪些?
- 按照类型完成自动装配的配置
- 按照名称完成自动注入装配的配置
- 注意事项(配置特征)
- 集合注入
- 加载 properties 文件
- 读取单个属性
- 核心容器
- 容器
- Bean 的三种获取方式
- 容器类层次结构
- BeanFactory的使用
- IOC/DI注解开发
- 注解开发定义bean
- 纯注解开发模式
- 注解开发bean作用范围与生命周期管理
- bean的作用范围
- bean的生命周期
- 注解开发依赖注入
- 注解实现按照类型注入
- 注解实现按照名称注入
- 简单数据类型注入
- 注解读取 properties 配置文件
- IOC/DI注解开发管理第三方bean
- 注解开发管理第三方bean
- 引入外部配置类
- 使用包扫描引入
- 使用@Import注解引入
- 注解开发实现为第三方bean注入资源
- 简单数据库类型
- 引用数据类型
- XML开发和注解的对比
Spring
官网:Spring | Home
简介
随着时代发展,软件规模与功能都呈几何式增长,开发难度也在不断递增,Spring可以简化开发
,降低企业级开发的复杂性,使开发变得更简单快捷
随着项目规模与功能的增长,遇到的问题就会增多,为了解决问题会引入更多的框架,Spring可以框架整合
,高效整合其他技术,提高企业级应用开发与运行效率
Spring发展到今天已经形成了一种开发的生态圈,Spring提供了若干个项目,每个项目用于完成特定的功能
- Spring Framework: Spring框架,是Spring中最早最核心的技术,也是所有其他技术的基础。
- SpringBoot: Spring是来简化开发,而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发。
- SpringCloud: 这个是用来做分布式之微服务架构的相关开发。
发展史
- IBM(IT公司-国际商业机器公司)在1997年提出了EJB思想,早期的JAVAEE开发大都基于该思想。
- Rod Johnson(Java和J2EE开发领域的专家)在2002年出版的Expert One-on-One J2EE Design and Development ,书中有阐述在开发中使用EJB该如何做。
- Rod Johnson在2004年出版的Expert One-on-One J2EE Development without EJB ,书中提 出了比EJB思想更高效的实现方案,并且在同年将方案进行了具体的落地实现,这个实现就是 Spring1.0。
- 随着时间推移,版本不断更新维护,目前最新的是Spring5
- Spring1.0是纯配置文件开发
- Spring2.0为了简化开发引入了注解开发,此时是配置文件加注解的开发方式
- Spring3.0已经可以进行纯注解开发,使开发效率大幅提升
- Spring4.0根据JDK的版本升级对个别API进行了调整
- Spring5.0已经全面支持JDK8
Spring Framework系统架构
Spring Framework 是 Spring 生态圈中最基础的项目,是其他项目的根基
Spring Framework的发展也经历了很多版本的变更,每个版本都有相应的调整
Spring Framework的5版本目前没有最新的架构图,而最新的是4版本,所以接下来主要研究的 是4的架构图
- 核心层
- Core Container:核心容器,这个模块是 Spring 最核心的模块,其他的都需要依赖该模块
- AOP层
- AOP:面向切面编程,它依赖核心层容器,目的是在不改变原有代码的前提下对其进行功能增强
- Aspect:AOP思想,Aspects 是对 AOP 思想的具体实现
- 数据层
- Data Access:数据访问,Spring 全家桶中有对数据访问的具体实现技术
- Data Integration:数据集成,Spring 支持整合其他数据层解决方案,比如MyBatis
- Transactions:事务,Spring 中事务管理是 Spring AOP 的一个具体实现
- Web层
- Spring MVC 框架
- Test层
- Spring 主要整合了 Junit 来完成单元测试和集成测试
spring 核心概念
IOC、IOC容器、Bean、DI
在这个代码中,由于业务层代码中需要数据层的对象,导致两层之间的耦合度很高,针对这个问题 Spring 提出了一个解决方案:使用对象时,在程序中不要主动使用 new 产生对象,转换为由外部提供对象
-
IOC(Inversion of Control)控制反转:控制反转的是对象的创建权
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建的控制权由程序转移到外部,此思想称为控制反转
- Spring技术对IOC思想进行了实现,提供了一个容器,称为
IOC容器
,用来充当IOC思想的 “ 外部 ” - IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为
Bean对象
-
DI(Dependency Injection)依赖注入:绑定对象与对象之间的依赖关系
但是现在又有一个问题,当 IOC 容器中创建好 service 和 dao 对象后,因为 service 运行需要依赖 dao 对象,但是 service 对象和 dao 对象没有任何关系,导致程序无法正确运行,而在容器中建立对象与对象之间的绑定关系就要用到 DI
- 在容器中建立 bean 与 bean 之间的依赖关系的整个过程,称为依赖注入
介绍完 Spring 的 IOC 和 DI 的概念后,我们会发现这两个概念的最终目标就是:充分解耦
,具体实现靠:
- 使用 IOC 容器管理 bean(IOC)
- 在 IOC 容器内将有依赖关系的 bean 进行关系绑定(DI)
- 最终结果为:使用对象时不仅可以直接从 IOC 容器中获取,并且获取到的 bean 已经绑定了所有的依赖关系.
- IOC容器:Spring 创建了一个容器用来存放所创建的对象,这个容器就叫 IOC 容器
- Bean:容器中所存放的一个个对象就叫 Bean 或 Bean 对象
IOC快速入门
-
创建 Maven 项目
-
pom.xml引入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency>
-
resources下创建 spring 配置文件(applicationContext.xml),然后配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--bean标签标示配置bean id属性标示给bean起名字 class属性表示给bean定义类型 --> <bean id="bookDao" class="项目下的Dao实现类路径"/> <bean id="bookService" class="项目下的Service实现类路径"/> </beans>
注意事项: bean 定义时 id 属性在同一个配置文件中不能重复
-
使用 Spring 提供的接口完成 IOC 容器的创建
-
从容器中获取对象进行方法调用
public class App { public static void main(String[] args) { //获取IOC容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");//这里的这个xml就是刚才创建的Spring的配置文件 BookDao bookDao = (BookDao) ctx.getBean("bookDao");//通过容器来获取Bean对象 bookDao.save(); } }
DI快速入门
实现依赖注入,必须要基于 IOC 管理 Bean
-
去除代码中的 new
-
为属性提供 setter 方法
public class BookServiceImpl implements BookService { //删除业务层中使用new的方式创建的dao对象 //private BookDao bookDao = new BookDaoImpl(); private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } //提供对应的set方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
-
在配置文件中添加依赖注入的配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--bean标签标示配置bean id属性表示给bean起名字 class属性表示给bean定义类型 --> <bean id="bookDao" class="com.XXX.dao.impl.BookDaoImpl"/> <bean id="bookService" class="com.XXX.service.impl.BookServiceImpl"> <!--配置server与dao的关系--> <!--property标签表示配置当前bean的属性 name属性表示配置哪一个具体的属性 ref属性表示参照哪一个bean --> <property name="bookDao" ref="bookDao"/> </bean> </beans>
注意: 配置中的两个 bookDao 的含义是不一样的
- name="bookDao"中bookDao的作用是让Spring的IOC容器在获取到名称后,将首字母大写,前 面加set找对应的setBookDao()方法进行对象注入
- ref="bookDao"中 bookDao 的作用是让 Spring 能在IOC容器中找到id为bookDao的Bean对象给 bookService 进行注入
IOC
IOC 中对象的相关配置以及实例化方式和生命周期
Bean基础配置
id与class属性
注意: 因为 bookDao 和 bookService 一般在项目中是接口,接口不能实现,所以一般是使用它的实现类来进行对象的创建
name属性
XML中的配置写法
<!--name属性:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
<!-- Ebi全称Enterprise Business Interface,翻译为企业业务接口 -->
<bean id="bookService" name="service service4 bookEbi"
class="BookServiceImpl的类路径">
<property name="bookDao" ref="bookDao"/>
</bean>
<bean id="bookDao" name="dao" class="BookDaoImpl的类路径"/>
注意:
- 通过 ref 属性来指定 bean ,被指定的 bean 必须在容器中存在,而且 ref 属性值可以是 bean 的 name 属性值
- 获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常 NoSuchBeanDefinitionException
scope属性
注意:
- bean 为单例的意思是在 Spring 的 IOC 容器中只会有该类的一个对象,避免了对象的频繁创建与销毁,达到了 bean 对象的复用,性能高
- bean在容器中
- 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
- 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
- 表现层对象、业务层对象、数据层对象、工具对象适合交给容器进行管理,封装实例的域对象,因为会引发线程安全问题,所以不适合交给容器进行管理
Bean的实例化
bean本质上就是对象,对象在new的时候会使用构造方法完成,那创建bean也是使用构造方法完成的,基于此,Bean 有三种创建方式
构造方法
Spring底层使用的是类的无参构造方法,而且是通过反射来访问到类中的方法的
静态工厂
这种方式一般是用来兼容早期的一些老系统,需要在Spring的配置文件中加入以下内容:
<bean id="orderDao" class="OrderDaoFactory类路径名" factory-method="getOrderDao"/>
<!-- class:工厂类的类全名 -->
<!-- factory-mehod:具体工厂类中创建对象的方法名 -->
虽然在工厂类中也是直接new对象,但是在工厂的静态方法中,我们除了new 对象外还可以做一些必不可少的业务操作
实例工厂
在Spring的配置文件中加入以下内容:
<bean id="userFactory" class="UserDaoFactory的类路径"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
实例化工厂运行的顺序是:
- 创建实例化的工厂对象,对应的是第一行配置
- 调用对象中的方法来创建bean对象,对应的是第二行配置
- factory-bean:工厂对象
- factory-method:工厂对象中具体创建 bean 对象的方法名,对应关系如下:
FactoryBean的使用(工厂实例的简化)
这种方式在Spring去整合其他框架的时候会被用到
让工厂类(这里以UserDaoFactory为例)实现FactoryBean接口,重写接口的方法:
- 方法一: getObject(),被重写后,在方法中进行对象的创建并返回
- 方法二: getObjectType(),被重写后,主要返回的是被创建类的Class对象
- 方法三: 没有被重写,因为它已经给了默认值 true(单例,false为非单例),作用是设置对象是否为单例
在Spring的配置文件中进行配置
<bean id="userDao" class="UserDaoFactoryBean类路径"/>
Bean的生命周期
bean 的生命周期:bean 对象,从创建到消亡的完整过程
bean 生命周期控制的是 bean 对象从创建后到销毁前做一些事情
关于 Spring 中对 bean 生命周期控制提供了两种方式:
- 在配置文件中的 bean 标签中添加
init-method
属性和destory-method
属性 - 类实现
InitializingBean
接口和DisposableBean
接口
添加初始化和销毁方法
<bean id="bookDao" class="com.XXX.dao.impl.BookDaoImpl" init-method="init"
destroy-method="destory"/>
init-method 属性是初始化方法,而 destroy-method 属性是销毁的方法
但是执行后只会执行初始化的 init 方法而未执行销毁方法destory,为什么?
原因是 Spring 的 IOC 容器是运行在 JVM 中,运行 main 方法后,JVM 启动,Spring 加载配置文件生成 IOC 容器,从容器获取 bean 对象,然后调方法执行,main 方法执行完后,JVM 退出,这个时候 IOC 容器中的 bean 还未来得及销毁就已经结束了,所以没有调用对应的 destory 方法
close关闭容器
ApplicationContext 中没有 close 方法,所以需要将 ApplicationContext 更换为 ClassPathXmlApplicationContext(ClassPathXmlApplicationContext 是 ApplicationContext 的子类) 来调用 close 方法,这样就可以正常的执行容器的销毁了
注册钩子关闭容器
在容器未关闭之前,提前设置好回调函数,让 JVM 在退出之前回调此函数来关闭容器,调用 registerShutdownHook 方法
注意: RegisterShutdownHook 方法在 ApplicationContext 中也没有,所以依然要使用 ClassPathXmlApplicationContext
关闭方式的区别
close 方法和 RegisterShutdownHook 方法区别:
- 相同点:两种方法都能用来关闭容器
- 不同点:close 方法是在执行调用的时候关闭,registerShutdownHook 方法是在 JVM 退出前自动调用关闭
通过接口来简化关闭容器
在实现类中实现 InitializingBean 和 DisposableBean 两个接口之后重写 afterPropertiesSet 方法和 destory 方法
注意: InitializingBean 接口中的 afterPropertiesSet 方法,翻译过来为 属性设置之后,对于 servce 层中的实现类方法来说,Dao 层的实现类为它的一个属性,setxxxDao 方法是 Spring 的 IOC 容器为它注入属性的方法,而setxxxDao方法先执行,afterPropertiesSet 方法后执行
总结:
对于 bean 的生命周期控制在 bean 的整个生命周期中所处的位置
- 初始化容器
- 1.创建对象(内存分配)
- 2.执行构造方法
- 3.执行属性注入(set操作)
- 4.执行 bean 初始化方法
- 使用 bean
- 执行业务操作
- 关闭/销毁容器
- 执行 bean 销毁方法
DI
Spring 中提供了两种注入方式:
- setter 注入
- 简单类型(基础数据类型和字符串)
- 引用类型
- 构造器注入
- 简单类型(基础数据类型和字符串)
- 引用类型
setter注入
-
在 bean 中定义引用类型属性,并提供可访问的 set 方法
public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
-
配置中使用 property 标签 ref 属性注入引用类型对象
<bean id="bookService" class="BookServiceImpl类路径"> <property name="实现类中的bookDao的属性" ref="bean中的bookDao对象"/> </bean> <bean id="bookDao" class="BookDaoImpl类路径"/>
注入引用数据类型
-
在实现类中声明属性并提供 setter 方法
public class BookServiceImpl implements BookService{ private BookDao bookDao; private UserDao userDao;//声明属性 public void setUserDao(UserDao userDao) {//提供setter方法 this.userDao = userDao; } public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
-
配置文件中进行注入配置
<bean id="bookDao" class="BookDaoImpl类路径"/> <bean id="userDao" class="UserDaoImpl类路径"/> <bean id="bookService" class="BookServiceImpl类路径"> <property name="实现类中的bookDao属性" ref="bean中的bookDao对象"/> <property name="实现类中的userDao属性" ref="bean中的userDao对象"/> </bean>
注入简单数据类型
-
在实现类中声明对应的简单数据类型的属性,并提供对应的 setter 方法
public class BookDaoImpl implements BookDao { private String databaseName;//声明属性 private int connectionNum;//声明属性 // 提供 setter 方法 public void setConnectionNum(int connectionNum) { this.connectionNum = connectionNum; } public void setDatabaseName(String databaseName) { this.databaseName = databaseName; } }
-
在配置问文件中进行注入配置
<bean id="bookDao" class="com.XXX.dao.impl.BookDaoImpl"> <property name="实现类方法中的参数databaseName(形参)" value="形参中的数据值"/> <property name="实现类方法中的参数connectionNum(形参)" value="形参中的数据值"/> </bean> <bean id="userDao" class="com.XXX.dao.impl.UserDaoImpl"/> <bean id="bookService" class="BookServiceImpl类路径"> <property name="实现类中的bookDao属性" ref="bean中的bookDao对象"/> <property name="实现类中的userDao属性" ref="bean中的userDao对象"/> </bean>
构造器注入
构造器注入也就是构造方法注入,无非是把实现类中的 setter 方法改为构造器方法
-
删除实现类中的 setter 方法并提供构造方法
public class BookServiceImpl implements BookService{ private BookDao bookDao; // 在构造方法中对属性进行设置 public BookServiceImpl(BookDao bookDao) { this.bookDao = bookDao; } }
-
在配置文件中配置构造方法注入
<bean id="bookDao" class="BookDaoImpl类路径"/> <bean id="bookService" class="BookServiceImpl类路径"> <constructor-arg name="实现类中构造方法的参数bookDao(形参)" ref="bean中的bookDao对象"/> <!-- 这里的constructor-arg标签就是通过构造方法注入的标签 --> </bean>
注意:
- name 属性对应的值为构造函数中方法形参的参数名,必须保持一致
- ref 属性指向的是 spring 的 IOC 容器中其他的 bean 对象
构造器注入多个数据类型
-
提供多个属性的构造函数
public class BookServiceImpl implements BookService{ private BookDao bookDao;// 声明引用类型属性 private UserDao userDao;// 声明引用类型属性 // 构造方法注入 public BookServiceImpl(BookDao bookDao,UserDao userDao) { this.bookDao = bookDao; this.userDao = userDao; } }
-
配置文件中配置
<bean id="bookDao" class="BookDaoImpl类路径"/> <bean id="userDao" class="UserDaoImpl类路径"/> <bean id="bookService" class="BookServiceImpl类路径"> <constructor-arg name="实现类中构造方法的参数bookDao" ref="bean中的bookDao对象"/> <constructor-arg name="实现类中构造方法的参数userDao" ref="bean中的userDao对象"/> </bean>
注意: constructor-arg 标签的顺序可以任意
构造器注入多个简单数据类型
-
添加多个简单属性并提供构造方法
public class BookDaoImpl implements BookDao { private String databaseName; //声明简单类型属性 private int connectionNum; //声明简单类型属性 // 通过构造器注入 public BookDaoImpl(String databaseName, int connectionNum) { this.databaseName = databaseName; this.connectionNum = connectionNum; } }
-
配置文件中配置
<bean id="bookDao" class="BookDaoImpl类路径"> <constructor-arg name="实现类中构造方法的参数databaseName" value="参数的值"/> <constructor-arg name="实现类中构造方法的参数connectionNum" value="参数的值"/> </bean> <bean id="userDao" class="UserDaoImpl类路径"/> <bean id="bookService" class="BookServiceImpl类路径"> <constructor-arg name="实现类中构造方法的参数bookDao" ref="bean中的bookDao对象"/> <constructor-arg name="实现类中构造方法的参数userDao" ref="bean中的userDao对象"/> </bean>
解决构造函数中参数名紧耦合
当构造函数中方法的参数名发生变化后,配置文件中的name属性也需要跟着变 ,两块存在紧耦合
解决方法:
方式一:删除 name 属性,添加 type 属性,按照类型注入
<bean id="bookDao" class="BookDaoImpl类路径">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
- 这种方式可以解决构造函数形参名发生变化带来的耦合问题
- 但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了
方式二:删除 type 属性,添加 index 属性,按照索引下标注入,下标从0开始
<bean id="bookDao" class="BookDaoImpl类路径">
<constructor-arg index="1" value="100"/>
<constructor-arg index="0" value="mysql"/>
</bean>
- 这种方式可以解决参数类型重复问题
- 但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题
介绍完这两种参数的注入方式,具体我们该如果选择?
- 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致 null 对象出现
- 强制依赖指对象在创建的过程中必须要注入指定的参数
- 可选依赖使用 setter 注入进行,灵活性强
- 可选依赖指对象在创建过程中注入的参数可有可无
- Spring 框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
- 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用 setter 注入完成可选依赖的注入
- 实际开发过程中还要根据实际情况分析,如果受控对象没有提供 setter 方法就必须使用构造器注入
- 自己开发的模块推荐使用 setter 注入
自动装配
相对于之前的手动配置比较麻烦,Spring 提供了自动装配的方式
什么是依赖自动装配
IOC 容器根据 bean 所依赖的资源在容器中自动查找并注入到 bean 中的过程称为自动装配
自动装配的方式有哪些?
- 按类型(常用)
- 按名称
- 按构造方法
- 不启用自动装配
按照类型完成自动装配的配置
自动装配只需要修改 xml 配置文件即可
- 在 bean 标签中移除 property 标签
- 在 bean 标签中添加 autowire 属性
按照类型注入的配置
<bean class="BookDaoImpl类路径"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="BookServiceImpl类路径" autowire="byType"/>
注意:
- 需要注入的属性的类中对应属性的 setter 方法不能省略
- 被注入的对象必须要被 Spring 的 IOC 容器管理
- 按照类型在 Spring 的 IOC 容器中如果找到多个对象,会报
NoUniqueBeanDefinitionExcepetion
按照名称完成自动注入装配的配置
如果一个类型在 IOC 中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式:
<bean class="BookDaoImpl类路径"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="BookServiceImpl类路径" autowire="byName"/>
注意:
-
按照名称注入中的名称是指对象实现类中的 set 方法的名称去掉 set 后剩下的部分的首字母小写的名称
-
以上图为例,因为 bookDao 是 private 修饰的,外部类无法直接访问,而外部类访问只能通过属性的 set 方法进行访问
-
而 set 方法生成的默认规则是,set 方法把属性名的首字母大写前面加上 set 形成方法名,所以按照名称注入,其实是和对应的 set 方法有关,但是如果按照标准起名称,属性名和 set 对应的名是一致的
-
如果按照名称去找对应的 bean 对象,找不到则注入 Null
-
当某一个类型在 IOC 容器中有多个对象,按照名称注入只找其指定名称对应的 bean 对象,不会报错
注意事项(配置特征)
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作
- 使用按类型装配时(byType)必须保障容器中相同类型的 bean 唯一
- 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合
- 自动装配优先级低于 setter 注入与构造器注入,同时出现时自动装配配置失效
集合注入
数据还有一种类型是集合,而 Spring 当然也支持集合的注入
Spring 中支持的集合类型
-
Array
-
<!-- 注入数组类型数据 --> <property name="array"> <array> <value>100</value> <value>200</value> <value>300</value> </array> </property>
-
-
List
-
<!-- 注入List类型数据 --> <property name="list"> <list> <value>caixunkun</value> <value>xiaoheizi</value> <value>ikun</value> <value>rap</value> </list> </property>
-
-
Set
-
<!-- 注入Set类型数据 --> <property name="set"> <set> <value>caixunkun</value> <value>xiaoheizi</value> <value>ikun</value> <value>rap</value> </set> </property>
-
-
Map
-
<!-- 注入Map类型数据 --> <property name="map"> <map> <entry key="country" value="china"/> <entry key="province" value="henan"/> <entry key="city" value="kaifeng"/> </map> </property>
-
-
Properties
-
<!-- 注入Properties类型数据 --> <property name="properties"> <props> <prop key="country">china</prop> <prop key="province">henan</prop> <prop key="city">kaifeng</prop> </props> </property>
-
注意:
- property 标签是 bean 标签下的子标签
- property 标签表示 setter 方式注入,构造方法注入 construstor-arg 标签内部也可以写array、list、set、map 标签
- List 底层也是通过数组实现的,所以 list 和 array 标签是可以混用的
- 集合中要添加引用类型,只需要把 value 标签改成 ref 标签,这种方式用的比较少
加载 properties 文件
对于有些数据写在配置文件中不利于后期维护,那么需要将这些值提取到一个外部的 properties 文件中,然后让 Spring 框架从外部 properties 文件中读取属性值
-
准备 properties 配置文件
-
resource 下创建一个 xxx.properties 文件,并添加对应的属性键值对,比如:
-
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db jdbc.username=root jdbc.password=root
-
-
开启 context 命名空间
-
在 applicationContext.xml(也就是 bean 对象定义的配置文件)中开启
context
命名空间 -
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
-
-
加载 properties 配置文件
-
<context:property-placeholder location="jdbc.properties"/>
-
在配置文件中使用
context
命名空间下的标签来加载 properties 配置文件
-
-
完成属性注入
-
使用 ${key} 来读取 properties 配置文件中的内容并完成属性注入
-
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <!-- 开启context空间 --> xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd <!-- 开启context空间 --> http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 加载配置文件 --> <context:property-placeholder location="jdbc.properties"/> <!-- 属性注入 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
-
读取单个属性
-
在实现类中添加对应的属性和属性的设置(setter)方法
- 这里实现类用 BookDao 的实现类 BookDaoImpl
-
完成配置文件的读取与注入
- 在 applicationContext.xml 添加配置,bean的配置管理、读取外部properties、依赖注入:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 读取外部配置文件 --> <context:property-placeholder location="jdbc.properties"/> <bean id="bookDao" class="BookDaoImpl类路径"> <!-- 属性注入 --> <property name="name" value="${jdbc.driver}"/> </bean> </beans>
注意事项:
-
问题一:properties 配置文件中,配置键值对的优先级问题
-
在配置文件中 key 键为 username 这个使用会有问题,假设给其设置的 value 值为 root666,而打印出来的则不是 root666 而是自己电脑的用户名
-
因为 context:property-placeholer 标签会加载系统的环境变量,而环境变量的值会优先加载
-
要解决需要在 context:property-placeholer 标签中加一个 system-properties-mode 属性并给他值设置为 NEVER,当然还有就是不使用
username
作为属性的key
-
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
-
-
问题二:当有多个 properties 配置文件需要被加载
-
调整配置文件内容,在 resource 下添加多个配置文件
-
修改 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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--方式一--> <context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/> <!--方式二--> <context:property-placeholder location="*.properties" systemproperties-mode="NEVER"/> <!--方式三--> <context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/> <!--方式四--> <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/> </beans>
-
注意:
-
方式一:如果配置文件多的话,需要每个都配置
-
方式二::
*.properties
代表所有以properties
结尾的文件都会被加载,可以解决方式一的问题,但是不标准 -
方式三:标准的写法,
classpath:
代表的是从根路径下开始查找,但是只能查询当前项目的根路径 -
方式四:不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的
properties
配置文件
-
核心容器
这里说的核心容器可以简单的理解为ApplicationgContext
容器
容器的创建方法有两种:
-
ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);
- 翻译过来为:类路径下的 XML 配置文件
-
ApplicationContext ctx = new FileSystemXmlApplicationContext(“D:\workspace\spring\spring_10_container\s rc\main\resources\applicationContext.xml”);
- 翻译过来为:文件系统下的 XML 配置文件
- 该方式是从项目路径下开始查找
applicationContext.xml
,所以要把括号内参数修改为绝对路径(也就是从盘符开始写起)
Bean 的三种获取方式
-
方式一:
-
BookDao bookDao = (BookDao) ctx.getBean(“bookDao”);
-
这种方式存在的问题就是获取的时候都需要进行类型转换
-
-
方式二:
-
BookDao bookDao = ctx.getBean(“bookDao”, BookDao.class);
-
这种方式可以解决类型强转问题,但是参数又多加了一个,相对来说没有简化多少
-
-
方式三:
-
BookDao bookDao = ctx.getBean(BookDao.class);
-
这种方式就类似依赖注入中的按类型注入,必须要确保 IOC 容器中该类型对应的 bean 对象只能有一个。
-
Bean 标签属性的总结
容器类层次结构
在 IDEA 中双击shift
,输入 BeanFactory,点击进入 BeanFactory 类,ctrl+h,就能查看到如下结构的层次关系中可以看出,容器类也是从无到有根据需要一层层叠加上来的,BeanFactory 是 IoC 容器的顶层接口,ApplicationContext 接口是 Spring 容器的核心接口,接口提供基础的 bean 操作相关方法,通过其他接口扩展其功能
BeanFactory的使用
使用BeanFactory来创建IOC容器的具体实现方式为:
public class AppForBeanFactory {
public static void main(String[] args) {
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean(BookDao.class);
bookDao.save();
}
}
BeanFactory 和 ApplicationContext 区别:
- BeanFactory 是延迟加载,只有在获取 bean 对象的时候才会去创建
- ApplicationContext 是立即加载,容器加载的时候就会创建 bean 对象
ApplicationContex 要想称为延迟加载,只需要如下配置
<?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标签下使用 lazy-init属性,值设置为true -->
<bean id="bookDao" class="BookDaoImpl类路径" lazy-init="true"/>
</beans>
依赖注入相关总结:
IOC/DI注解开发
在 Spring 发展到2.0的时候提供了注解开发,而且通过配置文件的开发发现太过于繁琐,那么接下来是 Spring2.5 的注解开发
注解开发定义bean
步骤:
-
删除原 XML 配置(将配置文件中的 bean 标签删除掉)
<bean id="bookDao" class="BookDaoImpl实现类"/>
-
Dao 上添加注解
@Component("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } }
- 注意: @Componet注解不可以添加在接口上,因为接口是无法创建对象的
- XML与注解配置的对应关系:
-
配置 Spring 的注解包扫描
-
为了让 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"> <context:component-scan base-package="com.xxx"/> </beans>
-
说明:
- component-scan
- component:组件,Spring 将管理的 bean 视作自己的一个组件
- scan:扫描
- base-package 指定 Spring 框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
- 包路径越多[如:com.XXX.dao.impl],扫描的范围越小速度越快
- 包路径越少[如:com.XXX],扫描的范围越大速度越慢
- 一般扫描到项目的组织名称即Maven的groupId下[如:com.XXX]即可。
-
-
Service 上添加注解
-
在 BookServiceImpl 类上也添加
@Component
交给 Spring 框架管理 -
@Component public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public void save() { System.out.println("book service save ..."); bookDao.save(); } }
-
@Component
注解如果不起名称,会有一个默认值就是当前类名首字母小写,所以也可以按照名称获取,如 -
BookService bookService = (BookService)ctx.getBean("bookServiceImpl");
-
对于
@Component
注解,还衍生出了其他三个注解@Controller
、@Service
、@Repository
通过查看源码会发现: -
这三个注解和@Component注解的作用是一样的,方便后期在编写类的时候能很好的区分出这个类是属于表现层、业务层还是数据层的类。
-
名称 @Component/@Controller/@Service/@Repository 类型 类注解 位置 类定义上方 作用 设置该类为 spring 管理的 bean 属性 value(默认):定义 bean 的id
-
纯注解开发模式
上面虽然是通过注解简化了一些操作,但是还是有配置文件,显得繁琐,Spring3.0 提供了纯注解开发,使用 Java 类替代配置文件,开启了 Spring 快速开发
-
创建配置类
-
标识该类为配置类
@Configuration //配置类注解,标识了该类为配置类,用来替代applicationContext.xml public class SpringConfig {//配置类 }
-
用注解替换包扫描配置
//在配置类上添加包扫描注解@ComponentScan替换application.xml文件中的<context:component-scan base-package=""/> @Configuration @ComponentScan("com.XXX") public class SpringConfig { } // application.xml文件就可以退休了
至此,纯注解开发方式已完成,主要包括:
-
Java 类替换 Spring 核心配置文件
-
@Configuration 注解用于设定当前类为配置类
-
@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据用数组格式
@ComponentScan({com.XXX.service","com.XXX.dao"})
-
读取 Spring 核心配置文件初始化容器对象切换为读取 Java 配置类初始化容器对象
//加载配置文件初始化容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //加载配置类初始化容器 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
-
名称 @Configuration 类型 类注释 位置 类定义上方 作用 设置该类为 Spring 配置类 位置 value(默认值):定义 bean 的id -
名称 @ComponentScan 类型 类注解 位置 类定义上方 作用 设置 Spring 配置类扫描路径,用于加载使用注解格式定义的 bean 位置 value(默认值):扫描路径,此路径可以逐层向下扫描 -
applicationContext.xml 中
<context:component-san/>
的作用是指定扫描包路径,注解为@ComponentScan -
@Configuration标识该类为配置类,使用类替换applicationContext.xml文件
-
ClassPathXmlApplicationContext 是加载XML配置文件
-
AnnotationConfigApplicationContext 是加载配置类
注解开发bean作用范围与生命周期管理
作用范围主要是指是否是单例模式,而生命周期则是指初始化和销毁的方法定义
bean的作用范围
使用@Scope
注解
@Repository
//@Scope设置bean的作用范围
@Scope("prototype")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
名称 | @Scope |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置该类创建对象的作用范围,可用于设置创建出的 bean 是否是单例对象 |
属性 | value:定义 bean 作用范围,默认值 singleton(单例),可选填 prototype(非单例) |
bean的生命周期
-
在 BookDaoImpl 中添加两个方法,
init
和destroy
,方法名可以任意 -
在对应的方法上添加
@PostConstruct
和@PreDestroy
注解即可@Repository public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..."); } @PostConstruct //在构造方法之后执行,替换 init-method public void init() { System.out.println("init ..."); } @PreDestroy //在销毁方法之前执行,替换 destroy-method public void destroy() { System.out.println("destroy ..."); } }
注意:
-
@PostConstruct
和@PreDestory
注解如果找不到,需要导入以下 Jar 包<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
-
因为从 JDK9之后 jdk 中的 javax.annotation 包被移除了,这两个注解刚好就在这个包中
名称 | @PostConstruct |
---|---|
类型 | 方法注解 |
位置 | 方法上 |
作用 | 设置该方法为初始化方法 |
属性 | 无 |
名称 | @PreDestroy |
---|---|
类型 | 方法注解 |
位置 | 方法上 |
作用 | 设置该方法为销毁方法 |
属性 | 无 |
注解开发依赖注入
Spring 为了使用注解简化开发,并没有提供构造函器注入、setter注入对应的注解,只提供了自动装配的注解实现。
注解实现按照类型注入
在 BookServiceImpl 类的 bookDao 属性上添加@Autowired
注解
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
// public void setBookDao(BookDao bookDao) {
// this.bookDao = bookDao;
// }
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
注意:
@Autowired
可以写在属性上,也可以写在 setter 方法上,最简单的处理方式是写在属性上并将setter
方法注释掉- 自动装配是基于反射创建对象并通过暴力反射为私有属性进行设值
- 普通反射只能获取 public 修饰的内容
- 暴力反射除了获取 public 修饰的内容还可以获取 private 修饰的内容,所以无需提供 setter 方法
@Autowired
是按照类型注入,那么对应 BookDao 接口如果有多个实现类,或导致报错,那么就需要使用按照名称注入
名称 | @Autowired |
---|---|
类型 | 属性注解或方法注解或方法形参注解 |
位置 | 属性定义上方或标准 set 方法上方或类 set 方法上方或方法形参前面 |
作用 | 为引用类型属性设置值 |
属性 | required:true/false,定义该属性是否允许为null |
注解实现按照名称注入
当根据类型在容器中找到多个 bean,注入参数的属性名又和容器中bean的名称不一致,这个时候就需要使用到@Qualifier
来指定注入哪个名称的bean对象。
@Service
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao1")
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
注意: @Qualifier
注解后的值就是需要注入的bean的名称,@Qualifier
不能单独使用,必须和@Autowired
一起使用
名称 | @Qualifier |
---|---|
类型 | 属性注解或方法注解 |
位置 | 属性定义上方或标准 set 方法上方或类 set 方法上方或 |
作用 | 为引用类型属性指定注入的 beanId |
属性 | value:设置注入的 beanId |
简单数据类型注入
简单类型注入的是基本数据类型或者字符串类型
假设在 BookDaoImpl 类中添加一个 name 属性,用其进行简单类型注入
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
数据类型换了,对应的注解也要跟着换,这次使用@Value
注解,将值写入注解的参数中就行了
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
@Value("XXXX")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
注意: 数据格式要匹配,如将 “abc” 注入给 int 值,这样程序就会报错。
名称 | @Value |
---|---|
类型 | 属性注解或方法注解 |
位置 | 属性定义上方或标准 set 方法上方或类 set 方法上方或 |
作用 | 为基本数据类型或字符串类型属性设置值 |
属性 | value:要注入的属性值 |
注解读取 properties 配置文件
@Value
一般会被用在从 properties 配置文件中读取内容进行使用
接下来进行演示
-
准备 properties 文件
//jdbc.properties name=XXXX888
-
加载 properties 文件
@Configuration @ComponentScan("com.XXX") @PropertySource("jdbc.properties") public class SpringConfig { }
-
使用
value
读取配置文件中的内容@Repository("bookDao") public class BookDaoImpl implements BookDao { @Value("${name}") private String name; public void save() { System.out.println("book dao save ..." + name); } }
注意:
-
如果读取的 properties 配置文件有多个,可以使用
@PropertySource
的属性来指定多个@PropertySource({"jdbc.properties","xxx.properties"})
-
@PropertySource
注解属性中不支持使用通配符*
,运行会报错的@PropertySource({"*.properties"})
-
@PropertySourcez
注解属性中可以把classpath:
加上,代表从当前项目的根路径找文件,而且也不可以加上*
@PropertySource({"classpath:jdbc.properties"})
名称 | @PropertySource |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 加载 properties 文件中的属性值 |
属性 | value:设置加载的 properties 文件对应的文件名或文件名组成的数组 |
IOC/DI注解开发管理第三方bean
对于第三方在 jar 包中的类,需要使用@Bean
注解
注解开发管理第三方bean
以对Druid
数据源的管理为例
-
导入 Druid 对应的 jar 包
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
-
在配置类中添加一个方法
@Configuration public class SpringConfig { public DataSource dataSource(){ //该方法的返回值就是要创建的Bean对象类型 DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); ds.setUsername("root"); ds.setPassword("root"); return ds; } }
-
在方法上添加
@Bean
注解@Configuration public class SpringConfig { @Bean //@Bean注解的作用是将方法的返回值制作为Spring管理的一个bean对象 public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); ds.setUsername("root"); ds.setPassword("root"); return ds; } }
注意: 不能使用DataSource ds = new DruidDataSource()
接口来实例化,因为 DataSource 接口中没有对应的 setter 方法来设置属性。
名称 | @Bean |
---|---|
类型 | 方法注解 |
位置 | 方法定义上方 |
作用 | 设置该方法的返回值作为 Spring 管理的 bean |
属性 | value:定义 bean 的 id |
引入外部配置类
如果把所有的第三方 bean 都配置到 Spring 的配置类 SpringConfig 中,虽然可以,但是不利于代码阅读和分类管理,可以按照类别将这些 bean 配置到不同的配置类中
比如:
public class JdbcConfig {
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
这个配置类如何能被 Spring 配置类加载到,并创建 DataSource 对象在 IOC 容器中
使用包扫描引入
-
在 Spring 配置类上添加包扫描
@Configuration @ComponentScan("com.XXX.config") public class SpringConfig { }
-
在 JdbcConfig 上添加配置注解
@Configuration public class JdbcConfig { @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); ds.setUsername("root"); ds.setPassword("root"); return ds; } }
这种方式虽然能够扫描到,但是不能很快的知晓都引入了哪些配置类
使用@Import注解引入
-
去除 JdbcConfig 类上的注解
public class JdbcConfig { @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("com.mysql.jdbc.Driver"); ds.setUrl("jdbc:mysql://localhost:3306/spring_db"); ds.setUsername("root"); ds.setPassword("root"); return ds; } }
-
在 Spring 配置类中引入
@Configuration //@ComponentScan("com.XXX.config") @Import({JdbcConfig.class}) public class SpringConfig { }
注意:
-
@Import
参数需要的是一个数组,可以引入多个配置类。 -
@Import
注解在配置类中只能写一次,下面的方式是不允许的@Configuration //@ComponentScan("com.XXX.config") @Import(JdbcConfig.class) @Import(Xxx.class) public class SpringConfig { }
名称 | @Import |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 导入配置类 |
属性 | value:定义导入的配置类类名,当配置类有多个时使用数组格式一次性导入多个配置类 |
注解开发实现为第三方bean注入资源
在使用@Bean创建bean对象的时候,如果方法在创建的过程中需要其他资源,这些资源会有两大类,分别是简单数据类型和引用数据类型
以下面 Durid 的管理为例
public class JdbcConfig {
@Bean
public DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
}
简单数据库类型
-
类中提供四个属性
-
使用
@Value
注解引用public class JdbcConfig { @Value("com.mysql.jdbc.Driver") private String driver; @Value("jdbc:mysql://localhost:3306/spring_db") private String url; @Value("root") private String userName; @Value("password") private String password; @Bean public DataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } }
但是一般数据库连接的四要素是写在 properties 配置文件中的
- resources目录下添加 jdbc.properties
- 配置文件中提供四个键值对分别是数据库的四要素
- 使用
@PropertySource
加载 jdbc.properties 配置文件 - 修改
@Value
注解属性的值,将其修改为 ${key},key 就是键值对中的键的值
引用数据类型
假设在构建 DataSource 对象的时候,需要用到 BookDao 对象,该如何把 BookDao 对象注入进方法内
-
在 SpringConfig 中扫描 BookDao
//扫描的目的是让Spring能管理到BookDao,也就是说要让IOC容器中有一个bookDao对象 @Configuration @ComponentScan("com.XXX.dao") @Import({JdbcConfig.class}) public class SpringConfig { }
-
在 JdbcConfig 类的方法上添加参数
@Bean public DataSource dataSource(BookDao bookDao){//直接将需要的类对象作为参数,写在方法的参数位置 System.out.println(bookDao); DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driver); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(password); return ds; } //引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。