一、SpringBoot简介
1.1 SpringBoot介绍
- SpringBoot基于Spring开发,SpringBoot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。换言之,它并不是用来替代Spring的解决方案,而是与Spring框架紧密结合,用于提升Spring开发者体验的工具。Spring Boot以约定大于配置的核心思想,底层默认进行了很多配置,多数Spring Boot应用只需很少的Spring 配置,同时集成了大量常用的第三方库配置(例如:Redis、MongoDB、JPA、RabbitMQ、Quartz等)。
- Spring Boot应用中这些第三方库几乎可以零配置的开箱即用。
1.2 主要优点
- 为所有Spring开发者更快的入门;
- 开箱即用,提供各种默认配置来简化项目的配置;
- 内嵌式容器简化Web项目;
- 没有冗余代码生成和XML配置的要求;
二、第一个SpringBoot程序
到底多么简单?!
2.1 开发工具
- jdk1.8
- maven 3.8.3
- springboot 最新版
- IDEA
官方:提供了一个快速生成的网站!IDEA集成了这个网站!
- 可以在官网直接下载,导入到IDEA中开发;
- 直接使用IDEA创建一个springboot项目(一般情况下);
2.2 流程步骤
三、原理初探
3.1 自动配置
- pom.xml
- spring-boot-dependencies:核心依赖在父工程!
- 在写入或引入一些springboot依赖时,不需指定版本,正是因为这些版本仓库!
3.2 启动器
- SpringBoot的启动环境,例如spring-boot-starter-web会自动导入web环境所有的依赖;
- SpringBoot会将所有功能场景,变成一个个启动器;
- 需使用什么功能,即只需找到对应的启动器即可starter;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.3 主程序
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
SpringBootApplication这个类做了以下事情:
- 推断应用的类型是普通项目还是Web项目;
- 查找并加载所有可用初始化器,设置到initializers属性中;
- 找出所有的应用程序监听器,设置到listeners属性中;
- 推断并设置main方法的定义类,找到运行的主类;
- 注解
@SpringBootApplication:标注这个类是springboot的应用,启动类下的所有资源导入
- @Configuration:spring配置类
- @Componet:说明这也是一个spring的组件
@SpringBootConfiguration:springboot的配置
@EnableAutoConfiguration:自动配置
- @AutoConfigurationPackage:自动配置包
- @Import({AutoConfigurationImportSelector.class}):自动导入选择
- 获取候选配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations =SpringFactoriesLoader.
loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(),
this.getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories.
If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
- META-INF/spring.factories:自动配置的核心文件
springboot所有自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里,但不一定会生效,要判断条件是否成立,只要导入了对应的start,就会有对应的启动器,有了启动器,自动装配就会生效,然后配置成功!
- springboot在启动时,从类路径下/META-INF/spring.factories获取指定的值;
- 将这些自动配置的类导入容器,自动配置就会生效,帮助开发者进行自动配置;
- 以前需自动配置的文件,springboot会帮开发者完成;
- 整合JavaEE,解决方案和自动配置的文件都在spring-boot-autoconfigure-2.2.0.RELEASE.jar这个包下;
- springboot会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器;
- 容器会存在很多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,比如@Configuration等;
总而言之,有了这些自动配置类,免去了开发者手动编写配置文件的工作量!
四、SpringBoot Web开发
4.1 静态资源
1)在springboot中,可以使用以下方式-新建目录-处理静态资源
默认已经新建了static目录,另外两个目录需新建。
- webjars:localhost:8080/webjars/
- public,static,/**,resources:localhost:8080/
2)优先级
resources>static(默认)>public
4.2 首页和图标定制
1)首页设置
在resources、static(默认)或public,添加index.html即可,然后通过localhost:8080/进行访问。
注意:在templates目录下的所有页面,只能通过controller来跳转!这个需要模板引擎的支持!
2)定制图标
在resources、static(默认)或public目录下,添加favicon.ico图标,并在yml配置文件上,设置
spring:
mvc:
favicon:
enabled: false
4.3 模板引擎
只需使用thymeleaf,导入相应的依赖即可!
并将HTML页面放在templates目录下
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
所有的html元素都可以被thymeleaf替换接管:th:元素名,并引入
xmlns:th="http://www.thymeleaf.org"
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2 th:text="${msg}"></h2>
</body>
</html>
4.4 扩展SpringMVC
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//视图跳转
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/xiao").setViewName("test");
}
}
4.5 整合JDBC
1)pom.xml配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
2)yml文件
# 数据库驱动:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
# 数据源名称
name: defaultDataSource
# 数据库连接地址 记得增加时区的配置,不然会报错
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UT
# 数据库用户名&密码:
username: root
password: 1234
3)测试代码
@RestController
public class JDBCController {
@Resource
JdbcTemplate jdbcTemplate;
//查询数据库的所有信息
//没有实体类,数据库中的东西,如何获取?
@GetMapping("/userList")
public List<Map<String,Object>> userList(){
String sql="select * from users";
List<Map<String,Object>> list_maps=jdbcTemplate.queryForList(sql);
return list_maps;
}
//插入新记录
@GetMapping("/addUser")
public String addUser(){
String sql="insert into mybatis.users(user_name,user_pwd,"+
"user_realname,user_img) values('YeJieJie',"+
"'124345','叶杰杰','5.jpg')";
jdbcTemplate.update(sql);
return "add-ok";
}
//更新记录
@GetMapping("/updateUser/{id}")
public String updateUser(@PathVariable("id") int id){
String sql="update mybatis.users set user_pwd=?,user_img=?"+
"where user_id="+id;
//封装
Object[] objects=new Object[2];
objects[0]="123456";
objects[1]="8.png";
jdbcTemplate.update(sql,objects);
return "update-ok";
}
//删除记录
@GetMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable("id") int id){
String sql="delete from mybatis.users where user_id=?";
jdbcTemplate.update(sql,id);
return "delete-ok";
}
}
4.6 整合MyBatis框架
1)pom.xml配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
2)properties或yml文件
# 应用名称
spring.application.name=springboot-05-mybatis
# 应用服务 WEB 访问端口
server.port=8080
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=1234
# 整合mybatis
mybatis.type-aliases-package=com.xsbc.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
3)测试代码
@RestController
public class UserController {
@Resource
private UserMapper userMapper;
@GetMapping("/queryUserList")
public List<User> queryUserList(){
List<User> users=userMapper.queryUserList();
return users;
}
//根据id查询用户
@GetMapping("/queryUserById")
public User queryUserById(){
User user=userMapper.queryUserById(6);
return user;
}
//增加一个用户
@GetMapping("/addUser")
public String addUser(){
int i=userMapper.addUser(new
User(7, "XiaoWang", "456245", "小王", "23.png"));
System.out.println(i);
return "add-ok";
}
//修改一个用户
@GetMapping("/updateUser")
public String updateUser(){
int i=userMapper.updateUser(new
User(12, "XiaoWang", "9876543", "小王", "21.png"));
System.out.println(i);
return "update-ok";
}
//根据id删除一个用户
@GetMapping("/deleteUser")
public String deleteUser(){
int i=userMapper.deleteUser(11);
System.out.println(i);
return "delete-ok";
}
}
- 映射文件
<?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="com.xsbc.mapper.UserMapper">
<select id="queryUserList" resultType="User">
select * from users
</select>
<select id="queryUserById" resultType="User">
select * from users where user_id=#{userId}
</select>
<insert id="addUser" parameterType="User">
insert into users(user_name,user_pwd,user_realName,user_img)
values(#{user_name},#{user_pwd},#{user_realName},#{user_img})
</insert>
<update id="updateUser" parameterType="User">
update users set user_pwd=#{user_pwd},user_img=#{user_img}
where user_id=#{user_id}
</update>
<delete id="deleteUser" parameterType="int">
delete from users where user_id=#{user_id}
</delete>
</mapper>
五、日志配置
5.1 日志框架
市面上的日志框架:
JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j等
spring-boot-starter-logging默认采用了slf4j+logback的形式,Spring Boot也能自动配置(JUL、log4j2、logback),并简化配置。
日志门面(抽象类) | 日志实现(实现类) |
JUL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-logging | Log4j JUL(java.util.logging) Log4j2 Logback |
5.2 SLF4j使用
1)在IDEA中使用SLF4j
在开发过程中,日志记录方法的调用,不应该直接调用日志实现类,而是调用日志抽象层里面的方法。在项目中导入slf4j的jar和logger。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
2)自定义日志常用配置
- 日志输出级别
trace 追踪日志 debug 调试日志 info 自定义日志或信息日志 warn 警告日志 error 错误日志
- 日志输出到文件
- 日志输出格式
日志输出到文件
- file的name:日志的名称,可添加路径+日志名
- file的path:路径
- 控制台和文件输出
file:
name: test.log
path: /
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss}---[%thread]%-5level %logger{50} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss}---[%thread]%-5level %logger{50} - %msg%n"
3)XML日志配置方式
xml文件定义位于resource/static目录下名,通常命名为:logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,
将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,
如果没有给出时间单位,默认单位是毫秒当scan为true时,
此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,
实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定义日志的根目录 -->
<property name="LOG_HOME" value="/app/log" />
<!-- 定义日志文件名称 -->
<property name="appName" value="atguigu-springboot"></property>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="stdout"
class="ch.qos.logback.core.ConsoleAppender">
<!--
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}
[%thread] %-5level %logger{50} - %msg%n</pattern>
</layout>
</appender>
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="appLogAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名称 -->
<file>${LOG_HOME}/${appName}.log</file>
<!--
当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,
既负责滚动也负责出发滚动。
-->
<rollingPolicy
class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
%i:当文件大小超过maxFileSize时,按照i进行文件滚动
-->
<fileNamePattern>
${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log
</fileNamePattern>
<!--
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。
假设设置每天滚动,且maxHistory是365,则只保存最近365天的文件,
删除之前的旧文件。注意,删除旧文件是,
那些为了归档而创建的目录也会被删除。
-->
<MaxHistory>365</MaxHistory>
<!--
当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动
注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,
必须配置timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ]
[ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>
<!--
logger主要用于存放日志对象,也可以定义日志类型、级别
name:表示匹配的logger类型前缀,也就是包的前半部分
level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在于children-logger是否使用
rootLogger配置的appender进行输出,
false:表示只用当前logger的appender-ref,true:
表示当前logger的appender-ref和rootLogger的appender-ref都有效
-->
<!-- hibernate logger -->
<logger name="com.atguigu" level="debug" />
<!-- Spring framework logger -->
<logger name="org.springframework" level="debug"
additivity="false"></logger>
<!--
root与logger是父子关系,没有特别定义则默认为root,
任何一个类只会和一个logger对应,
要么是定义的logger,要么是root,判断的关键在于找到这个logger,
然后判断这个logger的appender和level。
-->
<root level="info">
<appender-ref ref="stdout" />
<appender-ref ref="appLogAppender" />
</root>
</configuration>
5.3 AOP结合自定义注解实现日志统一处理
- 引入aop依赖
- 自定义注解
- 自定义切面类
常用的注解:
@Aspect | 标明这是一个切面类 |
@Componet | 标明这是一个bean |
@Pointcut("annotation(com.xsbc.annotation.SamleLog") | 定义切入点为自定义的注解 |
@before | 前置通知,在一个方法执行之前被调用 |
@after | 在方法执行之后调用的通知,无论方法执行是否成功 |
@after-returning | 仅当方法执行成功之后通知 |
@after-throwing | 在方法抛出异常退出时执行的通知 |
@around | 在方法执行之前和之后执行的通知 |
1)引入AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2)自定义注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SampleLog {
String value() default "";
}
3)自定义切面类
@Aspect
@Component
public class LogAspect {
private final Logger logger=LoggerFactory.getLogger(LogAspect.class);
@Pointcut("@annotation(com.xsbc.annotation.SampleLog)")
public void samplePointCut(){
//签名,可以理解成这个切入点的一个名称
}
@Before("samplePointCut()")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes attributes=(ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
assert attributes != null;
HttpServletRequest request=attributes.getRequest();
// 获取url,请求方法,ip地址,类名以及方法名,参数
logger.info("url={},method={},ip={},args={}",request.getRequestURI(),
request.getMethod(),request.getRemoteAddr(),
joinPoint.getArgs());
}
@AfterReturning("samplePointCut()")
public void printLog(JoinPoint joinPoint){
MethodSignature signature=(MethodSignature) joinPoint.getSignature();
Method method=signature.getMethod();
SampleLog sampleLog=method.getAnnotation(SampleLog.class);
String value=null;
if(sampleLog!=null){
value=sampleLog.value();
}
logger.info(new Date()+"------"+value);
}
}
4)Controller类
@RestController
@RequestMapping("/Log")
public class LogController {
Logger logger=LoggerFactory.getLogger(LogController.class);
@GetMapping("/testLog")
@SampleLog("测试AOP日志")
public void testLog(){
logger.trace("这是trace级别的日志");
logger.debug("这是debug级别的日志");
logger.info("这是正常自定义的日志");
logger.warn("这是trace级别的日志");
logger.error("这是error级别的日志");
}
}