SpringBoot入门到精通系列
上一篇文章我们讲的是SpringBoot的入门程序,万事开头难,本篇文章我们将学习SpringBoot的一些基础用法。
之前启动项目是在启动类执行main方法来启动,这种方式依赖于IDEA开发工具,如果我们项目要上线,就需要把项目打包(jar)后独立启动。
第一步:导入打包插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
第二步:使用terminal 执行命令 mvn package
打包
该命令回把项目按照pom.xml中指定的
打包方式把项目打成 jar包,输出到 target
目录
当然了也可以直接点击 Maven窗口中的 package 菜单,效果是一样的
使用CDM命令窗口,执行java -jar
启动项目,如下
这样我们如果要在linux部署项目,就只需要把项目的jar包推送上去,执行java -jar即可。
在IDEA中使用插件启动
另外 : Spring Boot Maven插件包含一个 run 目标,可用来快速编译和运行应用程序,并 且跟在IDE运行一样支持热加载,你可以使用 mvn spring-boot:run
来启动。
或者直接双击
热部署就是当我们修改了项目代码,不需要重启就可以看到最新的效果,我们可以引入三方热部署插件来实现.SpringBoot提供了开发工具包可以实现热部署。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
当我们修改了代码后,按住ctrl+F9
触发代码重新编译,SpringBoot检查到classpath变化,会重新加载字节码,达到无需手动重新即可看到最新效果的目的。
但是每次都去ctrl+f9显得特别麻烦,可以把编码配置成ctrl+s ,这样习惯性保存就编译。在setting中找到keymap,搜索build,找到Build Project ,右键 Add Keyboar Shortcut.增加ctrl+s按键。
重启项目,当修改了代码,按一下ctrl+s,即可触发热部署功能。当然还有一种方式是开启IDEA自动编译代码的功能,这样不用按ctrl+s就能自动触发热部署,但是这种方式对IDEA性能影响极大,每次修改代码都会重新热部署,个人不太建议。
YAML (YAML Ain’t a Markup Language)YAML不是一种标记语言,通常以.yml为后缀的文件,是一种直观的能够被电脑识别的数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,一种专门用来写配置文件的语言。YML具有如下规范
yml以 k: v
键值结构书写,冒号后面必须有一个空格
使用空格的缩进表示层级关系,空格数目不重要,只要是左对齐的一列数据,都是同一个层级的,同一级不能出现相同名字。
大小写敏感
缩进时不允许使用Tab键,只允许使用空格。
松散表示,java中对于驼峰命名法,可用原名或使用-代替驼峰,如java中的lastName属性,在yml中使用lastName或 last-name都可正确映射。
我们以普通的值(数字、字符串、布尔)、日期、对象、数组、集合为例来进行配置
#字符串配置
appName: "SprigBoot"
#不用加上引号也可以
#appName: SprigBoot
字符串默认不用加上单引号或者双绰号;
“”: 双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
name: “zhangsan \n lisi”:输出;zhangsan 换行 lisi
‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
date: 2019/01/01
map和对象的写法是一样的
#定义一个对象
user:
id: 1
name: zs
第二种写法
user: {id:1,name:zs}
数组使用中划线 -表示其中一个元素 , list 和 set写法也是一样的
names:
- zs
- ls
list 和 set写法也是一样的
users:
- id: 1
name: zs
- id: 2
name: ww
- {id:3,name:zy}
java对象
public class ClassRoom {
private Long id;
private String name;
}
public class Teacher {
private Long id;
private String name;
}
public class Student {
private Long id;
private String username;
private ClassRoom classRoom;
private List<Teacher> teachers;
private String hobby[];
private Map<String,String> property;
}
yml配置
student:
id: 1
username: "zs"
classRoom:
id: 1,
name: "一班"
teachers:
- id: 1,
name: "张老师"
- id: 2
name: "王老师"
hobby:
- "篮球"
- "跑步"
property:
hei: 170
weight: 120
在实际开发中,我们为了解除硬编码,往往需要把某些内容编写在配置文件中,然后读取到代码中使用,以至于修改的时候只需要修改配置文件而不需要修改代码。这里涉及到一个重要的环节就是如何把配置文件中的内容读取到代码中使用。介绍两种方式:@Value注解和@ConfigurationProperties注解。
我们来演示一个简单的案例,假如我们在 application.yml 有如下配置
user:
username: ls
password: 456
age: 99
那我们在代码中如何使用配置文件中的内容呢?很简单,只需要在类中创建一个成员变量,做如下操作即可
@RestController
public class HelloController {
//演示读取配置
@Value("${user.username}")
private String username;
//演示读取配置
@Value("${user.password}")
private String password;
//演示读取配置
@Value("${user.age}")
private int age;
@RequestMapping("/hello")
public String hello(){
System.out.println("username = "+username);
System.out.println("password = "+password);
System.out.println("age = "+age);
return "Hello Spring Boot";
}
}
这里我们使用了 @Value(" u s e r . u s e r n a m e " ) 方 式 来 取 值 , {user.username}") 方式来取值, user.username")方式来取值,{}是SPEL的写法,它可以自动从配置中拿到值赋值到被标记的字段上。启动项目,访问 /hello , 可以看到控制台输出了正确的值。
对于@Value它的弊端是:它只能一个一个读取配置项,如果配置项目比较多的话就需要写很多的字段和@Value注解。我们可以使用@ConfigurationProperties来解决。
还是刚才的application.yml配置,我可以为他编写一个对应的实体类
//交给Spring管理
@Component
//读取配置的注解,以 user 为前缀过滤出配置项,同名注入到字段中
@ConfigurationProperties(prefix = "user")
public class UserProp {
private Long id;
private String username;
private int age;
...省略get,set方法...
@Override
public String toString() {
return "UserProp{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
'}';
}
}
@ConfigurationProperties(prefix = “user”) : 它的作用是自动读取配置,以 prefix 为指定的值为前缀过滤出配置项,同名注入到字段中,该注解在SpringBoot源码中大量使用。记得需要贴:@Component,把当前类交给Spring管理。
随后我们只需要在代码中注入 UserProp 即可使用
@RestController
public class HelloController {
@Autowired
private UserProp userProp;
@RequestMapping("/hello")
public String hello(){
System.out.println(userProp);
return "Hello Spring Boot";
}
}
控制台打印效果: UserProp{id=null, username=‘ls’, age=99}
总结一下: @Value适用于单个配置的读取 , @ConfigurationProperties合适把一堆拥有相同前缀的配置读取到一个类中。
在真实项目中往往涉及到相互环境的切换,比如:我们在自己的电脑上把代码写好之后,会把代码打包推送给测试人员进行测试,测试通过没有问题之后又会把代码推送到生成环境上线。随着环境的切换,相关配置也需要修改。就拿数据库来说,我们开发代码的时候使用我们自己电脑电脑上的数据库,测试的时候需要有专门的测试数据库,对于DataSource而言就需要重新配置,而生产放进又有专门的数据库,有需要切换配置。当配置项变多的时候,每次切换环境就是一个很麻烦的事情,SpringBoot提供了两种方式进行多环境支持。
第一种方式,我们使用yml的文档块来进行多个环境配置(为不同的环境做不同的配置),在application.yml中做如下配置
spring:
profiles:
active: test #激活(选择)环境test
---
spring:
profiles: dev #指定环境名字dev
server:
port: 9999
---
spring:
profiles: test #指定环境名字test
server:
port: 8888
---
是用来文档分块的,spring.profiles
我们可以理解为是配置环境的名字 ,spring.profiles.active
则是用来激活配置的,那么上面配置了2个环境,一个dev 开发环境 ,端口使用 9999 , 一个 test 测试环境,端口 8888 ;
然后使用spring.profiles.active=test
来激活测试环境配置,也就是说程序启动会使用 test的配置,端口会使用 8888
启动日志如下
如果你需要切换为 dev ,只需要修改spring.profiles.active=dev
即可
第二种是使用多个配置文件,不同的环境使用不同的配置文件,首先创建一个名字为application-dev.yml
配置文件用作开发环境配置
server:
port: 9999
再创建一个名字为 application-test.yml
的配置文件,用作测试环境配置
server:
port: 8888
你应该看懂是什么意思了,没错 -dev
最后一个中划线后面的名字,代表的就是环境的名字。
接下来在主配置文件 application.yml
中来指定激活哪个配置
spring:
profiles:
active: dev #激活application-dev.yml
这里我激活了 dev环境的配置,启动测试如下
除了可以使用yml进行多环境配置,SpringBoot还提供了@Profile注解来指定配置在什么环境下生效。@Profile注解使用范围:@Configration 和 @Component 注解的类及其方法,其中包括继承了@Component的注解:@Service、@Controller、@Repository等,如下
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
//@Profile("dev")
public User user(){
return new User();
}
}
如果该注解贴在类上,那么整个类都会受到Profile限制,如果贴在方法上只,然后我们就可以在yml中通过spring.profiles.active=dev来激活,如
spring:
profiles:
active: dev
在启动独立的项目jar的时候,我们可以通过一个-D
来修改SpringBoot的配置项,比如在启动时指定 dev 环境
java -jar -Dspring.profiles.active=dev springboot-hello-1.0-SNAPSHOT.jar
-Dspring.profiles.active=dev : 指定激活的环境为 dev
比如修改启动的端口
java -jar -Dserver.port=8989 springboot-hello-1.0-SNAPSHOT.jar
日志对于程序的调试和排错是非常重要的,遥想当年刚出道那会儿,我打印日志的方式就是System.out.println()
,然后项目上线后控制台全是Sytem.out的输出,看起来非常凌乱,然后被老板批斗了一番。O(∩_∩)O哈哈~
使用System.out.println
调试程序确实比较方便,但是这个东西本身性能不好,而且没有开关,没办法做到关闭打印,除非你把打印的代码删除。同时打印出来的东西也没有规范的格式和时间,所以极其不推荐使用System.out.println来打印日志,除非你只是临时的调试,打印完之后就删除。
在实际项目中我们都是使用专业的日志框架去打印日志,这种日志有开关,有等级,有格式,同时可以做日志收集。
常见的日志框架有 Apache 的 Log4j ,Log4j2,Commons Logging,基于门面模式设计的Slf4j , Logback(Slf4j阵营) ,和Java自带的Jul。
日志框架的历史我这里不多说,你可以自己去百度,对于 Slf4j 而言,它的出现其实是为了整合市面上其他的日志框架使用标准不同统一的问题。它提供了 slf4j-api ,是一套简易Java日志门面,本身并无日志的实现,然后通过一些列的适配包来整合其他的日志框架,比如: slf4j-jdk14-1.7.13.jar 适配JDK原生的日志框架,比如:slf4j-log4j12-1.7.13.jar适配于log4j1.2日志框架。
总之我们在项目中需要引入 slf4j-api ,以及适配于其他日志框架的依赖如:slf4j-log4j12,对于SpringBoot而言,推荐使用Logback作为日志框架,且通过spring-boot-starter-web 已经引入了日志包,我们无需再导入日志包
打印日志需要创建Logger,注意:我们要面向接口编程,使用slf4j总的Logger
@RestController
public class HelloController {
//日志打印器
private Logger log = LoggerFactory.getLogger(HelloController.class);
@RequestMapping("/hello")
public String hello(){
log.error("错误日志 错误信息为 = {}","空指针异常");
log.warn("警告日志");
if(log.isDebugEnabled()){
log.debug("这是一个debug信息 {}" , "填充内容");
}
log.info("info信息");
log.trace("trace信息");
return "Hello Spring Boot";
}
}
可以通过log.isDebugEnabled()
来判断是否开启该日志级别,可以通过 {}
占位符的方式来传值。
打印效果如下
对于 info 和 trace 并没有被打印出来,那是因为SpringBoot默认的日志级别为 info,我们做如下修改即可打印所有级别日志
logging:
level:
cn.whale: trace #指定cn.whale包下的日志使用trace级别
注意:level 下面需要指定一个包名,测试效果如下
你知道日志级别有哪些吗?从内容打印的多到少排序 : trace > debug > info > warn > error 。比如你设置为debug,那么就可以打印出 deug,info,warn,error级别的日志。
lombok提供了一个注解@Slf4j,可以省略到Logger的创建即可打印,但是需要导入lombok的依赖
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
然后安装Lombok的插件
然后在需要打印值的类上贴@Slf4j
@RestController
//自动创建 Logger log
@Slf4j
public class HelloController {
//日志打印器
//private Logger log = LoggerFactory.getLogger(HelloController.class);
@RequestMapping("/hello")
public String hello(){
log.error("错误日志 错误信息为 = {}","空指针异常");
log.warn("警告日志");
if(log.isDebugEnabled()){
log.debug("这是一个debug信息 {}" , "填充内容");
}
log.info("info信息");
log.trace("trace信息");
return "Hello Spring Boot";
}
}
如果要对日志做个性化配置,可以直接修改yml,如下
logging:
level:
root: info #全局使用info级别
cn.whale: trace #指定cn.whale包下的日志使用trace级别
file:
path: logs #日志保存路径
max-size: 10MB #最多10MB就会删除老的日志
max-history: 7 #最多保留7天就会删除老的日志
name: logs/springboot.log #日志文件名
pattern: #指定日志的格式
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" #输出到文件的格式
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n" #输出到控制台的格式
但是官方更推荐使用 logback-spring.xml ,或者logback.xml作为默认的logback配置文件,在resoures下创建文件logback-spring.xml会自动识别为日志配置, 下面是一个样板。
<configuration>
<property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>UTF-8charset>
encoder>
appender>
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/springboot.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/springboot-%d{yyyyMMdd}-%i.log.gzfileNamePattern>
<maxFileSize>10MBmaxFileSize>
<maxHistory>30maxHistory>
<totalSizeCap>5GBtotalSizeCap>
rollingPolicy>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>UTF-8charset>
encoder>
appender>
<root level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
root>
<logger name="cn.whale" level="trace" additivity="false">
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
logger>
configuration>
通过在classpath下添加一个 banner.txt 或设置 banner.location 来指定相应 的文件可以改变启动过程中打印的banner,比如:我在resources下创建了banner.txt增加以下内容
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永无BUG 永不修改 //
启动项目观察控制台
如果你想关闭banner,可以使用如下配置
spring:
main:
banner-mode: off
我们可以先来回顾一下Spring的测试,如下
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
@ContextConfiguration(classes = JavaConfig.class )
public class StudentTest {
@Test
public void test() {
//打印
System.out.println(“...”);
}
}
在Spring的单元测试中,我们通过@ContextConfiguration(locations = “”) 来指定xml配置文件的地址,如果是配置类,可以通过@ContextConfiguration(classes = JavaConfig.class )方式来指定。
对于SpringBoot而言,提供了@SpringBootTest注解来加载配置类,如下:
@RunWith(SpringRunner.class)
//classes 后面跟上启动类的class
@SpringBootTest(classes = ApplicationConfig.class)
public class UserTest {
@Autowired
private IUserService userService ;
@Test
public void test(){
userService.selectAll().stream().forEach(System.out::print);
}
}
注意:@RunWith 跟的是 SpringRunner.class