1.Spring-TX-传播行为
@Service
public class UserService {
@Autowired
private UserDao userDao;
/**
* 转账
*/
@Transactional(propagation = Propagation.REQUIRED)
public void transfer(){
userDao.reduceMoney(); // -100
int i = 10/0;
userDao.addMoney(); // +100
}
}
propagation : 事务传播行为
事务方法 : 对数据库表数据进行变化的操作.
@Transactinal
public void add(){
//调用update方法
update();
}
public void update(){
}
Spring框架事务传播行为共有7种
REQUIRED : 如果add方法本身有事务 , 调用update方法之后 , update使用当前add方法里面事务,如果add方法本身没有事务 , 调用update方法之后 , 创建新事务
REQUIRED_NEW : 使用add方法调用update方法 , add方法无论是否有事务 , 都创建新的事务
2.Spring-TX-隔离级别
事务有隔离性 , 多事务操作之间不会产生影响. 不考虑隔离性会产生很多问题.
有三个读问题 : 脏读 , 不可重复度 , 幻读.
2.1 脏读
一个未提交的事务读取到另一个未提交的事务的数据.
读到了回滚前的数据
2.2 不可重复读
一个未提交的事务读取到了另一个提交事务修改的数据.
2.3 幻读
一个未提交的事务读取到另一个提交事务添加的数据.
2.4 隔离级别
通过设置事务的隔离级别 , 解决读问题
@Transactional(propagation = Propagation.REQUIRED , isolation = Isolation.REPEATABLE_READ)
3.Spring-TX-其他参数
3.1 timeout
事务需要在一定时间内进行提交 , 如果不提交则进行回滚.
默认为 -1 , 设置时间以秒为单位进行计算.
@Transactional(propagation = Propagation.REQUIRED ,
isolation = Isolation.REPEATABLE_READ,
timeout = 5
)
3.2 readOnly
readOnly 默认值为 false , 表示可以查询 , 可以进行事务操作.
设置readOnly 为 true , 只能查询
@Transactional(propagation = Propagation.REQUIRED ,
isolation = Isolation.REPEATABLE_READ,
timeout = 5,
readOnly = false
)
3.3 rollbackFor
设置出现哪些异常进行事务回滚.
3.4 noRollbackfor
设置出现哪些异常不进行事务回滚.
4.Spring-TX-完全注解开发
bean.xml
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql://192.168.64.140:3306/user_db?serverTimezone=UTC"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<context:component-scan base-package="com.nanoswarm"></context:component-scan>
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
Config.class
@Configuration
@ComponentScan(basePackages = "com.nanoswarm.spring5")
@EnableTransactionManagement
public class SpringConfig {
@Bean
public DruidDataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://192.168.64.140:3306/user_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DruidDataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Bean
public DataSourceTransactionManager transactionManager(DruidDataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
TestMain.class
public class TestMain {
@Test
public void transfer(){
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.transfer();
}
}
5.Spring-TX-整合日志框架
Spring5.0框架自带了通用的日志封装.
Spring5已经移除Log4jConfigListener , 官方建议使用Log4j2
5.1 Spring框架整合Log4j2
步骤一 : 引入jar包
步骤二 : 创建log4j2.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
<!--先定义所有的appender-->
<appenders>
<!--输出日志信息到控制台-->
<console name="Console" target="SYSTEM_OUT">
<!--控制日志输出的格式-->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</console>
</appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
6.Spring-@Nullable注解和函数式注册对象
6.1 @Nullable
@Nullable注解可以使用在方法上面 , 属性上面 , 表示方法返回值可以为空 , 属性值可以为空 , 参数值可以为空.
6.2 函数式创建对象
@Test
public void testGenericApplicationContext(){
//创建GenericApplicationContext对象
GenericApplicationContext context = new GenericApplicationContext();
//调用context的方法对象注册
context.refresh();
context.registerBean("userBean", User.class,()->new User());
//获取bean
User userBean = context.getBean("userBean", User.class);
System.out.println(userBean);
}
7.Spring-WebFlux基本概念
-
Spring5添加的新的模块 , 用于web开发 , 功能和SpringMVC类似 , WebFlux使用当前一种比较流行的响应式编程出现的框架.
-
使用传统web框架 , 比如 SpringMVC , 这些基于Servlet的容器 , Webflux是一种异步非阻塞的框架 , 核心是基于Reactor的相关API实现的.
-
异步非阻塞 : 被调用者收到请求之后 , 完成请求任务之后才给出反馈就是阻塞, 受到请求之后马上给出反馈然后再去执行请求任务则为非阻塞.
7.1 WebFlux优势
-
非阻塞式 : 在有限的资源下 , 提高系统吞吐量和伸缩性 , 以Reactor为基础实现响应式编程.
-
函数式编程
7.2 与WebMVC的区别
-
SpringMVC采用命令式编程 , WebFlux异步响应式编程.
-
两者都可以使用注解方式,都运行在Tomcat等容器中.
8.WebFlux-Code
8.1 响应式编程
电子表格程序就是响应式编程的一个例子 , 单元格可以包含字面值或者类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化.
8.2 观察者模式
java8提供的观察者模式中的两个类Observer和Observable
public class ObserveDemo extends Observable {
public static void main(String[] args) {
ObserveDemo observer = new ObserveDemo();
//添加观察者
observer.addObserver((ob,arg)-> System.out.println("发送变化A"));
observer.addObserver((ob,arg)-> System.out.println("发送变化B"));
observer.setChanged(); //数据变化
observer.notifyObservers(); //通知
}
}
9.webFlux-Reactor
-
响应式编程操作中 , Reactor是满足Reactive规范的框架.
-
Reactor有两个核心类 , Mono和Flux , 这两个类实现接口Publisher , 提供丰富操作 , Flux对象实现发布者 , 返回N个元素 , Mono实现发布者 , 返回0或者1个元素.
-
Flux和Mono都是数据流的发布者 , 使用Flux和Mono都可以发出三种数据信号
-
元素值
-
错误信号
-
完成信号
-
错误信号和完成信号都代表终止信号 , 终止信号用于告诉订阅者数据流结束了 , 错误信号终止数据流同事把错误传递给订阅者.
-
-
-
错误信号和完成信号都是终止信号 , 不能共存.
-
如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流.
-
如果没有错误信号,没有完成信号,表示是无限数据流.
9.1 Code
pom
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.3.16.RELEASE</version>
</dependency>
psvm
public class TestReactor {
public static void main(String[] args) {
//Object
Flux.just(1,2,3,4);
Mono.just(1);
//数组
Integer[] array = {1,2,3,4};
Flux.fromArray(array);
//集合
List<Integer> list = Arrays.asList(array);
Flux.fromIterable(list);
//流
Stream<Integer> stream = list.stream();
Flux.fromStream(stream);
}
}
发布订阅
public class TestReactor {
public static void main(String[] args) {
//Object
Flux.just(1,2,3,4).subscribe((o)-> System.out.println(o));
Mono.just(1).subscribe((o)-> System.out.println(o));
}
}
调用just或者其他方法只是声明数据流 , 数据流并没有发出 , 只有进行订阅之后才会触发数据流 , 不订阅什么都不会发生.
9.2 操作符
-
map元素映射为新的元素
- flatMap元素映射为流
把每个元素转换成流 , 把转换之后多个流合并成大的流.
10.WebFlux-执行流程和核心API
SpringWebflux 基于 Readctor , 默认使用容器是Netty , Netty是高性能的NIO框架 , 异步非阻塞的框架.
NIO
10.1 执行流程
SpringWebFlux核心控制器DispatchHandler , 实现接口WebHandler.
10.2 核心API
SpringWebflux 里面 DispatcherHandler , 负责请求的处理.
HandlerMapping : 请求查询到处理的方法.
HandlerAdapter : 真正负责请求处理.
HandlerResultHandler L相应结果处理.
-
SpringWebflux 实现函数式编程 , 两个接口 : RounterFunction (路由处理) 和 HandlerFunction(处理函数)
11.WebFlux-注解编程模型
SpringWebflux 实现方式有两种 , 注解编程模型和函数式编程模型.
使用注解编程模型方式和之前SpringMVC使用相似 , 只需要把相关依赖配置到项目中, SpringBoot自动配置相关运行容器 ,
默认情况下使用Netty服务器.
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
private final Map<Integer,User> userMap = new HashMap<>();
public UserServiceImpl() {
userMap.put(1,new User("wwn","m",50));
userMap.put(2,new User("gnx","f",60));
userMap.put(3,new User("xjp","m",70));
}
@Override
public Mono<User> findById(Integer id) {
return Mono.justOrEmpty(this.userMap.get(id));
}
@Override
public Flux<User> findAll() {
return Flux.fromIterable(this.userMap.values());
}
@Override
public Mono<Void> saveUser(Mono<User> userMono) {
return userMono.doOnNext(user ->{
int id = userMap.size() + 1;
userMap.put(id,user);
}).then(Mono.empty());//终止信号
}
}
UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/info/{id}")
public Mono<User> getById(@PathVariable("id") Integer id){
return userService.findById(id);
}
@GetMapping("/all")
public Flux<User> getAll(){
return userService.findAll();
}
@PostMapping("/save")
public Mono<Void> saveUser(@RequestBody User user){
Mono<User> mono = Mono.just(user);
return userService.saveUser(mono);
}
}
11.1 区别
-
SpringMVC方式实现 , 同步阻塞的方式 , 基于 SpringMVC + Servlet + Tomcat
-
SpringWebFlux 方式实现 , 异步非阻塞的方式 , 基于 SpringWebflux + Reactor + Netty
12.Webflux-函数式编程模型(Handler)
-
在使用函数式编程模型的时候 , 需要自己初始化服务器.
-
基于函数式编程模型的时候 , 有两个核心接口 : RouterFunction (实现路由功能 , 请求转发给对应的handler) 和 HandlerFunction(处理请求生成响应的函数).核心任务定义两个函数式接口的实现并且启动需要的服务器.
-
SpringWebFlux请求和响应不再是ServletRequest 和 ServletResponse 而是 ServerRequest 和 ServerResponse
12.1 HandlerFunction
public class UserHandler {
private final UserService userService;
public UserHandler(UserService userService) {
this.userService = userService;
}
/**
* find by id
*/
public Mono<ServerResponse> findById(ServerRequest request){
int userId = Integer.parseInt(request.pathVariable("id"));
//空值判断
Mono<ServerResponse> notFound = ServerResponse.notFound().build();
Mono<User> userMono = userService.findById(userId);
//转换成流
return userMono.flatMap(user ->ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(fromObject(user))).switchIfEmpty(notFound);
}
/**
* find all
* @return
*/
public Mono<ServerResponse> findAllUser(){
Flux<User> users = this.userService.findAll();
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);
}
/**
* add User
*/
public Mono<ServerResponse> saveUser(ServerRequest request){
Mono<User> userMono = request.bodyToMono(User.class);
return ServerResponse.ok().build(this.userService.saveUser(userMono));
}
}
12.2 HandlerFunction
public class Server {
//创建Rounter路由
public RouterFunction<ServerResponse> routingFunction(){
//创建handler对象
UserService userService = new UserServiceImpl();
UserHandler userHandler = new UserHandler(userService);
//设置路由
return RouterFunctions.route(
GET("/user/{id}").and(accept(MediaType.APPLICATION_JSON)),userHandler::findById).andRoute(
GET("/user").and(accept(MediaType.APPLICATION_JSON)),userHandler::findAllUser);
}
}
12.3 服务器适配
//创建服务器完成适配
public void createReactorServer(){
//路由和handler适配
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
public class Server {
//创建Rounter路由
public RouterFunction<ServerResponse> routingFunction(){
//创建handler对象
UserService userService = new UserServiceImpl();
UserHandler userHandler = new UserHandler(userService);
//设置路由
return RouterFunctions.route(GET("/user/{id}").and(accept(MediaType.APPLICATION_JSON)),userHandler::findById).andRoute(
GET("/user").and(accept(MediaType.APPLICATION_JSON)),userHandler::findAllUser);
}
//创建服务器完成适配
public void createReactorServer(){
//路由和handler适配
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
}
psvm
public static void main(String[] args) throws IOException {
Server server = new Server();
server.createReactorServer();
System.out.println("enter to exit");
System.in.read();
}
package com.nanoswarm.webfluxdemo.server;
import com.nanoswarm.webfluxdemo.handler.UserHandler;
import com.nanoswarm.webfluxdemo.service.UserService;
import com.nanoswarm.webfluxdemo.service.impl.UserServiceImpl;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.netty.http.server.HttpServer;
import java.io.IOException;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.toHttpHandler;
public class Server {
public static void main(String[] args) throws IOException {
Server server = new Server();
server.createReactorServer();
System.out.println("enter to exit");
System.in.read();
}
//创建Rounter路由
public RouterFunction<ServerResponse> routingFunction(){
//创建handler对象
UserService userService = new UserServiceImpl();
UserHandler userHandler = new UserHandler(userService);
//设置路由
return RouterFunctions.route(GET("/user/{id}").and(accept(MediaType.APPLICATION_JSON)),userHandler::findById).andRoute(
GET("/user").and(accept(MediaType.APPLICATION_JSON)),userHandler::findAllUser);
}
//创建服务器完成适配
public void createReactorServer(){
//路由和handler适配
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
//创建服务器
HttpServer httpServer = HttpServer.create();
httpServer.handle(adapter).bindNow();
}
}
13 webClient-调用
public class Client {
public static void main(String[] args) {
//调用服务器地址
WebClient webClient = WebClient.create("http://127.0.0.1:63676");
//根据id查询
String id = "1";
User userResult = webClient.get().uri("/user/{id}", id).
accept(MediaType.APPLICATION_JSON).
retrieve()
.bodyToMono(User.class)
.block();
System.out.println(userResult.getName());
//查询所有
Flux<User> usersResult = webClient.get().uri("/user").
accept(MediaType.APPLICATION_JSON).
retrieve()
.bodyToFlux(User.class);
usersResult.map(user -> user.getName()).buffer().doOnNext(System.out::println).blockFirst();
}
}