Spring学习笔记 - 第二章 - 注解开发、配置管理第三方Bean、注解管理第三方Bean、Spring 整合 MyBatis 和 Junit 案例
Spring 学习笔记全系列传送门:
1、IoC/DI 配置管理第三方bean
本节内容的主要内容是:如果有需求让我们去管理第三方jar包中的类,该如何管理
1.1 案例:数据源对象管理
1.1.1 环境准备
-
pom.xml 中添加依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies>
-
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"> </beans>
-
运行类 App
package priv.dandelion; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); } }
1.1.2 思路分析
注意:思路 中加粗部分,如何理解、如何做?
- 第三方类:DuridDataSource
- 如何注入:setter注入
- 需求:使用 Spring 的 IoC 容器管理 Druid 连接池对象
- 思路
- 使用第三方技术,需要在 pom.xml 中添加依赖
- 在配置文件中将第三方的类制作成一个Bean,让 IoC 容器进行管理
- 数据库连接需要基础的四要素
驱动
、连接
、用户名
和密码
,如何注入到对应的bean中 - 从IOC容器中获取对应的bean对象,将其打印到控制台查看结果
1.1.3 实现 Druid 管理
-
导入
druid
的依赖 -
在 Spring 配置文件中配置第三方 bean
<!-- 管理 DuridDataSource 对象 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <!-- 通过查询 DruidDataSource 具体实现发现需要使用 setter 注入 --> <property name="driverClassName" value="com.mysql.jdbc.driver"/> <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean>
-
从 IOC 容器中获取对应的 bean 对象
public class App { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); DataSource dataSource = (DataSource) ctx.getBean("dataSource"); System.out.println(dataSource); } }
1.1.4 实现 C3P0 管理
注意:
- 数据连接池在配置属性的时候,除了可以注入数据库连接四要素外还可以配置很多其他的属性,具体都有哪些属性用到的时候再去查,一般配置基础的四个,其他都有自己的默认值
- Druid和C3P0在没有导入mysql驱动包的前提下,一个没报错一个报错,说明Druid在初始化的时候没有去加载驱动,而C3P0刚好相反
- Druid程序运行虽然没有报错,但是当调用DruidDataSource的getConnection()方法获取连接的时候,也会报找不到驱动类的错误
-
添加依赖
可从mvn的仓库
https://mvnrepository.com/
中进行搜索<!-- https://mvnrepository.com/artifact/c3p0/c3p0 --> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency>
-
配置第三方Bean
<bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_db"/> <property name="user" value="root"/> <property name="password" value="123456"/> </bean>
-
到上面为止运行时会报错,因为必须依赖mysql驱动
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
1.2 加载 properties 文件
1.2.1 实现步骤
-
开启 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">
-
使用 context 命名空间,加载指定的 properties 文件
- system-properties-mode="NEVER" 不加载系统环境变量,防止username冲突
- 多个配置文件的加载使用
,
隔开(非规范方式) - 多个配置文件的加载也可以使用
classpath:*.properties
(规范格式,但仅限于当前项目的 properties 文件) classpath*:*.properties
(规范格式,包含当前项目和所导入的 jar 包中的 properties 文件,推荐使用)
<!-- system-properties-mode="NEVER" 不加载系统环境变量,防止username冲突 --> <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
-
使用属性占位符
${}
读取加载的属性值<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>
1.2.2 完整代码
-
Spring 配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 管理 DuridDataSource 对象 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.driver"/> <property name="url" value="jdbc:mysql://localhost:3306/spring_db"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> --> <!-- system-properties-mode="NEVER" 不加载系统环境变量,防止username冲突 --> <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/> <!-- 使用 properties 管理 DuridDataSource 对象 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
-
Properties 文件
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=mysql://127.0.0.1:3306/spring_db jdbc.username=root jdbc.password=123456
2、核心容器
2.1 环境准备
-
依赖
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> </dependencies>
-
BookDao
仅
save()
方法,细节不表 -
配置文件
<?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="bookDao" class="priv.dandelion.dao.impl.BookDaoImpl"/> </beans>
-
运行类
public class App { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); BookDao bookDao = (BookDao) ctx.getBean("bookDao"); bookDao.save(); } }
2.2 容器
2.2.1 容器的创建方式
-
加载类路径下的配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
-
从文件系统下加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("绝对路径");
-
加载类路径下的多个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("xml1.xml", "xml2.xml");
2.2.2 Bean 的三种获取方式
-
强转
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
-
将类型作为参数
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
-
按类型获取(类似依赖注入的按类型注入)
BookDao bookDao = ctx.getBean(BookDao.class);
2.2.3 BeanFactory 的使用与延迟加载
2.2.3.1 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(); } }
2.2.3.2 延迟加载
可尝试手动实现 BookDao 的构造方法并输出内容,运行 ApplicationContext 和 BeanFactory 对比结果
-
BeanFactory 是延迟加载,只有在获取 bean 对象的时候才会去创建
-
ApplicationContext 是立即加载,容器加载的时候就会创建 bean 对象
-
ApplicationContext 要想成为延迟加载,只需要在配置文件中按照如下方式进行配置
<bean id="bookDao" class="priv.dandelion.dao.impl.BookDaoImpl" lazy-init="true"/>
2.2 核心容器总结
2.2.1 容器相关
2.2.2 Bean 相关
-
<bean/>
标签的常用属性及其作用属性 作用 id="BookDao" bean 的 id name= bean 的 别名 class="priv.dandelion.dao.impl.BookDaoImpl" bean 的类型,静态工厂类,FactoryBean 类 scope="singleton" 控制 bean 的实例数量 init-method="方法名" 生命周期初始化方法 destroy-method="方法名" 生命周期销毁方法 autowire="byType" 自动装配类型 factory-method="getInstance" bean 工厂方法,应用于静态工厂或实例工厂 factory-bean="priv.dandelion.factory.BookDaoFactory" 实例工厂 bean lazy-init="true" 控制 bean 延迟加载
2.2.3 依赖注入相关
<bean id="bookService" class="priv.dandelion.service.impl.BookServiceImpl">
<!--
构造器注入,一般用在第三方技术整合
-->
<!-- 构造器注入引用类型,注意此处的 ref 属性值为构造方法的形参名 -->
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
<!-- 构造器注入简单数据类型 -->
<constructor-arg name="databaseName" value="mysql"/>
<constructor-arg name="connectionNum" value="10"/>
<!-- 类型匹配与索引匹配,用于解耦 -->
<constructor-arg type="int" value="10"/>
<constructor-arg index="0" value="mysql"/>
<!--
setter注入,自己开发的内容多用该方式
-->
<!-- 注入引用数据类型 -->
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
<!-- 注入简单数据类型 -->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
<!-- 注入集合类型 -->
<property name="list">
<list>
<!-- 集合注入简单数据类型 -->
<value>dandelion</value>
<!-- 集合注入引用数据类型 -->
<ref bean="bean的id"/>
</list>
</property>
</bean>
3、IoC/DI注解开发
3.1 环境准备
-
导入 Spring 依赖
-
BookDao 接口和实现类
-
BookService 接口和实现类
-
核心配置文件
<?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="bookDao" class="priv.dandelion.dao.impl.BookDaoImpl"/> </beans>
-
运行类
public class App { public static void main(String[] args) { // 获取IOC容器 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 获取Bean BookDao bookDao = (BookDao) context.getBean("bookDao"); bookDao.save(); } }
3.2 注解开发定义 Bean
3.2.1 流程
-
删除原 XML 配置
<bean id="bookDao" class="priv.dandelion.dao.impl.BookDaoImpl"/>
-
添加组件注解
@Component
Spring将管理的bean视作自己的一个组件
-
@Component
的 value 属性(可不指定)值为 bean 的 id@Component("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book priv.dandelion.dao save ..."); } }
-
@Component
也可以不声明id,加载时需要使用按类型访问@Component public class BookServiceImpl implements BookService { // 删除业务层中使用new的方式创建的dao对象 private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } // 提供所要创建成员对象的对应的set方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
-
-
配置 Spring 的注解包扫描
- component:组件,Spring将管理的bean视作自己的一个组件
- scan:扫描
- base-package:指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- <bean id="bookDao" class="priv.dandelion.dao.impl.BookDaoImpl"/>--> <!-- 配置扫描组件 --> <context:component-scan base-package="priv.dandelion"/> </beans>
-
运行类
-
BookServiceImpl类没有起名称,所以在App中是按照类型来获取bean对象
-
@Component注解如果不起名称,会有一个默认值就是
当前类名首字母小写
,所以也可以按照名称获取,如BookService bookService = (BookService)ctx.getBean("bookServiceImpl"); System.out.println(bookService);
public class App { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 注解中注明id,获取bean可按名称访问 BookDao bookDao = (BookDao) context.getBean("bookDao"); bookDao.save(); // 注解中没有注明id,获取bean需要按类型访问 BookService bookService = context.getBean(BookService.class); System.out.println(bookService); } }
-
3.2.2 相关知识点
3.2.2.1 @Component
的衍生
-
对于@Component注解,还衍生出了其他三个注解
@Controller
、@Service
、@Repository
这三个注解和@Component注解的作用是一样的,仅用于方便我们后期在编写类的时候能很好的区分出这个类是属于
表现层
、业务层
还是数据层
的类。 -
@Controller
:用于表现层 bean 定义 -
@Service
:用于业务层 bean 定义 -
@Repository
:用于数据层 bean 定义
3.2.2.2 @Component
相关内容归纳
名称 | @Component/@Controller/@Service/@Repository |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置该类为spring管理的bean |
属性 | value(默认):定义bean的id |
3.3 纯注解开发模式
3.3.1 思路分析
编写配置类,弃用Spring配置文件
3.3.2 实现步骤
-
删除配置文件
-
编写配置类
// 声明这是一个配置类 @Configuration // 配置包扫描,与原先的配置文件中一致 @ComponentScan("priv.dandelion") // @ComponentScan({"priv.dandelion.dao","priv.dandelion.service"}) public class SpringConfig { }
-
运行类
public class AppForAnnotation { public static void main(String[] args) { /* 原先使用配置文件时,需要使用ClassPathXmlApplicationContext()加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); */ // 加载配置类 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); // 注解中注明id,获取bean可按名称访问 BookDao bookDao = (BookDao) context.getBean("bookDao"); bookDao.save(); // 注解中没有注明id,获取bean需要按类型访问 BookService bookService = context.getBean(BookService.class); System.out.println(bookService); } }
3.3.3 相关知识点
3.3.3.1 @Configuration
-
@Configuration注解用于设定当前类为配置类
名称 @Configuration 类型 类注解 位置 类定义上方 作用 设置该类为spring配置类 属性 value(默认):定义bean的id
3.3.3.2 @ComponentScan
-
@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
名称 @ComponentScan 类型 类注解 位置 类定义上方 作用 设置spring配置类扫描路径,用于加载使用注解格式定义的bean 属性 value(默认):扫描路径,此路径可以逐层向下扫描
3.4 注解开发 Bean 作用范围和生命周期管理
3.4.1 Bean 的作用范围
-
在运行类中创建 BookDao 的两个 bean
运行结果:两个 bean 的地址一致
-
控制 Bean 的作用范围
@Repository("bookDao") // 设置为非单例(默认为单例) @Scope("prototype") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book priv.dandelion.dao save ..."); } }
-
@Scope
名称 @Scope 类型 类注解 位置 类定义上方 作用 设置该类创建对象的作用范围
可用于设置创建出的bean是否为单例对象属性 value(默认):定义bean作用范围,
默认值singleton(单例),可选值prototype(非单例)
3.4.2 Bean的生命周期
-
生命周期方法注解
- 初始化方法:
@PostConstruct
(即构造后) - 销毁方法:
@PreDestroy
(即销毁前)
- 初始化方法:
-
示例
-
Dao
@Repository("bookDao") @Scope("singleton") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book priv.dandelion.dao save ..."); } @PostConstruct public void init() { System.out.println("book priv.dandelion.dao init ..."); } @PreDestroy public void destroy() { System.out.println("book priv.dandelion.dao destroy ..."); } }
-
Main
public class App { public static void main(String[] args) { // 加载配置类 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); // 注解中注明id,获取bean可按名称访问 BookDao bookDao = (BookDao) context.getBean("bookDao"); bookDao.save(); // 手动关闭容器,以执行各项销毁 context.close(); /** * book priv.dandelion.dao init ... * book priv.dandelion.dao save ... * book priv.dandelion.dao destroy ... * * Process finished with exit code 0 */ } }
-
-
知识点整理
-
@PostConstruct
名称 @PostConstruct 类型 方法注解 位置 方法上 作用 设置该方法为初始化方法 属性 无 -
@PreDestroy
名称 @PreDestroy 类型 方法注解 位置 方法上 作用 设置该方法为销毁方法 属性 无
-
3.5 注解开发依赖注入
3.5.1 环境准备
-
配置类
// 声明这是一个配置类 @Configuration // 配置包扫描,与原先的配置文件中一致 @ComponentScan({"priv.dandelion.dao","priv.dandelion.service"}) public class SpringConfig { }
-
Dao 实现类
@Repository("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book priv.dandelion.dao save ..."); } @PostConstruct public void init() { System.out.println("book priv.dandelion.dao init ..."); } @PreDestroy public void destroy() { System.out.println("book priv.dandelion.dao destroy ..."); } }
-
Service 实现类
@Service public class BookServiceImpl implements BookService { private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
3.5.2 注解实现按照类型注入
先说结论和注意事项:
实现方式:使用
@Autowired
注解注意事项
@Autowired
可以写在属性上,也可也写在setter方法上
@Autowired
修饰的成员属性对应的 setter 可以删除普通反射只能获取public修饰的内容,而自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值。暴力反射除了获取public修饰的内容还可以获取private修改的内容,所以此处无需提供 setter 方法
@Autowired
是按照类型注入,当存在多个相同类型的不同 bean 时,就自动按照名称注入,但一般最好指定名称,将在 3.5.3 详细说明
-
示例
-
Service 实现类
@Service public class BookServiceImpl implements BookService { // 自动装配,按类型装配,采用暴力反射,不需要实现set方法 @Autowired private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
-
运行类
public class App { public static void main(String[] args) { // 加载配置类 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); // 注解中注明id,获取bean可按名称访问 BookDao bookDao = (BookDao) context.getBean("bookDao"); bookDao.save(); BookService bookService = context.getBean(BookService.class); bookService.save(); // 手动关闭容器,以执行各项销毁 context.close(); /** * book priv.dandelion.dao init ... * book priv.dandelion.dao save ... * book service save ... * book priv.dandelion.dao save ... * book priv.dandelion.dao destroy ... * * Process finished with exit code 0 */ } }
-
3.5.3 注解实现按照名称注入
先说结论和注意事项:
结论:使用
@Qualifier
注解注意事项
- 用于应对存在多个相同类型的 bean,首先需要为这些 bean 分别赋予名称,否则会报错
- 接 3.3.4,若存在多个相同类型的 bean,当使用
@Autowired
时,会按照名称进行注入,此时由于没有指定变量名,将默认采用该变量的变量名来进行匹配注入(如下面的代码块所示,此处为bookDao
)。但显而易见,这样会导致代码混乱且极易出错- 为避免出错,可以使用
@Qualifier
注解,在自动装配的情况下手动指定加载的 bean 的名称,该注解的 value 是 bean 的名称@Qualifier
注解必须搭配@Autowired
一起使用,不能单独使用3.3.4 中的 Service 代码展示
@Service public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
-
为同类型的不同的 bean 命名
@Repository("bookDao") public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } } // ========================================================= @Repository("bookDao2") public class BookDaoImpl2 implements BookDao { public void save() { System.out.println("book dao save ...2" ); } }
-
在自动装配的情况下手动指定加载的 bean 的名称
@Service public class BookServiceImpl implements BookService { // 自动装配,按类型装配,采用暴力反射,不需要实现set方法 @Autowired // 自动装配的情况下指定加载bean的名称 @Qualifier("bookDao") private BookDao bookDao; public void save() { System.out.println("book service save ..."); bookDao.save(); } }
3.5.4 简单数据类型注入
-
使用
@Value
注解实现,一般搭配外部配置文件使用,详见 3.5.5 -
示例
@Repository("bookDao") public class BookDaoImpl implements BookDao { @Value("dandelion") private String name; public void save() { System.out.println("book priv.dandelion.dao save1 ..." + name); } @PostConstruct public void init() { System.out.println("book priv.dandelion.dao init ..."); } @PreDestroy public void destroy() { System.out.println("book priv.dandelion.dao destroy ..."); } }
3.5.5 注解读取 properties 配置文件
-
添加配置文件
name=dandelion password=123456
-
在配置类中添加属性源
@PropertySource
注解注意:
- 若有多个配置文件则写作数组
- 文件名不能使用通配符
*
// 声明这是一个配置类 @Configuration // 配置包扫描,与原先的配置文件中一致 @ComponentScan({"priv.dandelion.dao","priv.dandelion.service"}) // 属性源 @PropertySource({"jdbc.properties","jdbc1.properties"}) public class SpringConfig { }
-
读取配置文件中的内容
@Repository("bookDao") public class BookDaoImpl implements BookDao { @Value("${name}") private String name; @Value("${password}") private String password; public void save() { System.out.println("book priv.dandelion.dao save1 ..." + name + " " + password); } @PostConstruct public void init() { System.out.println("book priv.dandelion.dao init ..."); } @PreDestroy public void destroy() { System.out.println("book priv.dandelion.dao destroy ..."); } }
3.5.6 依赖注入相关知识点
3.5.6.1 @Autowired
名称 | @Autowired |
---|---|
类型 | 属性注解 或 方法注解(了解) 或 方法形参注解(了解) |
位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 或 方法形参前面 |
作用 | 为引用类型属性设置值 |
属性 | required:true/false,定义该属性是否允许为null |
3.5.6.2 @Qualifier
名称 | @Qualifier |
---|---|
类型 | 属性注解 或 方法注解(了解) |
位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 |
作用 | 为引用类型属性指定注入的beanId |
属性 | value(默认):设置注入的beanId |
3.5.6.3 @Value
名称 | @Value |
---|---|
类型 | 属性注解 或 方法注解(了解) |
位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 |
作用 | 为 基本数据类型 或 字符串类型 属性设置值 |
属性 | value(默认):要注入的属性值 |
3.5.6.4 @PropertySource
名称 | @PropertySource |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 加载properties文件中的属性值 |
属性 | value(默认):设置加载的properties文件对应的文件名或文件名组成的数组 |
4、IoC/DI注解开发管理第三方bean
4.1 环境准备
-
Spring 依赖
-
配置类
@Configuration public class SpringConfig { }
-
Dao 实现类
@Repository public class BookDaoImpl implements BookDao { public void save() { System.out.println("book dao save ..." ); } }
-
运行类
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); } }
4.2 注解开发管理第三方 Bean
方式:使用自定义 Bean
- @Bean 注解的作用是将方法的返回值制作为 Spring 管理的一个 bean 对象(表示当前方法的返回值是一个 bean )
- @Bean 注解是有 Value 属性的,表示 bean 的名称,即 id
具体实现:
- 添加配置类
- 在配置类中添加方法,该方法返回所需的 bean 的类型,并为该方法添加
@Bean
注解- 在需要时,按类型获取 bean
-
导入第三方 jar 包
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
-
在配置类中添加方法
引入外部配置文件的具体相关内容已经再上一小节详细说明
@Configuration @PropertySource("jdbc.properties") public class SpringConfig { // 定义一个方法获得要管理的对象 public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName("${driverClassName}"); druidDataSource.setUrl("${url}"); druidDataSource.setUsername("${name}"); druidDataSource.setPassword("${password}"); return druidDataSource; } }
-
在方法伤添加
@Bean
注解@Bean注解的作用是将方法的返回值制作为 Spring 管理的一个 bean 对象(表示当前方法的返回值是一个 bean )
注意:
- 不能使用
DataSource ds = new DruidDataSource()
,因为 DataSource 接口中没有对应的 setter 方法来设置属性。
@Configuration @PropertySource("jdbc.properties") public class SpringConfig { // 定义一个方法获得要管理的对象 @Bean public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName("${driverClassName}"); druidDataSource.setUrl("${url}"); druidDataSource.setUsername("${name}"); druidDataSource.setPassword("${password}"); return druidDataSource; } }
- 不能使用
-
从IoC容器中获取对象并打印
public class App { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); DataSource dataSource = ctx.getBean(DataSource.class); System.out.println(dataSource); } }
4.3 引入外部配置类
在配置类中写过多的函数会导致混乱,故可以将配置拆出来单独写成类,后续会介绍如何引入外部配置类
4.3.1 使用包扫描引入
-
将函数拆解出来,单独作为一个类,并添加配置类注解
@Configuration @PropertySource("jdbc.properties") public class JDBCConfig { // 定义一个方法获得要管理的对象 @Bean public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName("${driverClassName}"); druidDataSource.setUrl("${url}"); druidDataSource.setUsername("${name}"); druidDataSource.setPassword("${password}"); return druidDataSource; } }
-
在配置类上使用注解,使用包扫描
@ComponentScan
引入外部配置类(不推荐,使用的是包名,引用内容不明了)@Configuration @ComponentScan("priv.dandelion.config") public class SpringConfig { }
4.3.2 使用 @Import
引入
-
将函数拆解出来,单独作为一个类,不需要添加配置类注解
// @Configuration @PropertySource("jdbc.properties") public class JDBCConfig { // 定义一个方法获得要管理的对象 @Bean public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName("${driverClassName}"); druidDataSource.setUrl("${url}"); druidDataSource.setUsername("${name}"); druidDataSource.setPassword("${password}"); return druidDataSource; } }
-
在配置类上使用注解,使用
@Import
引入外部配置类如果需要引入多个外部配置类,可以使用数组的格式进行书写
@Configuration @Import(JDBCConfig.class) public class SpringConfig { }
4.3.3 相关知识点
4.3.3.1@Bean
名称 | @Bean |
---|---|
类型 | 方法注解 |
位置 | 方法定义上方 |
作用 | 设置该方法的返回值作为spring管理的bean |
属性 | value(默认):定义bean的id |
4.3.3.2@Import
名称 | @Import |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 导入配置类 |
属性 | value(默认):定义导入的配置类类名, 当配置类有多个时使用数组格式一次性导入多个配置类 |
4.4 注解开发实现为第三方 Bean 注入资源
4.4.1 简单数据类型
@PropertySource("jdbc.properties")
public class JDBCConfig {
// 对成员变量进行注入,降低耦合度
@Value("${driverClassName}")
private String driver;
@Value("${url}")
private String url;
@Value("${name}")
private String name;
@Value("${password}")
private String password;
// 定义一个方法获得要管理的对象
@Bean
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(driver);
druidDataSource.setUrl(url);
druidDataSource.setUsername(name);
druidDataSource.setPassword(password);
return druidDataSource;
}
}
4.4.2 引用数据类型
4.4.2.1 需求分析
- 需求:假设需要在外部配置类中使用到BookDao
4.4.2.2 注入引用数据类型步骤
-
在 Spring 配置类中扫描
扫描的目的是让 Spring 能管理到 BookDao,也就是说要让 IOC 容器中有一个 bookDao类型的 Bean 对象,这一步骤的意义会在下面进行详细说明
@Configuration @ComponentScan("priv.dandelion.dao") @Import(JDBCConfig.class) public class SpringConfig { }
-
在外部配置类中加上参数并使用
-
引用类型注入只需要为 bean 定义方法设置形参即可,容器会根据类型自动装配对象。
-
此处如何理解:此处为自动装配,检测到该方法是在做第三方 Bean (要配置一个 Bean ),Spring 会认为这个形参要由 Spring 进行提供,此时会在 IoC 容器中寻找该类型的 Bean,而在上面主配置类中已经对 BookDao 类型的 Bean 进行了扫描。
// 定义一个方法获得要管理的对象 @Bean public DataSource dataSource(BookDao bookDao) { System.out.println(bookDao); DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driver); druidDataSource.setUrl(url); druidDataSource.setUsername(name); druidDataSource.setPassword(password); return druidDataSource; }
-
5、注解开发总结
功能 | XML配置 | 注解 |
---|---|---|
定义bean | bean 标签 * id 属性 * class 属性 |
@Component,定义 bean,为简单明了,常使用以下三个注解 * @Controller 用于 Controller 的 bean* @Service 用于 Service 的 bean* @Repository 用于 Dao 的 bean@ComponentScan,设置 spring 配置类扫描路径,用于加载使用注解格式定义的 bean |
设置依赖注入 | setter 注入 构造器注入 自动装配 |
@Autowired,自动装配,默认按类型注入 * @Qualifier ,按照名称进行注入,搭配@Autowired 使用@Value,简单类型注入 |
配置第三方bean | bean 标签 静态工厂、实例工厂、FactoryBean |
@Bean 注解,用于配置类或外部配置类中的方法 * 方法的返回值为所需的 bean 的类型 |
作用范围 | scope 属性 | @Scope * 默认值singleton(单例),可选值prototype(非单例) |
生命周期 | 标准接口 * init-method * destroy-method |
@PostConstructor,用于表示一个方法是初始化方法 @PreDestory,用于表示一个方法是销毁方法 |
6、Spring整合
6.1 Spring 整合 MyBatis 思路分析
6.1.1 环境准备(MyBatis 的原始开发)
-
准备数据库
create database spring_db character set utf8; use spring_db; create table tbl_account( id int primary key auto_increment, name varchar(35), money double );
-
导入依赖 jar 包
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> </dependencies>
-
根据表创建模型类
package priv.dandelion.entity; import java.io.Serializable; public class Account implements Serializable { private Integer id; private String name; private Double money; //setter...getter...toString...方法略 }
-
创建Dao接口
使用自动代理,不需要再写映射文件,仅编写接口并使用注解编写 SQL 语句
package priv.dandelion.dao; import priv.dandelion.entity.Account; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import java.util.List; public interface AccountDao { @Insert("insert into tbl_account(name,money)values(#{name},#{money})") void save(Account account); @Delete("delete from tbl_account where id = #{id} ") void delete(Integer id); @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ") void update(Account account); @Select("select * from tbl_account") List<Account> findAll(); @Select("select * from tbl_account where id = #{id} ") Account findById(Integer id); }
-
创建Service接口和实现类
-
接口
public interface AccountDao { @Insert("insert into tbl_account(name,money)values(#{name},#{money})") void save(Account account); @Delete("delete from tbl_account where id = #{id} ") void delete(Integer id); @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ") void update(Account account); @Select("select * from tbl_account") List<Account> findAll(); @Select("select * from tbl_account where id = #{id} ") Account findById(Integer id); }
-
实现类
@Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Override public void save(Account account) { accountDao.save(account); } @Override public void delete(Integer id) { accountDao.delete(id); } @Override public void update(Account account) { accountDao.update(account); } @Override public List<Account> findAll() { return accountDao.findAll(); } @Override public Account findById(Integer id) { return accountDao.findById(id); } }
-
-
添加数据库连接配置文件
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver # useSSL:关闭MySQL的SSL连接 jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false jdbc.username=root jdbc.password=123456
-
添加Mybatis核心配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--读取外部properties配置文件--> <properties resource="jdbc.properties"></properties> <!--别名扫描的包路径--> <typeAliases> <package name="priv.dandelion.entity"/> </typeAliases> <!--数据源--> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </dataSource> </environment> </environments> <!--映射文件扫描包路径--> <mappers> <package name="priv.dandelion.dao"></package> </mappers> </configuration>
-
编写应用程序
public class App { public static void main(String[] args) throws IOException { // 1.初始化 SqlSessionFactory // 创建SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 加载mybatis-config.xml配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 创建SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); // 2.获取连接,获取实现 // 获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 执行SqlSession对象执行查询,获取结果User AccountDao accountDao = sqlSession.getMapper(AccountDao.class); // 3.获取数据层接口 Account ac = accountDao.findById(2); System.out.println(ac); // 4.关闭连接 // 释放资源 sqlSession.close(); } }
6.1.2 整合思路分析
Mybatis的基础环境我们已经准备好了
- 思考:上述的内容中,哪些 Bean 可以交给Spring来管理?
-
Mybatis程序核心对象分析
由这部分内容可知:真正需要交给 Spring 管理的是第 1 部分初始化 SqlSessionFactory
public class App { public static void main(String[] args) throws IOException { // 1.初始化 SqlSessionFactory // 创建SqlSessionFactoryBuilder对象 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 加载mybatis-config.xml配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 创建SqlSessionFactory对象 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); // 2.获取连接,获取实现 // 获取SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 执行SqlSession对象执行查询,获取结果User AccountDao accountDao = sqlSession.getMapper(AccountDao.class); // 3.获取数据层接口 Account ac = accountDao.findById(2); System.out.println(ac); // 4.关闭连接 // 释放资源 sqlSession.close(); } }
-
整合 Mybatis,就是将 Mybatis 用到的内容交给 Spring 管理,分析下配置文件
- 第一部分读取外部 properties 配置文件,Spring 有提供具体的解决方案
@PropertySource
,需要交给 Spring - 第二部分起别名包扫描,为 SqlSessionFactory 服务的,需要交给Spring
- 第三部分主要用于做连接池,Spring 之前我们已经整合了 Druid 连接池,这块也需要交给Spring
- 前面三部分一起都是为了创建 SqlSession 对象用的,那么用 Spring 管理 SqlSession 对象吗?回忆下 SqlSession 是由 SqlSessionFactory 创建出来的,所以只需要将 SqlSessionFactory 交给 Spring 管理即可。
- 第四部分是 Mapper 接口和映射文件
(如果使用注解就没有该映射文件)
,这个是在获取到 SqlSession 以后执行具体操作的时候用,所以它和 SqlSessionFactory 创建的时机都不在同一个时间,可能需要单独管理。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 1.初始化属性数据,读取外部properties配置文件 --> <properties resource="jdbc.properties"></properties> <!-- 2.初始化类型别名,别名扫描的包路径 --> <typeAliases> <package name="priv.dandelion.entity"/> </typeAliases> <!-- 3.初始化dataSource,数据源 --> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </dataSource> </environment> </environments> <!-- 4.初始化映射配置,映射文件扫描包路径 --> <mappers> <package name="priv.dandelion.dao"></package> </mappers> </configuration>
- 第一部分读取外部 properties 配置文件,Spring 有提供具体的解决方案
6.2 Spring 整合 MyBatis 步骤
上一部分已经分析了 Spring 与 Mybatis 的整合,大体需要做两件事:
- Spring 要管理 MyBatis 中的 SqlSessionFactory,使用到了 SqlSessionFactoryBean
- Spring 要管理 Mapper 接口的扫描,使用到了 MapperScannerConfigurer
-
在原先依赖的基础上,再添加两个依赖
<dependencies> <!-- ... --> <dependency> <!--Spring操作数据库需要该jar包--> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.10.RELEASE</version> </dependency> <dependency> <!-- Spring与Mybatis整合的jar包 这个jar包mybatis在前面,是Mybatis提供的 --> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> </dependencies>
-
创建 Spring 的主配置类
// 配置类注解 @Configuration // 包扫描 @ComponentScan("priv.dandelion") public class SpringConfig { }
-
创建数据源的配置类
public class JDBCConfig { @Value("${jdbc.driver}") private String driver; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Bean public DataSource dataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driver); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } }
-
主配置类中读 properties 并引入数据源配置类
@Configuration @ComponentScan("priv.dandelion") @PropertySource("classpath:jdbc.properties") @Import(JDBCConfig.class) public class SpringConfig { }
-
创建 Mybatis 配置类并配置 SqlSessionFactory
说明:
- 使用 SqlSessionFactoryBean 封装 SqlSessionFactory 需要的环境信息
- SqlSessionFactoryBean 是 FactoryBean 的一个子类,在该类中将SqlSessionFactory 的创建进行了封装,简化对象的创建,使用时只需要将其需要的内容设置即可。
- 方法中有一个参数为 dataSource,当前 Spring 容器中已经创建了 Druid 数据源,类型刚好是 DataSource 类型,此时在初始化 SqlSessionFactoryBean 这个对象的时候,发现需要使用 DataSource 对象,而容器中刚好有这么一个对象,就自动加载了 DruidDataSource 对象。
- 使用 MapperScannerConfigurer 加载 Dao 接口,创建代理对象保存到 IOC 容器中
- MapperScannerConfigurer 对象也是 MyBatis 提供的专用于整合的 jar 包中的类,用来处理原始配置文件中的 mappers 相关配置,加载数据层的 Mapper 接口类
- MapperScannerConfigurer 有一个核心属性 basePackage,就是用来设置所扫描的包路径
public class MybatisConfig { /** * 定义bean,SqlSessionFactoryBean,负责创建 SqlSessionFactory,即配置文件的二三部分 * @param dataSource * @return SqlSessionFactoryBean */ @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) { SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean(); ssfb.setTypeAliasesPackage("priv.dandelion.entity"); // JDBCConfig 中已经定义了这个 bean,故此处可采用引用类型注入,方法是直接为本方法增加需要注入类型的形参 ssfb.setDataSource(dataSource); return ssfb; } /** * 定义bean,MapperScannerConfigurer,映射配置,配置包扫描,即配置文件的第四部分 * @return MapperScannerConfigurer */ @Bean public MapperScannerConfigurer mapperScannerConfigurer() { MapperScannerConfigurer msc = new MapperScannerConfigurer(); msc.setBasePackage("priv.dandelion.dao"); return msc; } }
- 使用 SqlSessionFactoryBean 封装 SqlSessionFactory 需要的环境信息
-
主配置类中引入 Mybatis 配置类
@Configuration @ComponentScan("priv.dandelion") @PropertySource("classpath:jdbc.properties") @Import({JDBCConfig.class, MybatisConfig.class}) public class SpringConfig { }
-
编写运行类
public class App2 { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class); AccountService accountService = ctx.getBean(AccountService.class); Account account = accountService.findById(1); System.out.println(account); } }
6.3 Spring 整合 Junit
6.3.1 环境准备
使用 Spring 整合 MyBatis 案例
6.3.2 整合 Junit 步骤
注意:
- 单元测试,如果测试的是注解配置类,则使用
@ContextConfiguration(classes = 配置类.class)
- 单元测试,如果测试的是配置文件,则使用
@ContextConfiguration(locations={配置文件名,...})
- Junit运行后是基于 Spring 环境运行的,所以 Spring 提供了一个专用的类运行器,这个务必要设置,这个类运行器就在 Spring 的测试专用包中提供的,导入的坐标就是这个东西
SpringJUnit4ClassRunner
- 上面两个配置都是固定格式,当需要测试哪个 bean 时,使用自动装配加载对应的对象,下面的工作就和以前做 Junit 单元测试完全一样了
-
引入依赖
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.10.RELEASE</version> </dependency>
-
编写测试类
// 设置类运行器 @RunWith(SpringJUnit4ClassRunner.class) // 设置Spring环境对应的配置类,加载配置类,可以使用数组 @ContextConfiguration(classes = {SpringConfig.class}) // @ContextConfiguration 注解也可以用于加载配置文件(如果需要),可以使用数组 //@ContextConfiguration(locations={"classpath:applicationContext.xml"}) public class AccountServiceTest { // 使用自动装配注入bean @Autowired private AccountService accountService; @Test public void testFindById() { System.out.println(accountService.findById(1)); } @Test public void testFindAll() { System.out.println(accountService.findAll()); } }
6.4 相关知识点
6.4.1 @RunWith
名称 | @RunWith |
---|---|
类型 | 测试类注解 |
位置 | 测试类定义上方 |
作用 | 设置JUnit运行器 |
属性 | value(默认):运行所使用的运行期 |
6.4.2 @ContextConfiguration
名称 | @ContextConfiguration |
---|---|
类型 | 测试类注解 |
位置 | 测试类定义上方 |
作用 | 设置JUnit加载的Spring核心配置 |
属性 | classes:核心配置类,可以使用数组的格式设定加载多个配置类 locations: 配置文件,可以使用数组的格式设定加载多个配置文件名称 |