SpringBoot 基础入门
Spring Boot简介
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的创建、运行、调试、部署等。使用Spring Boot可以做到专注于Spring应用的开发,而无需过多关注XML的配置。Spring Boot遵循“ 习惯优于配置”的理念,简单来说,它提供了一堆依赖打包,并已经按照使用习惯解决了依赖问题。使用Spring Boot可以不用或者只需要很少的Spring配置就可以让企业项目快速运行起来。
Spring Boot特点:
-
创建独立的Spring应用程序
-
嵌入的Tomcat,无需部署WAR文件
-
简化Maven配置
-
自动配置Spring
-
提供生产就绪型功能,如指标,健康检查和外部配置
-
绝对没有代码生成以及对XML没有要求配置
如果使用的IntelliJ IDEA,则无需下载插件,如果是使用Eclipse开发,没有SpringBoot插件的话,还是需要下载(这里可跳过…):
接受后,点击Finish,然后等待下载完毕,最后重启。
新建一个Spring Boot工程:
自动生成的目录结构如下:
在com.wu.application下再次新建一个controller包,并新建一个MyController类:
package com.wu.application.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/boot")
public class MyController {
@ResponseBody
@RequestMapping("/sayhello")
public String sayHello() {
return "Hello,Spring Boot 2!";
}
}
以上简单的几行语句就相当于SSM整合的几十甚至上百行代码,因为SSM整合需要编写许多配置文件,极其繁琐。
在application.properties中修改端口号和默认访问路径:
# 配置服务器的端口号
server.port=8888
# 配置服务器的默认访问路径
server.servlet.context-path=/app
Spring Boot不但能创建传统的war包应用,还能创建独立的不依赖于任何外部容器(比如Tomcat)的独立应用。
首先检查pom.xml文件:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wu</groupId>
<artifactId>springboot-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot-1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<!-- 无需关注版本号,自动版本仲裁 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
如果要打jar包,就修改为<packaging>jar</packaging>,如果要打成war包,就修改为<packaging>war</packaging>
右击项目,点击Run As,选择Maven Clean,清除编译后的目录,默认是target目录:
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< com.wu:springboot-1 >-------------------------
[INFO] Building springboot-1 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ springboot-1 ---
[INFO] Deleting E:\javaworkspace\springboot-1\target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.820 s
[INFO] Finished at: 2021-01-29T10:12:40+08:00
[INFO] ------------------------------------------------------------------------
[WARNING] The requested profile "pom.xml" could not be activated because it does not exist.
然后再次点击Run As,选择Maven Build,对Goals框输入内容 package:
可以发现,在项目目录下的target文件下出现了已经打好了的jar包:
文件路径搜索框搜索cmd,并输入java -jar springboot-1-0.0.1-SNAPSHOT.jar 命令:
对浏览器输入http://localhost:8888/app/boot/sayhello:
总结:约定优于配置,简洁胜于复杂,当学习完了SSH或SSM感触深刻,因为那就是配置的地狱。
@Configuration注解
创建两个pojo类:
package com.wu.application.pojo;
public class Student {
private String name;
private Long id;
private Integer age;
private Book book;
public Student(String name,Long id,Integer age,Book book) {
this.name = name;
this.id = id;
this.age = age;
this.book = book;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
@Override
public String toString() {
return "Student [name=" + name + ", id=" + id + ", age=" + age + ", book=" + book + "]";
}
}
package com.wu.application.pojo;
public class Book {
private String bookName;
private Long bookId;
public Book(String bookName,Long bookId) {
this.bookName = bookName;
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public Long getBookId() {
return bookId;
}
public void setBookId(Long bookId) {
this.bookId = bookId;
}
@Override
public String toString() {
return "Book [bookName=" + bookName + ", bookId=" + bookId + "]";
}
}
创建配置类:
将实例对象交给spring容器来统一管理
package com.wu.application.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.wu.application.pojo.Book;
import com.wu.application.pojo.Student;
@Configuration(proxyBeanMethods = true) // 默认情况下的配置类(等价于配置文件)为单例模式 : 使用代理模式得到强化
public class MyConfig {
@Bean("student")
public Student getStudentBean() {
return new Student("张三",20210129L,20,getBookBean());
}
@Bean("book")
public Book getBookBean() {
return new Book("一本书",0001L);
}
}
应用入口类:
package com.wu.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.wu.application.pojo.Book;
import com.wu.application.pojo.Student;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApplication.class, args);
Student student = context.getBean("student",Student.class);
Book book = context.getBean("book",Book.class);
System.out.println("Spring容器中的bean为单例模式:"+(student.getBook() == book));
}
}
结果:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.2)
2021-01-29 17:42:33.822 INFO 1452 --- [ main] com.wu.application.MyApplication : Starting MyApplication using Java 14.0.2 on PC-20200901HLGR with PID 1452 (E:\javaworkspace\springboot-1\target\classes started by Administrator in E:\javaworkspace\springboot-1)
2021-01-29 17:42:33.826 INFO 1452 --- [ main] com.wu.application.MyApplication : No active profile set, falling back to default profiles: default
2021-01-29 17:42:35.089 INFO 1452 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8888 (http)
2021-01-29 17:42:35.102 INFO 1452 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-01-29 17:42:35.102 INFO 1452 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-01-29 17:42:35.213 INFO 1452 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/app] : Initializing Spring embedded WebApplicationContext
2021-01-29 17:42:35.213 INFO 1452 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1315 ms
2021-01-29 17:42:35.453 INFO 1452 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-01-29 17:42:35.737 INFO 1452 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8888 (http) with context path '/app'
2021-01-29 17:42:35.752 INFO 1452 --- [ main] com.wu.application.MyApplication : Started MyApplication in 2.388 seconds (JVM running for 3.517)
Spring容器中的bean为单例模式:true
如果将@Configuration中的proxyBeanMethods设置为false,则上面的结果就会变为false,所以如果组件之间形成依赖关系,则使用默认的单例模式,如果没有依赖关系的话,最好使用多例模式,因为这样的模式速度会变得更快。
@Import注解
@Import注解可以用于导入第三方包 ,虽然@Bean注解也可以,但是@Import注解快速导入的方式会显得更加便捷
package com.wu.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import com.wu.application.pojo.Book;
import com.wu.application.pojo.Student;
@Import({com.wu.application.pojo.Student.class,com.wu.application.pojo.Book.class})
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApplication.class, args);
Student student = context.getBean(Student.class);
Book book = context.getBean(Book.class);
System.out.println("学生:"+student);
System.out.println("书籍:"+book);
}
}
结果:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.2)
2021-01-29 18:14:12.375 INFO 2428 --- [ main] com.wu.application.MyApplication : Starting MyApplication using Java 14.0.2 on PC-20200901HLGR with PID 2428 (E:\javaworkspace\springboot-1\target\classes started by Administrator in E:\javaworkspace\springboot-1)
2021-01-29 18:14:12.379 INFO 2428 --- [ main] com.wu.application.MyApplication : No active profile set, falling back to default profiles: default
2021-01-29 18:14:13.456 INFO 2428 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8888 (http)
2021-01-29 18:14:13.469 INFO 2428 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-01-29 18:14:13.470 INFO 2428 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41]
2021-01-29 18:14:13.577 INFO 2428 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/app] : Initializing Spring embedded WebApplicationContext
2021-01-29 18:14:13.577 INFO 2428 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1132 ms
2021-01-29 18:14:13.776 INFO 2428 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-01-29 18:14:14.024 INFO 2428 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8888 (http) with context path '/app'
2021-01-29 18:14:14.036 INFO 2428 --- [ main] com.wu.application.MyApplication : Started MyApplication in 2.133 seconds (JVM running for 3.041)
学生:Student [name=null, id=null, age=null, book=null]
书籍:Book [bookName=null, bookId=null]
@Conditional注解
package com.wu.application.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.wu.application.pojo.Book;
import com.wu.application.pojo.Student;
@Configuration(proxyBeanMethods = true) // 默认情况下的配置类(等价于配置文件)为单例模式 : 使用代理模式得到强化
public class MyConfig {
@Bean("student")
public Student getStudentBean() {
return new Student("张三",20210129L,20,getBookBean());
}
// 注意这里没有使用@Bean("book")标签
public Book getBookBean() {
return new Book("一本书",0001L);
}
}
package com.wu.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApplication.class, args);
boolean existStudent = context.containsBean("student");
boolean existBook = context.containsBean("book");
System.out.println("学生存在容器中:"+existStudent);
System.out.println("书籍存在容器中:"+existBook);
}
}
结果:
学生存在容器中:true
书籍存在容器中:false
如果使用@ConditionalOnBean:
@Configuration(proxyBeanMethods = true) // 默认情况下的配置类(等价于配置文件)为单例模式 : 使用代理模式得到强化
public class MyConfig {
@ConditionalOnBean(name = "book")
@Bean("student")
public Student getStudentBean() {
return new Student("张三",20210129L,20,getBookBean());
}
@Bean("book")
public Book getBookBean() {
return new Book("一本书",0001L);
}
}
结果:
学生存在容器中:false
书籍存在容器中:false
之所以为以上结果,是因为使用了条件注解,只有当spring容器中已经事先存在具有name为"book"的组件,才能使得以下的组件注册成功。
相应的,还有许多其它的类似的条件注解,我们只需按照字面的英语意思理解使用即可。
@ImportResource
通过导入资源文件来将组件注册进入spring容器中进行统一管理。
在resource文件夹下建立bean文件夹,在其下建立spring-bean.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">
<bean id = "student" class = "com.wu.application.pojo.Student">
<property name="name" value = "张三"/>
<property name="id" value = "20210129"/>
<property name="age" value = "20"/>
<property name="book" ref = "book" />
</bean>
<bean id = "book" class = "com.wu.application.pojo.Book">
<property name="bookName" value = "一本书"/>
<property name="bookId" value = "10001"/>
</bean>
</beans>
配置类:
package com.wu.application.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@ImportResource(value = "classpath:/bean/spring-bean.xml")
@Configuration // 默认情况下的配置类(等价于配置文件)为单例模式 : 使用代理模式得到强化
public class MyConfig {
}
}
MyApplication.java:
package com.wu.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.wu.application.pojo.Book;
import com.wu.application.pojo.Student;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApplication.class, args);
Student student = context.getBean("student",Student.class);
Book book = context.getBean("book",Book.class);
System.out.println(student);
System.out.println(book);
}
}
结果:
Student [name=张三, id=20210129, age=20, book=Book [bookName=一本书, bookId=10001]]
Book [bookName=一本书, bookId=10001]
@ConfigurationProperties注解
第一种:@ConfigurationProperties+@Component
配置绑定
首先在pom.xml文件中添加如下内容:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
对application.properties进行修改:
# 自定义配置
mybook.bookName=一本书
mybook.bookId=999
pojo类:
package com.wu.application.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component(value = "book")
@ConfigurationProperties(prefix = "mybook")
public class Book {
private String bookName;
private Long bookId;
public Book() {}
public Book(String bookName,Long bookId) {
this.bookName = bookName;
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public Long getBookId() {
return bookId;
}
public void setBookId(Long bookId) {
this.bookId = bookId;
}
@Override
public String toString() {
return "Book [bookName=" + bookName + ", bookId=" + bookId + "]";
}
}
应用入口类:
package com.wu.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.wu.application.pojo.Book;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApplication.class, args);
Book book = context.getBean("book",Book.class);
System.out.println(book);
}
}
结果:
Book [bookName=张三, bookId=999]
第二种:@EnableConfigurationProperties+@ConfigurationProperties
pojo类:
package com.wu.application.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "mybook")
public class Book {
private String bookName;
private Long bookId;
public Book() {}
public Book(String bookName,Long bookId) {
this.bookName = bookName;
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public Long getBookId() {
return bookId;
}
public void setBookId(Long bookId) {
this.bookId = bookId;
}
@Override
public String toString() {
return "Book [bookName=" + bookName + ", bookId=" + bookId + "]";
}
}
配置类:
package com.wu.application.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import com.wu.application.pojo.Book;
@Configuration // 默认情况下的配置类(等价于配置文件)为单例模式 : 使用代理模式得到强化
@EnableConfigurationProperties(Book.class) // 1.开启属性配置功能 2.使得该组件自动注册到spring容器中
public class MyConfig {
}
应用入口类:
package com.wu.application;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.wu.application.pojo.Book;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApplication.class, args);
Book book = context.getBean("mybook-com.wu.application.pojo.Book",Book.class);
System.out.println(book);
}
}
结果:
Book [bookName=张三, bookId=999]
通过Value注解获取自定义配置
首先在application.yml中添加自定义属性语句:
student:
name: 张三
age: 20
id: 2021
MyController.java:
package com.wu.springboot.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/home")
public class MyController {
@Value("${student.name}")
String studentName;
@Value("${student.age}")
int studentAge;
@Value("${student.id}")
long studentId;
@RequestMapping(value = "/info")
@ResponseBody
public String studentInfo(){
return "学生姓名:"+studentName+",学生年龄:"+studentAge+",学生学号:"+studentId;
}
}
结果:
事务处理
SpringBoot&Mybatis-Plus实现转账事务为例
事务实现是通过在启动类上添加注解@EnableTransactionManagement以及在相应类或者相应方法上添加@Transactional注解
package org.wu.project.controlle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.wu.project.mapper.AccountMapper;
import org.wu.project.service.AccountService;
@RestController
public class AccountController {
private AccountService accountService;
@Autowired
@Qualifier("accountServiceBean")
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
@RequestMapping("/transfer")
public boolean transfer(String senderId,String receiverId,Integer money){
try {
accountService.transfer(senderId, receiverId, money);
return true;
}catch(Exception e){
e.printStackTrace();
}
return false;
}
}
package org.wu.project.entity;
import lombok.Data;
@Data
public class Account {
private String id;
private String userName;
private int money;
}
package org.wu.project.mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository("accountMapperBean")
public interface AccountMapper {
void increaseMoney(@Param("id") String id,@Param("money") Integer money);
void reduceMoney(@Param("id") String id,@Param("money") Integer money);
}
package org.wu.project.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.wu.project.mapper.AccountMapper;
@Service("accountServiceBean")
public class AccountService {
private AccountMapper accountMapper;
@Autowired
@Qualifier("accountMapperBean")
public void setAccountMapper(AccountMapper accountMapper) {
this.accountMapper = accountMapper;
}
@Transactional
public void transfer(String senderId,String receiverId,Integer money){
accountMapper.increaseMoney(receiverId,money);
accountMapper.reduceMoney(senderId,money);
}
}
package org.wu.project;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("org.wu.project.mapper")
public class ProjectApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectApplication.class, args);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "org.wu.project.mapper.AccountMapper">
<update id="increaseMoney">
update account set money = #{money} + money
where id = #{id}
</update>
<update id="reduceMoney">
update account set money = money - #{money}
where id = #{id}
</update>
</mapper>
# 应用名称
spring.application.name=project
# 应用服务 WEB 访问端口
server.port=8888
#下面这些内容是为了让MyBatis-Plus映射
#指定Mybatis-Plus的Mapper文件
mybatis-plus.mapper-locations=classpath:mappers/*.xml
#指定Mybatis-Plus的实体目录
mybatis-plus.type-aliases-package=org.wu.project.entity
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=root
转账事务处理之前:
转账事务处理之后(本地局域网主机输入网址http://localhost:8888/transfer?senderId=001&receiverId=002&money=200):
然后模拟一个异常来使得该事务发生中断,重新输入url:
结果为保持以上的money:
进一步扩展事务有关的细节:
@Transactional相关的属性
参数名称 | 功能描述 |
readOnly | 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
rollbackFor | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
rollbackForClassName | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如: 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如: 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException") 指定多个异常类名称: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) |
propagation | 该属性用于设置事务的传播行为,具体取值可参考表6-7。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
isolation | 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
timeout | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
- 脏读:并发事务A读到了并发事务B还未提交的数据
- 不可重复读: 在一个并发事务里面读取了两次某个数据,读出来数据不一致
- 幻读: 在一个并发事务里面的操作中实现了不符合预期的数据(另一个并发事务改变了数据数量… ),幻读出现的前提是并发的事务中发生了插入、删除操作
SpringBoot工程多环境配置
一个产品从开发到用户使用一般会涉及到以下四个环境:
- 开发环境
- 测试环境
- 准生产环境
- 生产环境
建立四个以application-作为前缀的配置文件
在主配置文件application.yml中添加如下语句作为环境的选择:
#以下选择为开发环境
spring.profiles.active=dev
配置文件YAML类型
简介
YAML(读音:呀们) 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。 YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲。YAML 的配置文件后缀为 .yml或 .yaml
基本语法
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- '#'表示注释
用法
- 字面量意思
key: value
- 对象
# 写法一:行内写法
key: {key1:value1,key2:value2}
# 写法二:缩进写法
key:
key1: value1
key2: value2
- 数组
# 写法一:行内写法
key: [value1,value2]
# 写法二:缩进写法
key:
- value1
- value2
这里需要注意的地方为:单引号会将字符串中的转义字符原封不动地输出,而双引号则会解析转义字符
自定义类绑定的配置提示
在main文件夹下建立webapp文件夹,并将其设置为资源文件夹:
pom.xml中加入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
打包排除:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
配置复杂属性
application.yml:
others:
users:
- name: 张三
address: 中国
contact:
- QQ
- 微信
- name: 李四
address: 日本
contact:
- 邮箱
- 电话
Users.java:
package com.wu.springboot.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "others")
public class Users {
private List<User> users;
public List<User> getUsers(){
return users;
}
public void setUsers(List<User> users){
this.users = users;
}
@Override
public String toString() {
return "Users{" +
"users=" + users +
'}';
}
}
class User{
private String name;
private String address;
private List<String> contact;
public String getName() {
return name;
}
public String getAddress(){
return address;
}
public List<String> getContact(){
return contact;
}
public void setName(String name){
this.name = name;
}
public void setAddress(String address){
this.address = address;
}
public void setContact(List<String> contact){
this.contact = contact;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", contact=" + contact +
'}';
}
}
MyController:
package com.wu.springboot.cotroller;
import com.wu.springboot.pojo.Book;
import com.wu.springboot.pojo.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/")
public class MyController {
@Autowired
Users users;
@GetMapping("/others")
public String others(){
return users.toString();
}
}
结果:
SpringBoot集成传统jsp页面
pom.xml:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wu.springboot</groupId>
<artifactId>springboot-01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-01</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- springboot内嵌Tomcat对jsp的解析依赖 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
</dependencies>
<build>
<!-- 指定jsp文件编译的路径META-INF/resources -->
<resources>
<resource>
<!-- 指定源文件路径 -->
<directory>src/main/webapp</directory>
<!-- 指定编译路径 -->
<targetPath>META-INF/resources</targetPath>
<includes>
<include>*.*</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.2</version>
</plugin>
</plugins>
</build>
</project>
application.yml:
spring:
mvc:
view:
suffix: .jsp
prefix: /
MyController.java:
package com.wu.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/home")
public class MyController {
@RequestMapping(value = "/info")
public String index(Model model){
model.addAttribute("info","Hello World!");
return "index";
}
}
index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>${info}</h1>
</body>
</html>
SpringBoot集成Thymeleaf
Thymeleaf是新一代Java模板引擎,支持HTML原型,既可以让前端工程师在浏览器中直接打开查看样式,也可以让后端工程师结合真实数据查看显示效果。使用其余的模板技术时,整合方式同Thymeleaf类似,比如FreeMarker大致也是如此,如果读者使用的是目前流行的前后端分离技术(比如Vue,React),那么在开发过程中不需要整合视图层技术,后端提供接口即可。
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
application.yml:
server:
port: 8888
# Thymeleaf选项自定义配置
spring:
thymeleaf:
cache: false # 缓存关闭,一般生产环境中需要关闭
check-template: true # 检查模板是否存在
check-template-location: true # 检查模板位置是否存在
encoding: UTF-8 # 模板文件编码为UTF-8
prefix: classpath:/templates/ # 模板文件位置
suffix: .html # 模板文件后缀
servlet:
content-type: text/html # Content-type类型配置
Book.java:
package com.wu.springboot.pojo;
public class Book {
private String name;
private String author;
private Float price;
public String getName() {
return name;
}
public String getAuthor() {
return author;
}
public Float getPrice(){
return price;
}
public void setName(String name){
this.name = name;
}
public void setAuthor(String author){
this.author = author;
}
public void setPrice(Float price){
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
MyController.java:
package com.wu.springboot.cotroller;
import com.wu.springboot.pojo.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.List;
@Controller
@RequestMapping("/")
public class MyController {
@GetMapping("/books")
public ModelAndView books(){
List<Book> books = new ArrayList<Book>();
Book book1 = new Book();
book1.setAuthor("张三");
book1.setName("张三传");
book1.setPrice(new Float(100.5));
Book book2 = new Book();
book2.setAuthor("李四");
book2.setName("李四记");
book2.setPrice(new Float(150.0));
books.add(book1);
books.add(book2);
ModelAndView mv = new ModelAndView();
mv.addObject("books",books);
mv.setViewName("books");
return mv;
}
}
books.html:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>书籍信息</title>
</head>
<body>
<div align="center">
<table border="1">
<tr>
<td>书籍名称</td>
<td>书籍作者</td>
<td>书籍价格</td>
</tr>
<tr th:each="book:${books}">
<td th:text="${book.name}"></td>
<td th:text="${book.author}"></td>
<td th:text="${book.price}"></td>
</tr>
</table>
</div>
</body>
</html>
结果:
SpringBoot整合log4j2日志
日志级别
机制:当一条日志信息的级别大于等于配置文件的级别,就生成日志记录。
- trace:追踪,就是程序推进一下,可以写个trace输出,最不常用
- debug:调试级别,一般作为最低级别
- info:输出重要的信息,较常用
- warn:警告级别
- error:错误级别
- fatal:致命级别
输出源
- CONSOLE(输出到控制台)
- FILE(输出到文件)
输出格式
- SimpleLayout:以简单的格式显示
- HTMLLayout:以HTML表格格式显示
- PatternLayout:自定义格式显示
PatternLayout自定义日志格式
%d{yyyy-MM-dd HH:mm:ss} : 日志生成时间格式
%-5level : 日志输出级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
%c : 日志输出logger的名称(%logger)
%t : 日志输出当前线程名称
%p : 日志输出格式
%m : 日志内容,即 logger.info(“message”)
%n : 日志换行符
%C : 日志输出Java类名(%F)
%L : 日志输出行号
%M : 日志输出方法名
%l : 日志输出语句所在的行数, 包括类名、方法名、文件名、行数
hostName : 日志输出本地机器名
hostAddress : 日志输出本地IP地址
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除springboot默认日志配置 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>2.5.1</version>
</dependency>
log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy年MM月dd日 HH时mm分ss秒} [线程:%t 方法:%M] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
package org.wu.project.controlle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AccountController {
Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);
@RequestMapping("/test")
public void test(){
logger.info("测试信息打印");
}
控制台输出日志信息:
2021-10-31 14:56:43,442 main INFO Log4j appears to be running in a Servlet environment, but there's no log4j-web module available. If you want better web container support, please add the log4j-web JAR to your web archive or server lib directory.
__ _____ __ __ ___ _____ _
\ \ / (_) \ / / \ \ / (_) / ____| |
\ \_/ / _ \ \_/ /__ \ V / _ _ __ __ _| | | |__ ___ _ __
\ / | | \ / _ \ > < | | '_ \ / _` | | | '_ \ / _ \ '_ \
| | | | | | __// . \| | | | | (_| | |____| | | | __/ | | |
|_| |_| |_|\___/_/ \_\_|_| |_|\__, |\_____|_| |_|\___|_| |_|
__/ |
|___/
2021年10月31日 14时56分43秒 [线程:main 方法:logStarting] INFO org.wu.project.ProjectApplication - Starting ProjectApplication on yiyexingchen with PID 7616 (E:\onedrive\OneDrive - laosaonan2\桌面\Java\spring-demo01\target\classes started by Localhost in E:\onedrive\OneDrive - laosaonan2\桌面\Java\spring-demo01)
2021年10月31日 14时56分43秒 [线程:main 方法:logStartupProfileInfo] INFO org.wu.project.ProjectApplication - No active profile set, falling back to default profiles: default
2021年10月31日 14时56分44秒 [线程:main 方法:initialize] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat initialized with port(s): 8888 (http)
2021年10月31日 14时56分44秒 [线程:main 方法:log] INFO org.apache.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8888"]
2021年10月31日 14时56分44秒 [线程:main 方法:log] INFO org.apache.catalina.core.StandardService - Starting service [Tomcat]
2021年10月31日 14时56分44秒 [线程:main 方法:log] INFO org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.41]
2021年10月31日 14时56分44秒 [线程:main 方法:log] INFO org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
2021年10月31日 14时56分44秒 [线程:main 方法:prepareWebApplicationContext] INFO org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1203 ms
_ _ |_ _ _|_. ___ _ | _
| | |\/|_)(_| | |_\ |_)||_|_\
/ |
3.4.2
2021年10月31日 14时56分46秒 [线程:main 方法:initialize] INFO org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
2021年10月31日 14时56分46秒 [线程:main 方法:log] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8888"]
2021年10月31日 14时56分46秒 [线程:main 方法:start] INFO org.springframework.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8888 (http) with context path ''
2021年10月31日 14时56分46秒 [线程:main 方法:logStarted] INFO org.wu.project.ProjectApplication - Started ProjectApplication in 3.26 seconds (JVM running for 4.83)
2021年10月31日 14时59分33秒 [线程:http-nio-8888-exec-1 方法:log] INFO org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
2021年10月31日 14时59分33秒 [线程:http-nio-8888-exec-1 方法:initServletBean] INFO org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
2021年10月31日 14时59分33秒 [线程:http-nio-8888-exec-1 方法:initServletBean] INFO org.springframework.web.servlet.DispatcherServlet - Completed initialization in 8 ms
2021年10月31日 14时59分33秒 [线程:http-nio-8888-exec-1 方法:test] INFO - 测试信息打印
自定义类绑定的配置提示依赖实例:
<dependency>
<!-- Bean配置文件提示 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<configuration>
<excludes>
<!-- 打包排除 -->
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
package org.wu.project.entity;
import lombok.Data;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ConfigurationProperties(prefix = "test-object")
@Component
@Data
@ToString
public class TestObject {
String testName;
String testAge;
}