-
Spring5 学习笔记
个人代码: GitHub
https://github.com/bpf-collector/Study-Code/tree/master/Spring5_Code
1. Spring 概述
1.1 Spring 简介
Spring Framework 是一个使用Java开发的、轻量级的、开源框架,它的主要作用是为了解耦合。Spring 的核心技术是 IOC(控制反转) 和 AOP(面向切面编程)。
- 官方网站: https://spring.io
Spring 框架提高了很多功能,包括IOC容器、AOP、数据访问、事务、测试功能、定时任务、缓存等等。
1.2 优点
轻量、解耦、面向切面编程、方便与其他框架集成、方便测试、减低开发难度。
2. IOC 控制反转
2.1 IOC 是什么
IOC (Inversion of Control, 控制反转) 是一种理论,指导开发人员如何使用对象、管理对象,将对象的生命周期交给容器来管理。通过容器管理对象,开发人员只需要拿到对象,执行对象的方法即可。
- 控制:管理对象的创建、属性赋值、生命周期的管理。
- 正转:让开发人员掌控对象的创建、属性赋值,即整个生命周期的管理。
- 反转:把开发人员管理对象的权限转移给容器来实现,让容器完成管理。
2.2 IOC 的技术实现
DI (Dependency Injection, 依赖注入) 是 IOC 的一种技术实现,开发人员通过对象的名称获取已初始化的对象,而对象的创建、属性赋值、对象间的调用等都由容器内部实现。
2.3 IOC-创建对象 牛刀小试
Source Code2.3.1 测试步骤
- 创建 maven-quickstart 项目,并调整项目结构(字符编码、JDK版本等)
-
添加依赖
- spring-context
- junit
-
定义接口和实现类
-
接口: SomeService
-
方法:
doSome(): void
-
方法:
- 实现类: SomeServiceImpl
-
接口: SomeService
-
创建 Spring 配置文件(.xml),声明需要创建的对象
-
通过
<bean>
标签声明对象,一个标签对应一个对象。
-
通过
-
使用容器中的对象
-
创建
ApplicationContext
对象 -
通过
getBean()
获取容器中的对象
-
创建
2.3.2 依赖文件
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bpf</groupId>
<artifactId>M01-ioc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2.3.3 接口与实现类
2.3.4 配置文件
<?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 自定义对象的名称,保持唯一。
class 自定义对象的全限定类名,不能是接口。
>>> Spring 根据 id 和 class 创建对象,并将对象放入一个 map 对象中。
-->
<bean id="someService" class="com.bpf.service.impl.SomeServiceImpl" />
<bean id="someService1" class="com.bpf.service.impl.SomeServiceImpl" />
<bean id="mydate" class="java.util.Date" />
</beans>
2.3.5 测试创建对象
测试创建对象: CreateBeanTest.java
2.4 Spring 的配置文件
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) 根标签是 beans
2) xxx.xsd 是当前XML文件的约束文件
3) 在 beans 标签内声明 bean 对象。
一个 bean 就是一个java对象。
-->
</beans>
Spring 支持多配置文件方式,Spring 管理多配置文件常用的是包含关系。即在主配置文件中使用import
标签包含其他配置文件,在其他配置文件中定义声明各自的信息。
<!-- 主配置文件 -->
<!-- 路径中可以使用通配符 * 同时引入多个文件 -->
<import resource="classpath:其他配置文件路径" />
2.5 Spring IOC ☞ 创建对象
2.5.1 Spring 容器创建对象的特点
Spring 框架使用 DI 实现 IOC 思想,底层通过反射机制创建对象、初始化对象。
-
容器对象是
ApplicationContext
,它是一个接口。常用的实现类是ClassPathXmlApplicationContext
,并且通过getBean()
方法获取已初始化的对象。 - Spring 创建对象默认调用类的无参构造器。
-
Spring 在创建容器对象后,会读取配置文件,并创建文件中声明的所有java对象,然后都放在map对象(
ConcurrentMap
)中。
2.5.2 XML方式
Spring 通过在配置文件中使用bean
标签声明对象,使用id
属性指定创建的对象名称,使用class
属性指定创建的对象类型。
<!-- 配置文件中声明一个 bean 标签代表一个 java对象 -->
<bean id="对象名称" class="对象类型" />
2.5.3 注解方式
使用注解代替配置文件中的bean
标签,在Java类上使用注解,通过value
属性指定创建的对象名称(相对于标签的id
属性)。同时还需要在配置文件中开启注解扫描并指定扫描的包路径。
Spring 提供了四个注解:
配置文件开启注解扫描:
<!-- base-package 指定要扫描的包路径,Spring 会自动扫描包及其子包内表有上述注解之一的类,并创建和管理。 -->
<context:componet-scan base-package="包路径" />
<!-- 如何扫描多个包? -->
<!-- 1. 使用多个标签 -->
<context:componet-scan base-package="xx.yy.pack01" />
<context:componet-scan base-package="xx.yy.pack02" />
<!-- 2. 使用分隔符:分号(;)或逗号(,) -->
<context:componet-scan base-package="xx.yy.pack01;xx.yy.pack02" />
<!-- 3. 使用共同的父包 -->
<context:componet-scan base-package="xx.yy" />
2.6 Spring IOC ☞ 属性注入
Source Code2.6.1 XML方式
(1)set注入(设值注入)
set注入:通过对象的
setXxx()
方法给属性赋值。
特点:
- 注入的属性必须存在对应的 setter 方法
- 如果属性在对象中不存在,但存在 setter 方法,依然不会报错。
- Spring 容器只负责调用 setter 方法,与方法的具体实现无关。
<!-- 简单类型注入: 基本数据类型、String类型 -->
<bean id="xxx" class="yyy">
<property name="属性名" value="xxx" />
...
</bean>
<!-- 引用Java对象 -->
<bean id="xxx" class="yyy">
<property name="属性名" ref="其他bean标签的id值" />
...
</bean>
<!-- 或 -->
<bean id="xxx" class="yyy">
<property name="属性名">
<bean class="想要注入此属性的对象"></bean>
</property>
...
</bean>
<!-- 注入null值 -->
<bean id="xxx" class="yyy">
<property name="属性名">
<null/>
</property>
...
</bean>
<!-- 集合类型 -->
<bean id="xxx" class="yyy">
<property name="属性名">
<!-- 数组 -->
<array>
<value>xxx</value>
</array>
</property>
<property name="属性名">
<!-- List -->
<list>
<value>xxx</value>
<ref bean="其他bean标签的id值" />
</list>
</property>
<property name="属性名">
<!-- Set -->
<set>
<value>xxx</value>
</set>
</property>
<property name="属性名">
<!-- Map -->
<map>
<entry key="xxx" value="yyy" />
</map>
</property>
<property name="属性名">
<!-- 数组 -->
<array>
<value>xxx</value>
</array>
</property>
</bean>
(2)构造注入
构造注入:通过对象的 含参构造器 方法给属性赋值。
特点:
- 不需要属性的 setter 方法
- 需要有相对应的含参构造器
<!--
index 对应构造器的形参索引,从0开始,可以省略
name 对应构造器的形参名
value 对应构造器的形参值
ref 对应其他的Java Bean
-->
<bean id="xxx" class="yyy">
<constructor-arg name="构造器形参名" value="xxx" />
<constructor-arg index="构造器形参索引" value="xxx" />
...
</bean>
(3)引用类型自动注入
引用类型自动注入:只针对对象中的引用类型有效,可以指定根据名称或类型自动注入属性的值。
- byName: 根据名称注入。当配置文件中bean标签的id值与对象的属性名匹配且属于同个类型时,可以进行注入。
-
byType: 根据类型注入。当配置文件中bean标签的class值与对象的属性类型同源时,可以进行注入。
- bean标签的class值与对象的属性类型相同时。
- bean标签的class值与对象的属性类型存在父子关系时。
- bean标签的class值与对象的属性类型存在接口-实现类关系时。
特点
- byName 方式通过 bean 标签的id属性,需要保证id唯一。
- byType 方式提供 bean 标签的class属性,需要保证只能存在一个同源的bean,否则会报错。
- 引用类型自动注入本质上使用的是setter方法进行属性赋值的。
<!-- 引用类型自动注入 -->
<bean id="xxx" class="yyy" autowired="byName | byType">
...
</bean>
(4)小作业
主要功能:模拟用户注册操作。
- 实体类 User,保存用户数据。
- 定义一个 UserDao 接口,提供方法 insertUser(User),同时定义接口的实现类 MySqlUserDao,方法实现输出 "通过MySQL插入用户:用户数据"。
- 定义一个 UserService 接口,提供方法 addUser(User),同时定义接口的实现类 UserServiceImpl,并实现方法。
要求:使用 Spring 创建和管理接口的实现类对象,并通过 Spring 获取对象完成用户注册操作。
Source Code2.6.2 注解方式
(1)@Value
-
@Value
注解只能为属性赋普通类型的值。 -
@Value
注解的位置:- 属性声明上:无需setter方法
- setter方法上:需要setter方法,并且会调用setter方法
- 赋的值可以通过外部配置文件(.properties)指定。
<!-- 配置文件中引入外部配置文件 -->
<context:property-placeholder location="classpath:properties文件的路径" />
(2)@Autowired
-
@Autowired
注解可以为属性赋引用类型的值,默认方式是byType
。 -
@Autowired
注解的位置:- 属性声明上:无需setter方法
- setter方法上:需要setter方法,并且会调用setter方法
/**
* Autowired 注解源码
* 包含了 required 属性,默认值为true。表示当赋值的属性必须有值且赋值成功,当赋值的对象为null时,会抛出异常。
*/
public @interface Autowired {
boolean required() default true;
}
(3)@Qualifer
当使用@Autowired
注解进行引用类型注入时,由于默认方式为byType
,当存在多个同源的bean时,会抛出异常:org.springframework.beans.factory.NoUniqueBeanDefinitionException
。这时候就需要使用byName
方式了。
-
@Qualifer
注解结合@Autowired
注解使用可以实现byName
方式的引用类型自动注入。 - 注解位置同上。
/**
* Qualifer 注解中只有一个属性 value, 用来指定 bean 的名称即 id。
*/
public @interface Qualifier {
String value() default "";
}
(4)@Resource
-
@Resource
注解是JDK自带的注解,但 Spring 支持这样的注解使用。 -
@Resource
注解只能为属性赋引用类型的值,默认方式是byName
。-
当使用
byName
无法匹配到任何bean时,会使用byType
方式。 -
通过指定
name
属性让注解只通过byName
方式注入bean。
-
当使用
- 在 JDK8 及之前是自带此注解的,更高的版本需要手动导入依赖。
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
2.7 Spring IOC 总结
IOC 就是用来管理对象、管理依赖关系的。通过 IOC 可以实现解决处理业务逻辑对象之间的耦合关系,即 Service 和 DAO 之间的解耦合。
-
不适合交给Spring管理的对象:
- 实体类
- servlet、listener、filter 等 WEB 中的对象,因为它们是由 Tomcat 创建和管理的对象。
补充
> 完全注解开发
> Spring Bean 的生命周期
3. AOP 面向切面编程
3.1 AOP 是什么
AOP (Aspect Orient Programming, 面向切面编程) 是一种编程思想。它可以在不改变源代码的基础上,给业务方法新增功能。
AOP 是一种动态的思想,它是在程序运行期间,为特定的业务创建代理,通过代理来增加切面功能,而这个代理是存在于内存中的。
什么是切面?
- 给业务功能新增的功能就是切面。
- 切面一般是非业务功能,而且一般都是可复用的。
- 比如:日志功能、事务功能、权限检查、参数检查、信息统计等等。
AOP的作用?
- 给业务功能新增方法不需改变源代码。
- 让开发人员专注业务逻辑,提高开发效率。
- 实现业务功能与非业务功能解耦合。
- 切面复用。
3.2 AOP 中的重要术语
AOP 中重要的三个要素:Aspect、Pointcut、Advice,表示在 Advice时间、在 Pointcut位置 执行 Aspect切面。
3.3 AOP 的使用时机
- 当某些方法需要增加相同功能,而源代码又不方便修改时
- 当给业务方法增加非业务功能时
3.4 AOP 的技术实现
常用的 AOP 实现技术是 Spring 和 AspectJ。
- Spring:Spring 框架实现了 AOP 思想中的部分功能。但其操作比较繁琐和笨重。
-
AspectJ:独立的框架,专门负责 AOP,属于 Eclipse 基金会。
- 官网: https://www.eclipse.org/aspectj/
3.5 AspectJ 框架
AspectJ 框架中可以使用 注解 和 XML配置文件 的方式实现 AOP。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.12</version>
</dependency>
3.5.1 注解方式
Source Code(1)Advice 通知注解
AspectJ 框架中表示切面执行的时间是五种通知注解,分别代表不同的执行时间。
(2)Pointcut 切入点表达式
AspectJ 框架中表示切面执行的位置是切入点表达式,本质上可以看作是业务方法的定位标志。
execution(访问权限? 返回值类型 全限定类名?方法名(参数列表) 异常类型?)
-
?
代表可选。-
最简形式:
execution(返回值类型 方法名(参数列表))
-
最简形式:
- 四个部分之间通过空格分开,并且都可以使用通配符