The article contains the basic knowledge of Spring…
注意:该总结不包括Spring三剑客三本书中的内容,三剑客的总结都写在书上。
Sping
下载spring:
复制地址之后拼上前缀打开网址:
spring5_demo1
最基础的几个包就是上图的Core Container
之后在IDEA里面建立最基础的Java项目,根目录里面建立lib文件,将上述的几个包复制过来,然后导入:
搭建完基础设施之后装配bean并测试:
IOC概念与原理
IOC演变
原始方式(牵一发动全身)
工厂模式(目的:虽然耦合度不可能没有,但尽量给他降到最低)
经过上面两个模式之后演变而来的IOC
IOC接口
IOC操作Bean管理
xml的null标签(用于设置属性为null)
xml属性值包含特殊符号
xml配置文件注入
内部bean
级联赋值
方式一:
方式二:
注意,这种方式要给dept加get方法
注入list集合类型,值是对象
提取list集合类型属性注入使用
工厂Bean(FactoryBean)
这里有三个方法,第一个返回bean的实例(注意,这里原本类型是Object,我们将他改为了Course),第二个返回bean的类型,第三个表示bean是否是单例。
这跟普通的Bean不一样,普通的配置什么就返回什么,工厂Bean可以返回跟配置的Bean的类型完全不一样的Bean,只需要继承FactoryBean并指定泛型、实现方法即可,如上图
现在就不能把类型写成MyBean了:
而应该写成Course:
bean的生命周期
把任意实现的初始化和销毁方法注册到xml的bean中,用于演示bean的初始化和销毁的过程:
销毁的时候使用ClassPathXmlApplicationContext的close()方法即可
后置处理器(BeanPostProcessor)
其实bean的生命周期有7步,还有两步在上面的5步生命周期中的第三步的前面和后面
实现接口BeanPostProcessor的方法即可
最后拿到完整的bean生命周期的过程:
更多关于bean的生命周期的讲解请参考:
https://blog.csdn.net/nrsc272420199/article/details/103192081(【bean的生命周期】 --- 对象创建 + 初始化流程分析 --- 【重点@Autowired的作用时机】
)
https://blog.csdn.net/nrsc272420199/article/details/103204119(【bean的生命周期】--- 构造方法、@Autowired、BeanPostProcessor、InitializingBean等的执行顺序解析
)
https://blog.csdn.net/nrsc272420199/article/details/95033223(【bean的生命周期】详解InitializingBean、initMethod和@PostConstruct
)
https://blog.csdn.net/nrsc272420199/article/details/103226662(【bean的生命周期】--- DisposableBean、destroyMethod和@PreDestroy
)
xml中的autowire
byName
根据id值(属性名称)自动注入
byType
根据类型值自动注入,注意这个时候不能注册多个同类型的bean,会报错
IOC操作Bean管理(外部属性文件)
引入context空间
创建配置文件
引入外部配置文件
注解
引入aop依赖并开启组件扫描
多个包用逗号隔开,也可以写这些包的上层
自定义组件扫描规则
首先use_default_filters设置为false表示不让他自动扫描全局,而是通过我们设置的规则取扫描
之后配置include-filter,其中type是annotation,表示扫描所有带有Controller注解的组件
当然还有exclude-filter,表示不扫描哪些组件
最后创建类并在类上面创建对象注解
这四个都可以
基于注解方式实现属性注入
主要讲一下@Resource
@Autowired是根据类型注入,@Qualifier是根据名称注入,而@Resource既可以根据类型注入也可以根据名称注入
抛弃xml,使用全注解
@Configuration与@ComponentScan
创建配置类,替代配置文件
AnnotationConfigApplication
原先我们是获取xml文件,现在获取配置类
之后通过context获取bean
AOP底层原理
底层使用的是动态代理,分两种情况:1、有接口;2、无接口
有接口情况下使用JDK动态代理
通过接口UserDao来创建代理对象,通过代理对象增强方法
使用Proxy类中的方法创建代理对象
实现Proxy代理,可以像下图这样写一个内部的InvocationHandler类,并在内部实现他
也可以像下图这样先创建该类,再写回
现在我们来仔细看看如何实现这个类
首先注入我们希望做功能扩展的类,为了更加通用直接写Object
完了之后写里面的增强逻辑:
最后写回newInstance方法并返回一个代理:
现在我们像上图那样调用add方法就会有增强效果了
无接口情况下使用CGLIB动态代理
没有接口的情况下,一般情况下我们可以像上面绿色的代码块一样创建一个子类来扩展方法,但事实上我们用的是动态代理,如上图右侧黑色代码块,由于没有接口,因此创建的是User类的代理对象(该代理对象也是User的子类,能完成绿色代码块的类似的功能,但是他不是new出来的)
AOP术语
AOP准备工作
AOP依赖包
AOP切入点表达式
开启Aspect生成代理对象
AOP中当有多个增强类的时候的增强顺序
@Order()
基于XML的AOP
使用配置类全注解开发
@EnableAspectJAutoProxy(proxyTargetClass = true)
上面讲到使用xml开启Aspect生成代理对象:
现在可以用@EnableAspectJAutoProxy(proxyTargetClass = true):
JDBC
添加依赖
(tx是针对事务操作的依赖包)
配置数据库连接池并注入jdbcTemplate
之后在配置文件开启组件扫描之后即可创建Service层和Dao层(可以用@Service和@Repository注解进行注释)
RowMapper
使用这个接口里面的实现类完成数据的封装(集合、类等等都可以用这个),当然我们也可以自己实现这个接口
BeanPropertyRowMapper<>()
这是Spring提供的RowMapper的一个实现
batchUpdate批量处理数据
底层实现事实上就是一个遍历导入数据
事务
编程式事务管理(不建议)
声明式事务管理
创建事务管理器
引入tx名称空间
开启事务注解
@Transactional
在Service类上面(或者Service类里面的方法上面)添加事务注解@Transactional
注意,这个注解可以加在类上面也可以加在方法上面
事务操作(声明式事务管理参数配置)
propagation
事务传播行为
比方说上图,一个方法标注了@Transactional另一个没有标注,那在使用add()方法的时候事务该如何处理。这个就叫事务的传播行为
7种行为
REQUIRED
REQUIRED_NEW
配置方法
Spring默认用的就是REQUIRED
isolation
事务隔离级别
4个隔离级别
配置方法
timeout
超时时间
设置方法
默认是-1,表示没有时间限制
readOnly
是否只读
配置方法
rollbackFor
回滚
该属性后面写异常的class,这些class的异常会进行回滚
noRollbackFor
不回滚
该属性后面写异常的class,这些class的异常不会进行回滚
事务操作(XML)
由于是xml配置的,因此开启事务注解这句话就不需要了:
配置通知
注意,这里的method的name可以指定全名,比方说上面的accountMoney,也可以让spring帮忙匹配,比方说上面注释掉的那一行中的account*
配置切入点和切面
切面中的advice-ref的值就是配置通知里面的advice的id
事务操作(完全注解声明式事务管理)
@EnableTransactionManagement
之前需要配置开启事务的注解:
现在直接用@EnableTransactionManagement来开启
测试
Spring5新功能
日志
这个log4j2.xml的内容是一个模板的,如下:
还可以手动输出log:
@Nullable
核心容器支持函数式风格GenericApplicationContext
之前如果我们自己new一个对象spring是不知道的也不会帮我们管理这个对象,我们需要将对象注册到spring中他才能帮我们管理(当然我们直接给对象添加注解,如@Bean、@Component等这样的话spring就直接可以管理这些对象了)
现在我们可以通过函数式编程里面的GenericApplicationContext对象来进行对象注册:
首先refresh()把内容清空,准备在里面进行注册,
之后调用registerBean()来注册对象,如上图
之后可以获取注册的对象,第一种方法是使用全类名,如上图
第二种方法是指定对象的名称,如上图
JUnit5
整合Junit4
先看看如何整合Junit4
@RunWith与@ContextConfiguration
之前我们测试都是要写context的,很麻烦,现在可以用注解的形式来做:
使用这两个注解之后可以直接用@Autowire注入要测试的组件
整合Junit5
@ExtendWith与@ContextConfiguration
@SpringJunitConfig
对上述写法的一种简化:
SpringWebFlux
响应式编程
Flow
上面用到的是Java8中的Observable和Observer,这两个类在Java9之后就过时了,被Flow取代,而且Flow才是真正意义上的响应式,SpringWebFlux底层的Reactor的相关API用的也是Java9及以后版本中的响应式内容,并且Reactor的响应式比Java9的Flow更加强大(事实上它是对Java9的Flow的一种封装)
上图是Java9中的Flow的基本用法,里面有发布者和订阅者,订阅者的onNext表示他所做出的行为,发布者会根据这些行为做出一些响应
响应式编程(Reactor实现)
Flux和Mono
基本用法
此外Flux.error(Throwable error)可用于发送错误信号
此时如果直接执行的话是不会有任何输出的,需要使用订阅才会有输出:
三种信号特点
操作符
Netty
先来看看BIO 阻塞方式:
每一个请求都要完成之后才能进行下一个请求,这就是阻塞方式
现在看看NIO 非阻塞:
有Connext、Accept、Read、Write四个状态,Selector选择器只需要关注Channel中的这几个状态并完成任务即可
它通过多个channel在selector中进行注册实现多路复用
SpringWebFlux和SpringMVC很相似
MVC是DispatchServlet,WebFlux是DispatchHandler
先将starter改为webflux:
上图改成:
WebHandler中的handle
跟MVC里面的流程基本是一样的
HandlerMapping具体找到哪个方法
HandlerAdapter实现请求的处理,也就是实现具体的业务方法
HandlerResultHandler对响应的结果进行处理
WebHandler还有很多其他的实现类:
ResourceWebHandler用于处理静态资源文件
WebHandlerDecorator是一种装饰器,用于扩展功能
RouterFunctionWebHandler是做路由的相关处理的
SpringWebflux(基于注解的编程模型)
特别要注意的区别是这里的service,现在都用上了Mono和Flux,如果没有返回值(比如上图的添加用户),那就使用Mono
接下来编写UserService的实现类:
注意,我们之前讲Flux和Mono最终都有一个结束信号,不然就是一个无限的数据流了,这里的完成信号是Mono.empty(),当然也可以用一个错误信号来表示完成
最后编写Controller:
别的都没问题,就是要注意这个添加的接口,注意他的写法
SpringWebflux(基于函数式编程模型)
路由通过适配器找到handler中的某一个具体的操作来执行
首先把上面基于注解的工程复制一份,将Controller删掉,service和entity不用动,之后我们编写自己的HandlerFunction:
直接创建一个普通的Java类,不需要继承类或者实现接口,就普普通通的一个Java类,里面去写类似Controller的方法:
由于SpringWebflux请求和响应不再是ServletRequest和ServletResponse了,而是ServerRequest和ServerResponse,因此传入Mono的是ServerResponse,表示最终返回的是一个流,那么怎么把一个Mono对象变成流呢?这里我们用到了之前讲的flatMap,并且用它来构造ServerResponse,如上图
那么该id的对象可能为空,因此需要做空值处理:
上图的build表示订阅,当里面发生变化的时候通知我,我来执行具体的操作
注意,上图的两个方法的返回,一个用了flatMap,一个没用,其实这两个返回完全是等价的,只是两种不同的写法
下面来初始化Netty服务器,顺便编写RouterFunction:
这里报了一个错,我们到getAllUsers方法里面:
发现他没有将ServerRequest传入(虽然方法里面没有用到这个request,但是也要传入,不然就会在上上图中的路由里面报错)
接下来创建适配器和服务器
最终调用:
WebClient
先开启服务器,之后用WebClient调用方法
再来一个查询所有:
这里使用了map,注意写法
doOnNext表示一个一个往下执行
blockFirst()就类似于订阅这个操作,不订阅就不会执行或输出任何操作