@SpringBootApplication
public class SpringBootTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTestApplication.class, args);
}
}
@SpringBootTest
class SpringBootTestApplicationTests {
@Test
void contextLoads() {
}
}
我们在创建SpringBoot项目之后,首先会自动生成一个主类,而主类中的main
方法中调用了SpringApplication
类的静态方法来启动整个SpringBoot项目,并且我们可以看到主类的上方有一个@SpringBootApplication
注解
同时还自带了一个测试类,测试类的上方仅添加了一个@SpringBootTest
注解
@SpringBootApplication
注解中自动触发包扫描
pom.xml
<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.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.6version>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>springboot-studyartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>SpringBootTestname>
<description>SpringBootTestdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
SpringBoot内嵌了一个Tomcat服务器,也就是说打包成jar包之后,相当于就是一个可以直接运行的应用程序
由于SpringBoot是自动扫描的,因此我们直接创建一个Controller即可被加载:
它还可以自动识别类型,如果我们返回的是一个对象类型的数据,那么它会自动转换为JSON数据格式,无需配置:
@Data
@Component
public class Student {
String name;
String sex;
int sid;
}
@RequestMapping("/student")
@ResponseBody
public Student student(){
Student student = new Student();
student.setName("小明");
student.setSex("男");
student.setSid(10);
return student;
}
# 修改端口为80
server.port=80
自定义的配置项,并在我们的项目中通过@Value
直接注入:
test.name = Hello world!
@Value("${test.name}")
String name;
通过这种方式,我们就可以更好地将一些需要频繁修改的配置项写在配置文件中,并通过注解方式去获取值。
配置文件除了使用properties
格式以外,还有一种叫做yaml
格式,它的语法如下:
一级目录:
二级目录:
三级目录1: 值
三级目录2: 值
三级目录List:
- 元素1
- 元素2
- 元素3
我们可以看到,每一级目录都是通过缩进(不能使用Tab,只能使用空格)区分,并且键和值之间需要添加冒号+空格来表示。
SpringBoot也支持这种格式的配置文件,我们可以将application.properties
修改为application.yml
或是application.yaml
来使用YAML语法编写配置:
server:
port: 80
test:
name: hello world!!!
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
此时我们还没有配置用户,springboot会自动提供我们一个默认账户 user
密码在控制台
可以直接在yml文件中直接进行配置
spring:
security:
user:
name: test # 用户名
password: 123456 # 密码
roles: # 角色
- user
- admin
实际上这样的配置方式就是一个inMemoryAuthentication
,只是我们可以直接配置而已。
当然,页面的控制和数据库验证我们还是需要提供WebSecurityConfigurerAdapter
的实现类去完成:
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().hasRole("user")
.and()
.formLogin();
}
}
注意这里不需要再添加@EnableWebSecurity
了,因为starter依赖已经帮我们添加了。
运行报错
org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length = 1
检查yml编码为GBK 删除yml中文注释
再次运行 成功
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
导入依赖后,直接启动会报错,是因为有必要的配置我们没有去编写
指定数据源的相关信息:
spring:
datasource:
url: jdbc:mysql://localhost:3306/study
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
再次启动,成功。
我们发现日志中会出现这样一句话:
这是Mybatis自动扫描输出的语句,导入依赖后,我们不需要再去设置Mybatis的相关Bean了,也不需要添加任何@MapperSacn
注解,因为starter已经帮助我们做了,它会自动扫描项目中添加了@Mapper
注解的接口,直接将其注册为Bean,不需要进行任何配置。
@Data
public class UserData {
int id;
String username;
String password;
String role;
}
@Mapper
public interface MainMapper {
@Select("select * from users where username = #{username}")
UserData findUserByName(String username);
}
当然,如果你觉得每个接口都去加一个@Mapper
比较麻烦的话也可以用回之前的方式,直接@MapperScan
使用包扫描。
添加Mapper之后,使用方法和SSM阶段是一样的,我们可以将其与SpringSecurity结合使用:
@Service
public class UserAuthService implements UserDetailsService {
@Resource
MainMapper mapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserData data = mapper.findUserByName(username);
if(data == null) throw new UsernameNotFoundException("用户 "+username+" 登录失败,用户名不存在!");
return User
.withUsername(data.getUsername())
.password(data.getPassword())
.roles(data.getRole())
.build();
}
}
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Resource
UserAuthService service;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().hasAnyRole("user","admin")
.and()
.formLogin();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(service)
.passwordEncoder(new BCryptPasswordEncoder());
}
}
删除无用的yml配置
server:
port: 80
spring:
datasource:
url: jdbc:mysql://localhost:3306/study
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
整合Thymeleaf也只需导入对应的starter即可:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
接着我们只需要直接使用即可:
@RequestMapping("/index")
public String index(){
return "index";
}
但是注意,这样只能正常解析HTML页面,但是js、css等静态资源我们需要进行路径指定,不然无法访问,我们在配文件中配置一下静态资源的访问前缀:
spring:
mvc:
static-path-pattern: /static/**
接着我们像之前一样,把登陆页面实现一下吧。
@RequestMapping("/login")
public String login(){
return "login";
}
修改安全配置 SecurityConfiguration
设置登录页面,成功后页面
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/static/**").permitAll() // 允许全部静态资源
.anyRequest().hasAnyRole("user","admin")
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/doLogin")
.permitAll()
.defaultSuccessUrl("/index",true);
}
修改login页面,,将输入框name属性添加上,登录按钮从a标签改为button
给html页面添加上thymeleaf命名空间
<html lang="en" xmlns:th=http://www.thymeleaf.org
xmlns:sec=http://www.thymeleaf.org/extras/spring-security>
给login页面添加上隐藏的csrf
<input th:name="${_csrf.getParameterName()}" th:value="${_csrf.getToken()}" type="hidden">
给form表单添加提交地址
<form action="/doLogin" method="post">
实现 记住我 功能
SecurityConfiguration
@Resource
DataSource dataSource;
JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setCreateTableOnStartup(true); // 自动建表
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/static/**").permitAll() // 允许全部静态资源
.anyRequest().hasAnyRole("user","admin")
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/doLogin")
.permitAll()
.defaultSuccessUrl("/index",true)
.and()
.rememberMe() // 记住我
.tokenRepository(jdbcTokenRepositoryImpl()); // 令牌库
}
Spring Security RememberMe的令牌持久化JdbcTokenRepositoryImpl的工作原理
https://blog.csdn.net/qq_41490274/article/details/120377361
springboot 启动时自动打开浏览器
SpringBoot为我们提供了丰富的日志系统,它几乎是开箱即用的。
我们首先要区分一下,什么是日志门面(Facade)什么是日志实现,我们之前学习的JUL实际上就是一种日志实现,我们可以直接使用JUL为我们提供的日志框架来规范化打印日志,而日志门面,如Slf4j,是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接口方法进行调用即可,由于它只是一个接口,并不是一个具体的可以直接单独使用的日志框架,所以最终日志的格式、记录级别、输出方式等都要通过接口绑定的具体的日志系统来实现,这些具体的日志系统就有log4j、logback、java.util.logging等,它们才实现了具体的日志系统的功能。
日志门面和日志实现就像JDBC和数据库驱动一样,一个是画大饼的,一个是真的去做饼的。
但是现在有一个问题就是,不同的框架可能使用了不同的日志框架,如果这个时候出现众多日志框架并存的情况,我们现在希望的是所有的框架一律使用日志门面(Slf4j)进行日志打印,这时该怎么去解决?我们不可能将其他框架依赖的日志框架替换掉,直接更换为Slf4j吧,这样显然不现实。
这时,可以采取类似于偷梁换柱的做法,只保留不同日志框架的接口和类定义等关键信息,而将实现全部定向为Slf4j调用。相当于有着和原有日志框架一样的外壳,对于其他框架来说依然可以使用对应的类进行操作,而具体如何执行,真正的内心已经是Slf4j的了。
所以,SpringBoot为了统一日志框架的使用,做了这些事情:
SpringBoot使用的是Slf4j作为日志门面,Logback(Logback 是log4j 框架的作者开发的新一代日志框架,它效率更高、能够适应诸多的运行环境,同时天然支持SLF4J)作为日志实现,对应的依赖为:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
dependency>
此依赖已经被包含了,所以我们如果需要打印日志,可以像这样:
@RequestMapping("/login")
public String login(){
Logger logger = LoggerFactory.getLogger(MainController.class);
logger.info("用户访问了一次登陆界面");
return "login";
}
因为我们使用了Lombok,所以直接一个注解也可以搞定哦:
@Slf4j
@Controller
public class MainController {
@RequestMapping("/login")
public String login(){
log.info("用户访问了一次登陆界面");
return "login";
}
日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,SpringBoot默认只会打印INFO以上级别的信息。
Logback官网:https://logback.qos.ch
和JUL一样,Logback也能实现定制化,我们可以编写对应的配置文件,SpringBoot推荐将配置文件名称命名为
logback-spring.xml
表示这是SpringBoot下Logback专用的配置,可以使用SpringBoot 的高级Profile功能,它的内容类似于这样:
<configuration>
configuration>
最外层由configuration
包裹,一旦编写,那么就会替换默认的配置,所以如果内部什么都不写的话,那么会导致我们的SpringBoot项目没有配置任何日志输出方式,控制台也不会打印日志。
我们接着来看如何配置一个控制台日志打印,我们可以直接导入并使用SpringBoot为我们预设好的日志格式,在org/springframework/boot/logging/logback/defaults.xml
中已经帮我们把日志的输出格式定义好了,我们只需要设置对应的appender
即可:
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>${CONSOLE_LOG_CHARSET}charset>
encoder>
appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
root>
configuration>
配置完成后,我们发现控制台已经可以正常打印日志信息了。
接着我们来看看如何开启文件打印,我们只需要配置一个对应的Appender即可:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}pattern>
<charset>${FILE_LOG_CHARSET}charset>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<FileNamePattern>log/%d{yyyy-MM-dd}-spring-%i.logFileNamePattern>
<cleanHistoryOnStart>truecleanHistoryOnStart>
<maxHistory>7maxHistory>
<maxFileSize>10MBmaxFileSize>
rollingPolicy>
appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
root>
配置完成后,我们可以看到日志文件也能自动生成了。
我们也可以魔改官方提供的日志格式,官方文档:https://logback.qos.ch/manual/layouts.html
这里需要提及的是MDC机制,Logback内置的日志字段还是比较少,如果我们需要打印有关业务的更多的内容,包括自定义的一些数据,需要借助logback MDC机制,MDC为“Mapped Diagnostic Context”(映射诊断上下文),即将一些运行时的上下文数据通过logback打印出来;此时我们需要借助org.sl4j.MDC类。
比如我们现在需要记录是哪个用户访问我们网站的日志,只要是此用户访问我们网站,都会在日志中携带该用户的ID,我们希望每条日志中都携带这样一段信息文本,而官方提供的字段无法实现此功能,这时就需要使用MDC机制:
@Slf4j
@Controller
public class MainController {
@RequestMapping("/login")
public String login(HttpServletRequest request){
MDC.put("reqId", request.getSession().getId());
log.info("有人访问了一次登陆界面");
return "login";
}
}
通过这种方式,我们就可以向日志中传入自定义参数了,我们日志中添加这样一个占位符%X{键值}
,名字保持一致:
%clr([%X{reqId}]){faint}
这样当我们向MDC中添加信息后,只要是当前线程(本质是ThreadLocal实现)下输出的日志,都会自动替换占位符。
我们在之前发现,实际上Banner部分和日志部分是独立的,SpringBoot启动后,会先打印Banner部分,那么这个Banner部分是否可以自定义呢?答案是可以的。
我们可以直接来配置文件所在目录下创建一个名为banner.txt
的文本文档,内容随便你:
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永无BUG 永不修改 //
可以使用在线生成网站进行生成自己的个性Banner:https://www.bootschool.net/ascii
我们甚至还可以使用颜色代码来为文本切换颜色:
${AnsiColor.BRIGHT_GREEN} //绿色
也可以获取一些常用的变量信息:
${AnsiColor.YELLOW} 当前 Spring Boot 版本:${spring-boot.version}
在日常开发中,我们项目会有多个环境。例如开发环境(develop)也就是我们研发过程中疯狂敲代码修BUG阶段,生产环境(production )项目开发得差不多了,可以放在服务器上跑了。不同的环境下,可能我们的配置文件也存在不同,但是我们不可能切换环境的时候又去重新写一次配置文件,所以我们可以将多个环境的配置文件提前写好,进行自由切换。
由于SpringBoot只会读取application.properties
或是application.yml
文件,那么怎么才能实现自由切换呢?SpringBoot给我们提供了一种方式,我们可以通过配置文件指定:
spring:
profiles:
active: dev
接着我们分别创建两个环境的配置文件,application-dev.yml
和application-prod.yml
分别表示开发环境和生产环境的配置文件,比如开发环境我们使用的服务器端口为8080,而生产环境下可能就需要设置为80或是443端口,那么这个时候就需要不同环境下的配置文件进行区分:
spring:
profiles:
active: dev
这样我们就可以灵活切换生产环境和开发环境下的配置文件了。
不创建新文件的写法
springboot 2.4以上提示已弃用spring.profiles
配置中将spring.profiles
或spring.profiles.active
配置替换为了spring.config.activate.on-profile
。而激活配置spring.profiles.active
的使用并未改变。启动应用时,指定加载不同环境配置方式不变,依然采用spring.profiles.active
指定。
SpringBoot自带的Logback日志系统也是支持多环境配置的,比如我们想在开发环境下输出日志到控制台,而生产环境下只需要输出到文件即可,这时就需要进行环境配置:
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
root>
springProfile>
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="FILE"/>
root>
springProfile>
注意springProfile
是区分大小写的!
那如果我们希望生产环境中不要打包开发环境下的配置文件呢,我们目前虽然可以切换开发环境,但是打包的时候依然是所有配置文件全部打包,这样总感觉还欠缺一点完美,因此,打包的问题就只能找Maven解决了,Maven也可以设置多环境:
pom.xml
<profiles>
<profile>
<id>devid>
<activation>
<activeByDefault>trueactiveByDefault>
activation>
<properties>
<environment>devenvironment>
properties>
profile>
<profile>
<id>prodid>
<activation>
<activeByDefault>falseactiveByDefault>
activation>
<properties>
<environment>prodenvironment>
properties>
profile>
profiles>
接着,我们需要根据环境的不同,排除其他环境的配置文件:
标签下
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<excludes>
<exclude>application*.ymlexclude>
excludes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<filtering>truefiltering>
<includes>
<include>application.ymlinclude>
<include>application-${environment}.ymlinclude>
includes>
resource>
resources>
接着,我们可以直接将Maven中的environment
属性,传递给SpringBoot的配置文件,在构建时替换为对应的值:
spring:
profiles:
active: '@environment@' #注意YAML配置文件需要加单引号,否则会报错
这样,根据我们Maven环境的切换,SpringBoot的配置文件也会进行对应的切换。
最后我们打开Maven栏目,就可以自由切换了,直接勾选即可,注意切换环境之后要重新加载一下Maven项目,不然不会生效!
现在我们的SpringBoot项目编写完成了,那么如何打包运行呢?非常简单,只需要点击Maven生命周期中的package
即可,它会自动将其打包为可直接运行的Jar包,第一次打包可能会花费一些时间下载部分依赖的源码一起打包进Jar文件。
我们发现在打包的过程中还会完整的将项目跑一遍进行测试,如果我们不想测试直接打包,可以手动使用以下命令:
mvn package -DskipTests
打包后,我们会直接得到一个名为springboot-study-0.0.1-SNAPSHOT.jar
的文件,这时在CMD窗口中输入命令:
java -jar springboot-study-0.0.1-SNAPSHOT.jar
输入后,可以看到我们的Java项目成功运行起来了,如果手动关闭窗口会导致整个项目终止运行。