一、 SpringBoot项目的打包和启动
【问题】:在执行mvn package命令的时候,会先执行test命令,将在test中写的crud的操作都执行一遍,此时会有测试数据的增加,为了避免test操作,可以跳过test执行命令:
1-1、springboot打包插件:spring-boot-maven-plugin的作用
使用springboot打包插件打出的jar包会比没有使用打包插件打出的jar包更大,内容更多!
1、打包的文件结构的区别
2、jar包描述文件的区别
3、程序启动时,发现端口被占用
【注意】:
推荐使用进程pid号,杀死进程;因为,进程名称可能会有多个!
1-2、临时属性的设置
示例:使用java -jar启动项目的时候,临时想用另一个端口号启动
(application.yml中用的是80,但是启动的时候,想用8080端口)
1、启动命令行,设置临时属性
【注意】:
属性和application.yml中的key是一致的!
2、在idea中设置启动命令的临时属性:
设置:Program arguments
此时设置的参数,可以在启动类中获取到:
所以,出于安全的考虑,可以在启动boot程序时,断开读取外部临时配置对应的入口,也就是去掉读取外部参数的形参:
二、Spring Boot 配置文件application
2-1、多层级Spring Boot 配置文件加载优先级:
若是做一些保密级别较高的项目,数据库等配置信息保密,配置文件可以保存在与project同文件夹的config文件夹中,并且config文件夹没有访问权限。
多层级的配置文件间的属性采用叠加并覆盖的形式作用于程序。
2-2、自定义配置文件
1、通过启动参数加载自定义的文件名(无需写配置文件的扩展名)
2、通过通过启动参数加载指定文件路径下的配置文件
【注意】:
1、这两种方式:properties文件和yml格式的文件都是支持的。
2、当指定了自定义配置文件的时候,这时若是还有默认的application配置文件,就不是叠加并覆盖的形式了,而是就用指定的自定义的配置文件!
【备注】:
2-3、多环境开发下的配置文件
1、多环境
2、YAML 的多 profile 写法(推荐)
如果你使用
application.yml
,可以用---
进行多环境分隔。properties格式的文件,没有这种写法!!!
【注意】:
将每个环境中的公共配置,放在最上面,不同环境的配置,写在不同环境中!
【注意】:
这种方式有信息暴露的风险,因为所有的信息都在一个文件中。 所以,可以写多个配置文件。
3、多个配置文件的写法
yml和properties格式的文件,只是写法不同,其余完全一致
【注意】:
1、主配置文件中设置公共配置(全局)
2、环境分类配置文件中常用于设置冲突属性(局部)
4、多环境开发,独立配置文件,书写技巧:
(1)、使用maven占位符
实际开发中,maven可以有配置文件profiles,springboot也有配置文件profiles。
因为springboot是依赖于maven的,所以,一定是maven先运行,所以,springboot可以读取maven的配置文件信息,使用@...@读取。
所以,实际开发中,有一个写法:
@profile.name@
是 Maven 占位符,它通常出现在 Spring Boot 项目的配置文件中,表示这个值将在构建(mvn package
)时被 Maven 插件替换成实际的值。
需要通过 pom.xml
中的配置在打包时注入实际的值。
实际注入方式(在 pom.xml
中定义):
<properties>
<profile.name>dev</profile.name>
</properties>
或者通过 <build>
的 resources
插件替换变量:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering> <!-- 开启占位符替换 -->
</resource>
</resources>
</build>
结果:
打包后 application.yml
中的:
spring:
profiles:
active: '@profile.name@'
就会被替换成:
spring:
profiles:
active: dev
【小提示】:
只有开启了资源文件的 filtering (
<filtering>true</filtering>
) 才能让占位符生效。开发时如果没有打包,IDEA 中运行时不会解析
@profile.name@
,你可以临时写死或者通过启动参数指定-Dspring.profiles.active=dev
。
【小结】:
【问题】:在idea中,切换默认激活的代码到其他的环境可能会不生效:
这是idea的缓存导致的。
解决方式:mvn compile,手工编译。
(2)、根据功能对配置文件拆分
【写法一】:include属性
【注意】:
此时这些配置文件是有加载顺序的!先顺序加载include中的配置文件,最后加载active中的配置文件(后面的文件信息覆盖前面的文件信息)
【写法二】:group属性(推荐)
【注意】:
此时,配置文件的加载顺序变化了,先加载active中的配置文件,再 顺序加载include中的配置文件(后面的文件信息覆盖前面的文件信息)
三、代码中使用日志记录工具
3-1、日志级别
- TRACE:运行堆栈信息,使用率低
- DEBUG:程序员调试代码使用
- INFO:记录运维过程数据(默认)
- WARN:记录运维过程报警数据
- ERROR:记录错误堆栈信息
- FATAL:灾难信息,合并计入 ERROR
3-2、设置某个包的日志级别:
3-3、设置分组,对某个分组设置日志级别(推荐)
3-4、快速创建日志对象
【问题】:每个类都要写一遍,很繁琐
private static final Logger logger = LoggerFactory.getLogger(CarController.class);
方式一:使用继承的方式
1、定义一个父类
public class MyLogUtils {
protected final Logger logger = LoggerFactory.getLogger(getClass());
}
2、让需要使用日志的类继承这个父类,即可。
方式二:使用 Lombok 的 @Slf4j 注解(推荐)
【回顾】:getClass()方法
getClass()
是定义在java.lang.Object
中的一个 实例方法,它在运行时返回的是 实际对象的类,而不是声明它的那个类。
举个例子:
public class LogSupport {
public void printClassName() {
System.out.println("class: " + getClass());
}
}
public class CarController extends LogSupport {
}
public class Demo {
public static void main(String[] args) {
LogSupport obj = new CarController();
obj.printClassName();
}
}
输出:
class: class CarController
你看,尽管 printClassName()
是写在 LogSupport
中的,但运行时调用的是 obj.getClass()
,而 obj
的实际类型是 CarController
,所以返回的是 CarController.class
。
所以,你在 MyLogUtils
类中写了:
protected final Logger logger = LoggerFactory.getLogger(getClass());
谁继承了 LogSupport,调用 getClass() 得到的就是子类的 class 对象。
例如:
public class CarController extends LogSupport {
// logger 实际是 LoggerFactory.getLogger(CarController.class)
}
这就解决了每个类都写 LoggerFactory.getLogger(XXX.class)
的繁琐问题。
3-5、日志的输出格式
日志输出格式的控制
3-6、文件记录日志
这表示:
每天都会生成一个新日志文件(
%d{yyyy-MM-dd}
)在这一天中,如果日志文件 超过 3KB,则会 自动滚动,新文件以
.1.log
,.2.log
等结尾。