Spring5知识回顾及查漏补缺
基本概念
-
Spring是一个轻量级的开源的JavaEE框架
-
Srping可以解决企业应用开发的复杂性
-
Spring有两个核心部分:IOC和AOP
- IOC:控制反转,把创建对象的过程交给Spring进行管理
- AOP:面向切面
-
Spring特点:
- 方便解耦,简化开发
- AOP编程支持
- 方便程序测试
- 方便集成其他框架
- 降低JavaEE API开发使用难度
- 方便进行事务操作
IOC容器
底层原理
用到xml解析、工厂模式、反射技术
IOC容器实现方式
IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
Spring提供IOC容器实现两种方式:BeanFactory、ApplicationContext
BeanFactory
IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用
特点:
加载配置文件时不会创建对象,在使用对象时才创建对象
ApplicationContext
BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
特点:
加载配置文件时就会将配置文件中的对象进行创建
ApplicationContext接口的主要实现类有:
- FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext
什么是Bean管理
- Spring创建对象
- Spring注入属性
基于xml管理bean
方式一
无参构造+属性set方式创建:
1 |
|
默认执行无参构造来创建对象
方式二
有参构造创建:
1 |
|
常用标签及属性解释
- bean:指一个对象
- id:唯一标识
- class:类全路径
- scope:作用域
- singleton:单实例,默认值,spring加载配置文件时就会创建对象
- prototype:多实例,加载配置文件时不创建对象,调用getBean时创建对象
- property:指对象的属性
- name:属性名
- value:属性值
- ref:关联其他bean
- null:设置属性值为null
- constructor-arg:指类的构造器
- name:构造器中参数名
- value:参数值
属性值包含特殊字符解决方式
- 将特殊字符转义
- 使用<![CDATA[]]>包裹属性值
自动装配
根据指定装配规则(属性名或者属性类型),不需要明确指定属性名或属性类型,Spring自动将匹配的属性值注入;
使用到bean标签的autowire
属性
- byName:根据属性名称注入
- byType:根据属性类型注入
FactoryBean
Spring有两种bean,一种普通bean,另外一种工厂bean(FactoryBean)
- 普通bean:在配置文件中定义的bean类型就是返回类型
- 工厂bean:在配置文件中定义bean类型可以和返回类型不一样,实现FactoryBean接口
Bean生命周期
- 通过构造器创建bean实例(无参构造)
- 为bean的属性设置值和对其他bean引用(调用set方法)
- 把bean实例传递给bean的后置处理器方法(BeanPostProcessor接口的postProcessBeforeInitialization)
- 调用bean的初始化方法(需要进行配置)
- 把bean实例传递给bean的后置处理器方法(BeanPostProcessor接口的postProcessAfterInitialization)
- bean可以使用了
- 当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)
基于注解管理bean
需要引入spring-aop依赖,开启组件扫描(配置文件或注解方式)
配置文件方式开启组件扫描:
1 |
|
注解方式开启组件扫描:
1 |
|
对象创建
- @Component:一般用在组件
- @Service:一般用在service层
- @Controller:一般用在controller层
- @Repository:一般用在dao层
上面四个注解功能是一样的,都可以用来创建Bean实例
属性注入
- @Autowired:根据属性类型自动装配
- @Qualifier:根据属性名称进行注入,需要和@Autowired一起使用,当接口有多个实现类时用此注解指定实现类名称
- @Resource:可以根据类型注入,也可以根据名称注入,它是javax包中的
- @Value:注入普通类型属性
AOP
底层原理
底层使用动态代理,分为两种情况:
-
有接口时,使用JDK动态代理(创建接口实现类的代理对象)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43public class CalculateProxy {
public static void main(String[] args) {
ICalculate o = (ICalculate) getProxyInstance(new PlusCalculate());
System.out.println(o.calculate(1, 2));
// invoke执行前...
// calculate方法执行...
// invoke执行后...
// 3
}
public static Object getProxyInstance(Object target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new CalculateInvocationHandler(target));
}
private static class CalculateInvocationHandler implements InvocationHandler {
private Object target;
public CalculateInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke执行前...");
Object o = method.invoke(target, args);
System.out.println("invoke执行后...");
return o;
}
}
}
interface ICalculate {
int calculate(int a, int b);
}
class PlusCalculate implements ICalculate {
@Override
public int calculate(int a, int b) {
System.out.println("calculate方法执行...");
return a + b;
}
} -
没接口时,使用CGLIB动态代理(创建当前类子类的代理对象)
术语
- 连接点(Join point):类中可以增强的方法
- 切点(Pointcut):通过切点表达式匹配接入点
- 通知(增强,Advice):实际增强的逻辑部分
- 前置通知:切入方法执行前做通知,@Before
- 后置通知:切入方法执行后做通知,@AfterReturning
- 环绕通知:切入方法执行前后做通知,@Around
- 异常通知:切入方法执行抛异常做通知,@AfterThrowing
- 最终通知:切入方法执行不管有无异常都做通知(类似finally),@After
- 切面(Aspect):由一系列切点、增强和引入组成的模块对象,可定义优先级,从而影响增强和引入的执行顺序
切入点表达式
execution([权限修饰符] [返回类型] [类全路径] [方法名称] [参数列表])
AspectJ注解方式
1 |
|
执行结果:
1 |
|
其他注意事项
将可以重复使用的表达式提取出来,放到切入点,如下:
1 |
|
当一个方法被多个增强类切入时,可以在增强了上加上@Order
注解来设置优先级
AspectJ配置文件方式
1 |
|
JdbcTemplate
Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作
JdbcTemplate对象的创建和属性注入
使用注解方式
1 |
|
在dao层引入并使用
1 |
|
常用方法
-
update(sql, args…):执行增删改操作
-
queryForObject(sql, class, args…):查询返回单列,如count查询
- class未返回单列的类型
-
queryForObject(sql, rowmapper, args…):查询返回单个对象
- 第一个参数是sql语句
- 第二参数是RowMapper接口实现类的实例
1
jdbcTemplate.queryForObject("select * from user where id = ?", new BeanPropertyRowMapper<User>(User.class), id);
- 第三个参数是sql语句值,是可变参数列表
-
query(sql, rowmapper, args…):查询多个对象的集合
- 第一个参数是sql语句
- 第二个参数是RowMapper接口实现类的实例
1
jdbcTemplate.query("select * from user where name like concat('%', ? '%')", new BeanPropertyRowMapper<User>(User.class), name);
- 第三个参数是sql语句值,是可变参数列表
-
batchUpdate(sql, list):批量增删改
- 第二个参数 List<Object[]> batchArgs ,每个Object[]表示添加一行数据的sql语句值
1
2
3
4
5
6
7public int[] batchAdd(List<User> users) {
List<Object[]> batchArgs = new ArrayList<>();
users.forEach(u -> {
batchArgs.add(new Object[]{u.getName(), u.getPassword()});
});
return jdbcTemplate.batchUpdate("INSERT INTO user (`name`, `password`) VALUES (?, ?)", batchArgs);
}
事务管理
Spring进行事务管理有两种方式:
- 编程式事务管理:使用比较麻烦
- 声明式事务管理:使用方便
- 基于注解方式
- 基于xml配置文件方式
Spring进行声明式事务管理,底层使用AOP原理
Spring事务管理API
PlatformTransactionManager接口针对不同的框架有不同的实现类
使用方式
Spring配置类上加上@EnableTransactionManagement
注解开启事务
继续在配置类中创建事务管理器对象实例
1 |
|
在需要事务操作的方法上加上@Transactional
注解
1 |
|
声明式事务管理参数配置
即@Transactional的属性
- propagation:事务传播行为,描述当一个事务方法被另一个事务方法调用时,这个事务该如何进行
- REQUIRED:若有事务在运行当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行
- SUPPORTS:若有事务正在运行,当前方法就在这个事务内运行,否则他可以不允许在事务中
- MANDATORY:当前方法必须运行在事务内部,若没有正在运行的事务,就抛出异常
- REQUIRES_NEW:当前方法必须启动新事务,并在自己的事务内运行,若有其他事务正在运行,应该将它挂起
- NOT_SUPPORTED:当前方法不应该运行在事务中,若有运行的事务,将它挂起
- NEVER:当前方法不应该运行在事务中,若有运行的事务,就抛出异常
- NESTED:若有事务在运行,当前方法就应该在这个事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行
- isolation:配置事务隔离级别,三个读问题:脏读、不可重复度、幻读
- DEFAULT:数据库默认隔离级别
- READ_UNCOMMITTED:读未提交,会出现脏读、不可重复度、幻读
- READ_COMMITTED:读已提交,会出现不可重复度、幻读
- REPEATABLE_READ:可重复读,特殊情况下会出现幻读
- SERIALIZABLE:可串行化
- timeout:超时时间,以秒为单位(默认为-1,表示不使用),事务需要在一定时间内提交,否则会回滚
- readOnly:是否只读,开启后只能查询,不能增删改
- rollbackFor:回滚,表示出现哪些异常后进行回滚
- noRollbackFor:不回滚,表示出现哪些异常后不进行回滚
Spring5新特性
-
自带了通用的日志封装
-
核心容器支持@Nullable注解
- 可以使用在方法、属性、参数上面,表示方法返回、属性值、参数值可以为空
-
核心容器支持函数式风格
-
支持整合JUnit5、JUnit4
- JUnit4单元测试加上下面的注解
1
2
3
4
5
6@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
@Test // org.junit.Test
public void test() {
}- JUnit5单元测试加上下面的注解
1
2
3
4
5
6
7//@ExtendWith(SpringExtension.class)
//@ContextConfiguration(classes = SpringConfig.class)
@SpringJUnitConfig(SpringConfig.class) // 可以用这一个注解代替上面两个注解
@Test // org.junit.jupiter.api.Test
public void test() {
}
SpringWebFlux
基本概念
Spring5添加的新模块,用于web开发,功能与SpingMVC类似,WebFlux是使用响应式编程的一种框架;
SpringMVC是基于Servlet容器的框架,而WebFlux是一种异步非阻塞的框架(Servlet3.1之后才支持异步非阻塞),核心是基于Reactor的相关API实现的
异步非阻塞
异步和同步是针对调用者,调用者发送请求后,不等待回应就去进行其他操作为异步,反之为同步
非阻塞和阻塞是针对被调用者,被调用者收到请求后,马上做出反馈再去执行操作为非阻塞,反之为阻塞
特点
- 非阻塞式:在有限的资源下,提高系统吞吐量和伸缩性,基于Reactor实现响应式编程
- 函数式编程:WebFlux使用函数式编程实现路由请求
与SpingMVC比较
- 都可以使用注解
- 都可以运行在tomcat
- SpingMVC采用命令式编程
- WebFlux采用异步响应式编程
响应式编程
响应式编程是一种面向数据流和变化传播的编程范式,这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
如,excel中公式“=B1+C1”,包含该公式的单元格值会随着其他单元格值的变化而变化。
观察者模式类
Java8及其之前版本提供Observer接口和Observable类;
实现Observer接口的是观察者,监听被观察者的变化;
继承Observable类的类是可观察的,即被观察者;
示例如下:
1 |
|
Java9使用Flow类替代观察者模式
响应式(Reactor)编程实现
两个核心类,Mono和Flux,这两个类实现Publisher接口,提供丰富操作符;
Flux对象实现发布者,可以返回N个元素,Mono对象实现发布者,返回0或1个元素;
Mono和Flux的特点
-
都可以发出三种数据信号:元素值、错误信号、完成信号
-
错误信号和完成信号都是终止信号,告诉订阅者数据流结束了,错误信号还会吧错误信息传递给订阅者
-
若没有发送任何元素值,而是直接发送终止信号,表示是空数据流
-
若没有终止信号,表示是无线数据流
使用示例
1 |
|
常用操作符
- map:将元素映射为新元素
- flatMap:将元素映射为流
执行流程和核心API
SpringWebFlux基于Reactor,默认使用容器是Netty,Netty是高性能的NIO(异步非阻塞)框架
执行过程相关类
核心控制器DispatcherHandler,实现接口WebHandler
-
DispatcherHandler:负责请求的处理
-
HandlerMapping:负责查询处理请求的方法
-
HandlerAdapter:负责实现请求处理
-
HandlerResultHandler:负责对响应结果的处理
实现函数式编程相关类
- RouterFunction接口:路由处理
- HandlerFunction接口:处理函数
基于注解编程模型
与SpringMVC差不多,也使用@Controller、@Service、@Repository
基于函数式编程模型
与基于注解编程模型的区别主要在控制层(对外提供接口)的实现上不一样
围绕两个核心接口:RouterFunction(实现路由功能,将请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数);
请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse