SpringBoot
# 概述
Spring Boot 是 Spring 快速开发的脚手架,Spring Boot 大大优化了 Spring的 ==复杂配置== 和 ==依赖配置版本== 问题
特点
- 独立运行 Spring项目
- 内嵌 tomcat 和 jetty 容器 , 无需打包war文件
- 简化 maven配置
- 无 代码生成/xml配置
- ...
# 首次应用
# Spring Boot无xml应用Web项目
模拟原生的Servlet搭建Web
该配置方式学习了解即可,并非针对学习!!
创建 Maven无骨架项目 IDEA创建Maven项目 (opens new window) (无骨架Java项目)
pom.xml
引入 SpringBoot相关依赖依赖配置 展开
<packaging>war</packaging> <!-- 集中定义依赖版本号 --> <properties> <spring.version>5.1.2.RELEASE</spring.version> <pagehelper.version>4.2.1</pagehelper.version> </properties> <dependencies> <!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build>
建立 模拟web.xml配置 的Java程序类
WebInit
类web.xml文件配置 (比对示例的配置文件
<servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
Java程序配置 (应用代码
public class WebInit implements WebApplicationInitializer { // 初始化配置 @Override public void onStartup(ServletContext servletContext) throws ServletException { // 指定 spring配置类 AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(SpringMVCConfig.class); // 添加Serlvet并指定映射 ServletRegistration.Dynamic springmvc = servletContext.addServlet("springmvc",new DispatcherServlet(context)); springmvc.addMapping("/"); springmvc.setLoadOnStartup(1); } }
SpringMVCConfig类 需要指定加载的 SpringMVC配置的类,在下面
创建 SpringMVC配置类
web.xml文件配置 (比对示例的配置文件
<mvc:annotation-driven /> <context:component-scan base-package="com.sans"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/html/"/> <property name="suffix" value=".html"/> </bean>
Java程序配置 (应用代码
@Configuration @ComponentScan("com.sans") @EnableWebMvc public class SpringMVCConfig implements WebMvcConfigurer { // 组件注解加载 xml:<mvc:default-servlet-handler/> @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } @Bean public ViewResolver viewResolver() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/html/"); viewResolver.setSuffix(".html"); return viewResolver; }
创建 Controller
@Controller public class HelloController { @RequestMapping("/hello") public String hello() { return "test"; } }
在Web项目访问 html/test.html文件 访问即可
项目结构
.
|
├── src
| ├── main
| | ├── java
| | | └── com
| | | ├── controller
| | | | └── HelloController.java
| | | └── config
| | | ├── SpringMVCConfig.java
| | | └── WebInit.java
| | └── resources
| test
└── pom.xml
# Spring Boot启动器应用Web项目
创建 Maven无骨架项目 IDEA创建Maven项目 (opens new window) (无骨架Java项目)
pom.xml
引入 SpringBoot相关依赖<project> ... <!--父工程坐标 web相关应用无需配置再配置版本号(解决版本冲突问题) --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <!--当中包含 tomcat、SpringMVC、...(有关web应用,自动配置)--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> ... </project>
创建
Application
启动器类 全限定名 com.Applicationpackage com; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; // @SpringBootApplication启动类 @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class , args); } }
创建
HelloController
控制器类 全限定名 com.controller.Application (为了方便看出构架结构)package com.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.sql.DataSource; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "hello , Spring Boot..."; } }
启动类启用Main方法,再浏览器访问:http://localhost:8080/hello (页面返回有信息表示成功!)
项目结构
.
|
├── src
| ├── main
| | ├── java
| | | └── com
| | | ├── controller
| | | | └── HelloController.java
| | | └── Application.java
| | └── resources
| test
└── pom.xml
# 基础配置
Spring Boot应用的是全注解配置(Java程序配置
注解:
@Configuration
声明在类上 , 作为配置类(代替xml文件)
@Bean
声明在方法上 , 将方法返回值导入Bean容器(代替 标签)
@value
属性注入(需要属性文件进行搭配应用)
@PropertySource
引入读取外部属性文件
-
引入读取外部属性文件 , 自动注入 成员变量(需要指定前缀 、Properties文件
@EnableConfigurationProperties
自动注入 @ConfigurationProperties类
@EnableAutoConfiguration
开启Spring Boot的自动配置机制
@RequestMapping
负责URL路由映射 , 可以添加映射规则(请求类型/响应体/请求头/...)-
响应的对象都会转换为JSON格式
@Component
把类对象实例化到 Bean容器 (类似于 @Service
@PostConstruct
在配置类 执行的构造函数 和 自动注入 后执行初始化的方法(类似servlet的init()方法)
-
Servcie事务业务异常回滚应用
@Repository
DAO数据访问的层面 , 一般用于JDBC , 如果当中出现了异常很好的被 Service进行处理该异常
# Bean配置
通过 Spring Boot 还原 Spring配置连接池 的操作
Spring Bean配置
<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
Spring Boot Bean配置
在以上篇章 首次应用的代码 基础上进行更改
pom.xml
引入 Druid连接池依赖<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency>
创建
jdbc.properties
文件 到resources
资源中jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/test jdbc.username=root jdbc.password=root
创建
JdbcConfig
配置类//配置类 @Configuration //读取数据文件 @PropertySource("classpath:jdbc.properties") public class JdbcConfig { /** 属性 * 注入属性 */ @Value("${jdbc.driverClassName}") String driverClassName; @Value("${jdbc.url}") String url; @Value("${jdbc.username}") String username; @Value("${jdbc.password}") String password; /** 对象 * 声明的Bean方法返回值会加入到Spring容器中 * 说明: * - 注解方法 Bean对象 * - 默认对象名id(Bean的ID) 与 方法名 一致 * - 自定义Bean对象名,可在注解 @Bean("自定义名称"),指定新对象 */ @Bean public DataSource dataSource() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; } }
在
HelloController
控制类 自动注入测试@RestController public class HelloController { // 该注解自动匹配名称进行指定注入 @Autowired private DataSource dataSource; @GetMapping("/hello") public String hello() { return "hello , Spring Boot..." + dataSource; } }
启用Main方法调试测试。可利用断点进行查看配置连接池的参数传递情况
# Bean读取
SpringBoot Bean读取方式有 :
方式 | 说明 |
---|---|
@Autowired | 根据 对象类型 进行匹配 获取Bean |
@Resource | 根据 setter方法 进行匹配 获取Bean |
getBean() | 根据 指定参数匹配获取Bean |
Constructor 注入 | 构造器注入 |
getBean()方法重载有 :
getBean(String name)
指定Bean标记的名称获取
getBean(Class<T> clazz)
根据Bean的类型获取Bean , 如果同一类型有多个Bean , 会抛出异常
getBean(String name, Class<T> clazz)
根据名称和类型获取Bean
<T> T getBean(Class<T> clazz, Object... args)
根据类型和构造器参数获取Bean
...
非Spring环境下获取Bean
SpringUtil 工具类 点击展开
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.StringUtils;
/**
* spring工具类 方便在非spring管理环境中获取bean
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
/** Spring应用上下文环境 Bean定义的后置处理器 */
private static ConfigurableListableBeanFactory beanFactory;
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name)
{
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
}
/**
* 获取当前的环境配置,无配置返回null
*
* @return 当前的环境配置
*/
public static String[] getActiveProfiles()
{
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
*
* @return 当前的环境配置
*/
public static String getActiveProfile()
{
final String[] activeProfiles = getActiveProfiles();
return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
}
/**
* 获取配置文件中的值
*
* @param key 配置文件的key
* @return 当前的配置文件的值
*
*/
public static String getRequiredProperty(String key)
{
return applicationContext.getEnvironment().getRequiredProperty(key);
}
}
# Constructor 注入
这个是强制依赖 , 是在构造器中提供的依赖 , 构造器必须有这依赖Bean , 并且依赖的对象受 final
修饰 (防止循环依赖)
示例
@Controller
public class HelloController {
private final AlphaService alphaService;
private final BetaService betaService;
@Autowired
public HelloController(AlphaService alphaService, BetaService betaService) {
this.alphaService = alphaService;
this.betaService = betaService;
}
}
搭配lombok @RequiredArgsConstructor
@RequiredArgsConstructor
public class VerifyController {
private final VerifyService verifyService;
private final InvitationService invitationService;
private final VerificationCodeService verificationCodeService;
}
# Controller配置
Controller配置中 Spring提供有以下两种注解进行应用
@Controller
请求包含有 页面和数据@RestController
请求只有 数据 (前后端分离模式)
一般情况下使用的
@RestController
居多 , 前后端分离嘛 .@Controller
则需要搭配模板引擎进行应用
参数传递 直接上代码 , 详细了解进入 SpringMVC参数传递篇
@RestController
public class ParamsController {
// http://localhost:8080/getTest1
@RequestMapping(value = "getTest1", method = RequestMethod.GET)
public String getTest1() {
return "GET请求1";
}
// http://localhost:8080/getTest2?nickname=zs&phone=123
@RequestMapping(value = "getTest2", method = RequestMethod.GET)
public String getTest2(String nickname, String phone) {
System.out.println("nickname = " + nickname);
System.out.println("phone = " + phone);
return "GET请求2";
}
// http://localhost:8080/getTest3?name=zs
@RequestMapping(value = "getTest3", method = RequestMethod.GET)
public String getTest3(@RequestParam("name") String nickname) {
System.out.println("nickname = " + nickname);
return "GET请求3";
}
// http://localhost:8080/postTest1
@RequestMapping(value = "/postTest1", method = RequestMethod.POST)
public String postTest1() {
return "POST请求1";
}
// http://localhost:8080/postTest2
/*
* application/x-www-form-urlencoded : {
* username:zs,
* password:123
* }
* */
@RequestMapping(value = "/postTest2", method = RequestMethod.POST)
public String postTest2(String username, String password) {
System.out.println("username = " + username);
System.out.println("password = " + password);
return "POST请求2";
}
// http://localhost:8080/postTest3
/*
* application/x-www-form-urlencoded : {
* username:zs,
* password:123
* }
* */
@RequestMapping(value = "/postTest3", method = RequestMethod.POST)
public String postTest3(MyUser user) {
System.out.println(user);
return "POST请求3";
}
// http://localhost:8080/postTest4
/*
application/json : {
"username":"zs",
"password":"123"
}
*/
@RequestMapping(value = "/postTest4", method = RequestMethod.POST)
public String postTest4(@RequestBody MyUser user) {
System.out.println(user);
return "POST请求4";
}
// http://localhost:8080/test/xxx...
@GetMapping("/test/**")
public String test() {
return "通配符请求";
}
}
通配符
*
: 任意单词**
: 任意路径?
: 任意单个字符
通常使用的
*
较多 , 比如指定未知文件名的文件等等...
# 配置文件
SpringBoot 在启动时会将 resources 目录下的 application.properties
/apllication.yml
文件 作为其默认配置文件
.properties 和 .yml 的区别
- 配置结构 : .properties 线性结构 ;.yml 树状结构
- 分割方式 : .properties 以
.
进行分割;.yml 以:
进行分割
.properties配置文件结构
# properties格式
environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App
my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com
.yml配置文件结构
# YAML格式
environments:
dev:
url: http://dev.bar.com
name: Developer Setup
prod:
url: http://foo.bar.com
name: My Cool App
my:
servers:
- dev.bar.com
- foo.bar.com
优先级
目录的配置文件都会被加载,且顺序就是它们的优先级依次降低
- file:
./config/
- file:
./config/*/
- file:
./
- classpath:
/config/
- classpath:
/
相同位置的 application.properties
和 application.yml
,而application.properties
更优先加载
file: 指当前项目根目录
**classpath:**指当前项目的类路径,即 resources 目录
多个配置文件存在的时候且它们有相同的配置内容情况下,SpringBoot会根据优先级进行覆盖低级的配置,形成互补配置:
- 存在相同配置内容,高级覆盖低级的配置内容
- 存在不同的配置内容,所有配置内容都读取
示例:
在以上的基础进行测试
application.yml
文件
# 类路径下的 config 目录下
# 端口号为8090
# 上下文路径为 /test2
server:
port: 8090
servlet:
context-path: /test2
#...
application.properties
文件
server.port=8080
server.servlet.path=/test1
#...
启动 SpringBoot,控制信息:
通过浏览器访问测试
出现以上字样,因本地 test1、test2没有对应的资源
# 读取配置
SpringBoot中的配置也是根据用户定义的约定进行配置 , 唯独与Spring区别 , 就在于它不需要繁琐的xml解析的过程 , 省去了加载所需要的资源 !
SpringBoot读取配置方式 :
参考文章 :
# 单属性读取
@Value 实现读取配置
jdbc.driver.username=root
jdbc.driver.password=root
@Configuration
public class JdbcProperties {
@Value("${jdbc.driver.username}")
private String username;
@Value("${jdbc.driver.password}")
private String password;
}
提示
@Configuration
注解是必不可少 , 启动项目时会实注入
# 对象成员变量读取
大致步骤 :
在配置文件中写好配置
在指定类应用
@ConfigurationProperties
注解的prefix
属性配置前缀 , 匹配 前缀和属性名使映射的属性配置生效 , 有以下两种方式实现
- 启动类 配置扫描加载
@ConfigurationPropertiesScan
注解- 配置类 加载生效类
@EnableConfigurationProperties
注解
- 配置类 加载生效类
- 启动类 配置扫描加载
测试应用
配置文件
jdbc.driver.username=root
jdbc.driver.password=root
引用类
配置约定 : 成员变量名 和 配置后缀名 进行匹配 , 前缀则自行在注解中添加参数prefix
配置 , 例如 :
@Data
@Configuration
@ConfigurationProperties(prefix = "jdbc.driver")
public class JdbcProperties {
private String username;
private String password;
// settre/gettre 方法省略 (一定一定不能少写
}
提示
我采用了lombok 可以使用@Data
, 成员属性的 settre/gettre 方法 , 一定一定不能省略少写
生效配置
有两种实现方法
启动类 (推荐 , SpringBoot2.2以上版本生效)
@ConfigurationPropertiesScan @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
类加载
@EnableConfigurationProperties
注解 , 可写在 启动类/配置类 上加载@EnableConfigurationProperties(JdbcProperties.class) @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
测试
采用 自动注入形式 测试即可
# 业务事务
事务就不做多解释了 . SpringBoot中的业务事务 , 通过 @Transactional
注解 实现
@Transactional常用属性
属性名 | 说明 |
---|---|
propagation | 配置 事务传播行为 |
isolation | 配置 事务隔离级别 |
timeout | 配置 事务超时时间(单位:s) |
readOnly | 配置 事务是否只读 |
rollbackFor | 配置 事务回滚异常 |
一般些在 serviceImpl类使用如下 : (抛异常DDDD)
@Override
@Transactional(rollbackFor = {RuntimeException.class})
public boolean updateBean(Bean bean) {
....
}
# Session集群共享
SpringBoot默认情况下存Session到本机中 , 多台服务器不会共享Session
**解决方案 : **
Spring-Session 和 Redis 整合
引入
redis
和spring-session redis
依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.17.6</version> </dependency>
配好
redis
连接 和Session
存储方式# redis 配置 spring: redis: port: 6379 host: localhost database: 0 session: store-type: redis
Session的读取会通过Redis进行的
# 计划任务
@EnableScheduling
在配置类上使用,开启计划任务的支持
@EnableScheduling
定义指定启动类 application上@Scheduled
定义指定方法 预定执行(运行频率/运行周期等配置...)- 运行即可
# 热部署
在实际开发中会频繁的修改后台类文件 , 而且每次修改都需要重新进行编译运行 , 因此非常的麻烦 .
Spring Boot 提供了 devtools组件 , 热部署正是解决了该问题 , devtools会监听 , classpath下的文件变动 , 触发 Restart类加载器加载该类 , 实现文件和配置热部署功能
热部署应用
pon.xml
<!-- 热部署 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
application.properties
# 热部署 # 启动 热部署 spring.devtools.restart.enabled=true # 重启目录 spring.devtools.restart.additional-paths=src/main/java # classpath目录 修改修改不重启 spring.devtools.restart.exclude=static/**
设置启动自动构建项目 设置 -> 搜索Conpile(编辑器) -> 勾选Build project automatically(启动自动构建项目)
设置运行时执行刷新 ==Ctrl+Shift+Alt+/== -> 进入Registry(注册表) -> 启动compiler.automake.allow.when.app.running(运行时执行)
PS : 如果 4步骤没有该选项 , 则操作一下步骤 : 设置 -> Advansed Settings(高级设置) -> 勾选Allow auto-make(运行时执行)
做完以上配置 , 可以在类中边修改边测试代码啦
# 日志
SpringBoot 选用 SLF4j 和 logback (大厂建议: SLF4j)
SLF4j官方文档 :
- https://springdoc.cn/spring-boot/features#features.logging (opens new window)
- https://www.slf4j.org/manual.html (opens new window)
- https://www.slf4j.org/apidocs/index.html (opens new window)
简单示例
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
# 日志级别
trace
(跟踪) < debug
(调试) < info
(信息) < warn
(警告) < error
(错误)
在配置 info
时 , 仅打印靠后的 warn
, error
(包含本身info)
常用 trace
/ debug
/ info
三种日志输出
# 日志配置
只需在 applicaton.properties
文件 配置即可
配置项 | 值 | 说明 |
---|---|---|
logging.level.** (指定类路径) | (String) info | 控制台打印级别限制解除 |
logging.file.name | (String) my.log | 输出日志到 指定文件(my.log) |
logging.file.path | (String) /var/log | 输出日志到 指定目录的指定文件(my.log) |
logging.pattern.console | (String) | 在控制台输出日志格式 |
logging.pattern.file / logging.config | (String) | 指定文件中日志输出格式 |
基本配置
# 日志配置
logging:
level:
# 根据 maven控制环境 , 按不同环境配置日志级别 (在pom.xml中查看对应环境)
com.bozhu: @logging.level@
org.springframework: warn
# 环境控制
环境控制交给Maven管理 , 在 pom.xml
增加配置 , 标识日志级别
<profiles>
<profile>
<id>local</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active>
<logging.level>debug</logging.level>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active>
<logging.level>debug</logging.level>
</properties>
<activation>
<!-- 默认环境 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>prod</id>
<properties>
<profiles.active>prod</profiles.active>
<logging.level>warn</logging.level>
</properties>
</profile>
</profiles>
# 日志格式
日志格式在默认使用中 一般会很长 , 大部分不是阅读维护查阅的信息 , 因此需要简化格式 .
布局文档 : https://logback.qos.ch/manual/layouts.html (opens new window)
默认格式 : 日期 - 时间 - 日志级别 - 线程id - 线程名称 - 全类名 - 消息
配置日志格式 :
日志格式 | 说明 |
---|---|
%d | 日期时间 |
%thread | 线程名 |
%-5level | 级别显示字符宽度 |
%logger{50} | 日志名最长50 , 否则分割 |
%msg | 日志消息 |
%n | 换行 |
文件配置应用
配置日志
logging.config: classpath:logback-plus.xml
创建
logback-plus.xml
, resources根路径点击展开
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- 存储路径 --> <property name="log.path" value="./logs"/> <!-- 红色日期 绿色线程 高亮级别(5个字符空间) 洋红色类名 日志信息 --> <property name="console.log.pattern" value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> <!-- 去掉高亮颜色 --> <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/> <!-- appender日志输出组件 --> <!-- 控制台输出 --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!-- 控制台输出格式(引用上面格式) --> <encoder> <pattern>${console.log.pattern}</pattern> <charset>utf-8</charset> </encoder> </appender> <!-- 控制台输出 --> <appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/sys-console.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志文件名格式 --> <fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 日志最大 1天 --> <maxHistory>1</maxHistory> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> <charset>utf-8</charset> </encoder> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <!-- 过滤的级别 --> <level>INFO</level> </filter> </appender> <!-- 系统日志输出 --> <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/sys-info.log</file> <!-- 循环政策:基于时间创建日志文件 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志文件名格式 --> <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 日志最大的历史 60天 --> <maxHistory>60</maxHistory> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!-- 过滤的级别 --> <level>INFO</level> <!-- 匹配时的操作:接收(记录) --> <onMatch>ACCEPT</onMatch> <!-- 不匹配时的操作:拒绝(不记录) --> <onMismatch>DENY</onMismatch> </filter> </appender> <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${log.path}/sys-error.log</file> <!-- 循环政策:基于时间创建日志文件 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志文件名格式 --> <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 日志最大的历史 60天 --> <maxHistory>60</maxHistory> </rollingPolicy> <encoder> <pattern>${log.pattern}</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <!-- 过滤的级别 --> <level>ERROR</level> <!-- 匹配时的操作:接收(记录) --> <onMatch>ACCEPT</onMatch> <!-- 不匹配时的操作:拒绝(不记录) --> <onMismatch>DENY</onMismatch> </filter> </appender> <!-- info异步打印追加日志 --> <appender name="async_info" class="ch.qos.logback.classic.AsyncAppender"> <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> <discardingThreshold>0</discardingThreshold> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <queueSize>512</queueSize> <!-- 添加附加的appender,最多只能添加一个 --> <appender-ref ref="file_info"/> </appender> <!-- error异步打印追加日志 --> <appender name="async_error" class="ch.qos.logback.classic.AsyncAppender"> <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> <discardingThreshold>0</discardingThreshold> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <queueSize>512</queueSize> <!-- 添加附加的appender,最多只能添加一个 --> <appender-ref ref="file_error"/> </appender> <!--系统操作日志--> <root level="info"> <!-- 分别引用上面的日志输出组件 --> <appender-ref ref="console" /> <appender-ref ref="async_info" /> <appender-ref ref="async_error" /> <appender-ref ref="file_console" /> </root> </configuration>
测试打印日志即可
# 事件监听
详细篇章 : 点击跳转 (opens new window)
简单示例
点击展开
创建监听对象 MsgEvent
@Data public class MsgDataEvent { // 消息类型 private String type; // 消息内容 private String content; // 发送者 private String sender; // 接收者 private String receiver; }
创建 监听接收
@Slf4j @Service // @Component 也可以 , 只需将对象放入ioc容器即可(Bean管理) public class MsgServiceImpl { @EventListener public void MsgEvent(MsgDataEvent msg) { log.info("监听到消息啦: {}", msg); } }
构建 事件发送
@RestController @RequiredArgsConstructor public class EventTestController { private final ApplicationContext applicationContext; @GetMapping("/event1") public String event1() { MsgDataEvent msg = new MsgDataEvent(); msg.setType("INFO"); msg.setContent("hello world"); msg.setReceiver("system"); msg.setSender("localhost"); applicationContext.publishEvent(msg); return "event1"; } }
发送请求测试即可
# 监听顺序
在监听一个对象多个监听器方法时 , 他们的执行是无序的
通过 @Order
注解 控制排序 , 注解中的val值是有由小到大控制顺序
在以上简单示例的基础上修改
监听接收
@Order(0)
@EventListener
public void MsgEvent1(MsgDataEvent msg) {
log.info("1 => 监听到消息啦: {}", msg);
}
@Order(2)
@EventListener
public void MsgEvent3(MsgDataEvent msg) {
log.info("3 => 监听到消息啦: {}", msg);
}
@Order(1)
@EventListener
public void MsgEvent2(MsgDataEvent msg) {
log.info("2 => 监听到消息啦: {}", msg);
}
# 异步监听
监听事件执行的线程默认是同步 , 必须等待监听器的方法执行完才能继续执行 . 在某些业务场景可能会影响执行时长 (日志打印等..)
通过 @Async
注解 异步实现
异步弊端
异步的线程是无法获取当前请求线程的信息 . 必须通过 传递监听对象 / 缓存 / 静态 等方式传递数据
在以上简单示例的基础上修改
监听接收
@GetMapping("/event1")
public String event1() {
MsgDataEvent msg = new MsgDataEvent();
msg.setType("INFO");
msg.setContent("hello world");
msg.setReceiver("system");
msg.setSender("localhost");
log.info(" => op");
applicationContext.publishEvent(msg);
log.info(" => ed");
return "event1";
}
事件发送
@Async
@EventListener
public void MsgEvent1(MsgDataEvent msg) {
log.info("1 => 监听到消息啦: {}", msg);
}
@Order(2)
@EventListener
public void MsgEvent3(MsgDataEvent msg) {
log.info("3 => 监听到消息啦: {}", msg);
}
@Order(1)
@EventListener
public void MsgEvent2(MsgDataEvent msg) {
log.info("2 => 监听到消息啦: {}", msg);
}
/** 结果
=> op
2 => 监听到消息啦: MsgDataEvent(type=INFO, content=hello world, sender=localhost, receiver=system)
3 => 监听到消息啦: MsgDataEvent(type=INFO, content=hello world, sender=localhost, receiver=system)
=> ed
1 => 监听到消息啦: MsgDataEvent(type=INFO, content=hello world, sender=localhost, receiver=system)
**/
# 事务监听
事务监听能够确保事件是有效避免无效数据执行
通过 @TransactionalEventListener
注解 实现异步 , 当完外部方法事务提交后执行 (更多事务自行查)
在以上简单示例的基础上修改
监听接收
@TransactionalEventListener
@EventListener
public void MsgEvent1(MsgDataEvent msg) {
log.info("1 => 监听到消息啦: {}", msg);
}
@EventListener
public void MsgEvent3(MsgDataEvent msg) {
log.info("3 => 监听到消息啦: {}", msg);
}
/** 结果
3 => 监听到消息啦: MsgDataEvent(type=INFO, content=hello world, sender=localhost, receiver=system)
异常...
**/
在以上示例可以看出异常后 , 不会对事务的方法进行执行
以上笔记做了多次更改 , 如有 理解片面 , 评论指正 , 谢谢!