文章目录
- 【实战教程】SpringBoot全面指南:快速上手到项目实战(SpringBoot)
- 1. SpringBoot介绍
- 1.1 SpringBoot简介
- 1.2系统要求
- 1.3 SpringBoot和SpringMVC区别
- 1.4 SpringBoot和SpringCloud区别
- 2.快速入门
- 3. Web开发
- 3.1 静态资源访问
- 3.2 渲染Web页面
- 3.3 YML与Properties用法
- 3.4使用Freemarker模板引擎渲染web视图
- 3.5 使用thymeleaf渲染Web页面
- 4.数据库访问
- 4.1 springboot整合使用JdbcTemplate
- 4.2 springboot整合使用mybatis
- 4.3 springboot整合多数据源
- 5.事务管理
- 5.1 Springboot整合事务管理
- 5.2 SpringBoot分布式事务管理
- 6.整合热部署
- 6.1 Spring Boot集成lombok让代码更简洁
- 6.2 Spring Boot整合热部署框架
- 7.整合配置文件
- 7.1使用@value注解和@ConfigurationProperties注解
- 7.2配置文件占位符
- 7.3多环境配置和核心配置
- 8.日志管理
- 8.1使用logback记录日志
- 8.2使用log4j记录日志
- 8.3使用AOP统一处理Web请求日志
- 9.其他内容
- 9.1使用@Scheduled创建定时任务
- 9.2使用@Async实现异步调用
- 9.3全局捕获异常
- 9.4发布打包
【实战教程】SpringBoot全面指南:快速上手到项目实战(SpringBoot)
1. SpringBoot介绍
1.1 SpringBoot简介
SpringBoot 是一个快速开发的框架, 封装了Maven常用依赖、能够快速的整合第三方框架;简化XML配置,全部采用注解形式,内置Tomcat、Jetty、Undertow,帮助开发者能够实现快速开发,SpringBoot的Web组件 默认集成的是SpringMVC框架。
SpringBoot原理介绍:
- 能够帮助开发者实现快速整合第三方框架 (原理:Maven依赖封装)
- 去除xml配置 完全采用注解化 (原理:Spring体系中内置注解方式)
- 无需外部Tomcat、内部实现服务器(原理:Java语言支持内嵌入Tomcat服务器)
1.2系统要求
Java1.8及以上
Spring Framework 5.0及以上
本课程采用Java1.8版本、SpringBoot2.1.8版本
1.3 SpringBoot和SpringMVC区别
SpringBoot 是一个快速开发的框架,能够快速的整合第三方框架,简化XML配置,全部采用注解形式,内置Tomcat容器,帮助开发者能够实现快速开发,SpringBoot的Web组件默认集成的是SpringMVC框架。
SpringMVC是控制层。
1.4 SpringBoot和SpringCloud区别
SpringBoot 是一个快速开发的框架,能够快速的整合第三方框架,简化XML配置,全部采用注解形式,内置Tomcat容器,帮助开发者能够实现快速开发,SpringBoot的Web组件 默认集成的是SpringMVC框架。
SpringMVC是控制层。
SpringCloud依赖与SpringBoot组件,使用SpringMVC编写Http协议接口,同时SpringCloud是一套完整的微服务解决框架。
2.快速入门
2.1创建一个Maven工程
2.2 pom文件引入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
spring-boot-starter-parent
作用
在pom.xml中引入spring-boot-start-parent
,spring官方的解释叫什么stater poms,它可以提供dependency management,也就是说依赖管理,引入以后在申明其它dependency的时候就不需要version了,后面可以看到。
spring-boot-starter-web作用
springweb 核心组件
2.3 @RestController
@RestController
是由 @Controller(控制层注解)+@ResponBody(数据传输格式为 json 格式)
组合而成的
Rest 微服务接口开发中 rest 风格的数据传输格式为 json 格式,采用HTTP协议
在上加上RestController
表示修饰该Controller
所有的方法返回JSON
格式,直接可以编写
Restful
接口
注意该注解是(@RestController )SpringMVC
提供的哦!
2.4@EnableAutoConfiguration
该注解:作用在于让 Spring Boot 根据应用所声明的依赖来对 Spring 框架进行自动配置
这个注解告诉Spring Boot根据添加的jar依赖猜测你想如何配置Spring。由于spring-boot-starter-web添加了Tomcat和Spring MVC,所以auto-configuration将假定你正在开发一个web应用并相应地对Spring进行设置。
2.5 SpringBoot
的3种启动方式
方式一
package com.zhaoli.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAutoConfiguration
public class HelloworldService {
@RequestMapping("/hello")
public String hello() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(HelloworldService.class, args);
}
}
可以请求本类中的 url
启动主程序,打开浏览器访问http://127.0.0.1:8080/hello
,可以看到页面输出Hello World
方式二
package com.zhaoli.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan("com.zhaoli.service")//扫包范围
@EnableAutoConfiguration
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
package com.zhaoli.service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HellowordService {
@RequestMapping("/hello02")
public String hello02() {
return "Hello 赵立";
}
}
可以启动扫包范围下的所有类中的 url
方式三(推荐使用)
在启动类上直接加上@SpringBootApplication
即可
@SpringBootApplication
被 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
注解所修饰,换言之 Springboot
提供了统一的注解来替代以上三个注解
扫包范围:在启动类上加上@SpringBootApplication
注解,当前包下或者子包下所有的类都可以扫到。
package com.zhaoli.service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
3. Web开发
3.1 静态资源访问
在我们开发Web
应用的时候,需要引用大量的js
、css
、图片等静态资源。
默认配置
Spring Boot
默认提供静态资源目录位置需置于classpath
下,目录名需符合如下规则:
/static
/public
/resources
/META-INF/resources
举例:我们可以在src/main/resources/
目录下创建static
,在该位置放置一个图片文件。启动程序后,尝试访问http://localhost:8080/one.png
。如能显示图片,配置成功。
微服务项目
前后端分离
前端----vue----前端工程师
后端—springboot–后端工程师
动静分离 部署cdn上
cdn 减少带宽距离传输 减少自己服务器带宽;
3.2 渲染Web页面
com.zhaoli.controller
—视图层 渲染我们页面
com.zhaoli.service
—业务逻辑层
com.zhaoli.dao
—数据库访问层
渲染Web页面
在之前的示例中,我们都是通过@RestController
来处理请求,所以返回的内容为json
对象。那么如果需要渲染html
页面的时候,要如何实现呢?
模板引擎 能够非常好的帮助seo搜索到该网页
在动态HTML
实现上Spring Boot
依然可以完美胜任,并且提供了多种模板引擎的默认配置支持,所以在推荐的模板引擎下,我们可以很快的上手开发动态网站。
Spring Boot
提供了默认配置的模板引擎主要有以下几种:
- Thymeleaf
- FreeMarker
- Velocity
- Groovy
- Mustache
Spring Boot
建议使用这些模板引擎,避免使用JSP
,若一定要使用JSP
将无法实现Spring Boot
的多种特性,具体可见后文:支持JSP的配置
当你使用上述模板引擎中的任何一个,它们默认的模板配置路径为:src/main/resources/templates
。当然也可以修改这个路径,具体如何修改,可在后续各模板引擎的配置属性中查询并修改。
3.3 YML与Properties用法
SpringBoot
支持两种配置方式,一种是properties
文件,一种是yml
。(都是在resources
目录下)
使用yml
可以减少配置文件的重复性。
例如:application.properties
配置
test.name=zhaoli
test.age=21
例如:application.yml
配置
test:
name: zhaoli
age: 20
在企业中application.yml
方式用的是比较多的;
package com.zhaoli.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MayiktPropertiesService {
@Value("${mayikt.name}")
private String name;
@Value("${mayikt.age}")
private String age;
@RequestMapping("/getProperties")
public String getProperties(){
return name+"---------"+age;
}
}
3.4使用Freemarker模板引擎渲染web视图
1.Maven依赖
<!-- 引入freeMarker的依赖包. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
2.后端代码
package com.zhaoli.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
@Controller
public class FreemarkerIndexController {
@RequestMapping("/freemarkerIndex")
public String freemarkerIndex(Map<String,String> result){
//转发到页面展示数据
result.put("name","赵立");
return "freemarkerIndex";
}
}
3.前端代码
在src/main/resources/
创建一个templates
文件夹,用来存 *.ftl
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8"/>
<title></title>
</head>
<body>
${name}
</body>
</html>
4.Freemarker其他用法
Freemarker
配置
新建application.yml
文件 (加粗提示对应的配置可以进行修改)
spring:
##Freemarker配置
http:
encoding:
force: true
### 模版引擎编码为UTF-8
charset: UTF-8
freemarker:
allow-request-override: false
cache: false
check-template-location: true
charset: UTF-8
content-type: text/html; charset=utf-8
expose-request-attributes: false
expose-session-attributes: false
expose-spring-macro-helpers: false
## 模版文件结尾.ftl
suffix: .ftl
## 模版文件目录
template-loader-path: classpath:/templates
*.ftl
文件中使用符号替换方法
符号 | 替换为(条件判断) |
---|---|
> | gt 或者 <#if(x>y)> |
>= | gte 或者 <#if(x>=y)> |
< | lt 或者 <#if(x<y)> |
<= | lte 或者 <#if(x<=y)> |
判断条件用法
com.zhaoli.controller.FreemarkerIndexController
@RequestMapping("/freemarkerIndex02")
public String freemarkerIndex02(Map<String, Object> result) {
//转发到页面展示数据
result.put("name", "赵立");
//0为男 1为女
result.put("sex", "0");
result.put("age", 21);
return "freemarkerIndex02";
}
freemarkerIndex02.ftl
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8"/>
<title></title>
</head>
<body>
${name}
<#if sex=='0'>
男
<#elseif sex='1'>
女
<#else >
其他
</#if>
<#if age gt 17>
已经成年了
<#else >
没成年
</#if>
</body>
</html>
list集合
com.zhaoli.controller.FreemarkerIndexController
@RequestMapping("/freemarkerIndex03")
public String freemarkerIndex03(Map<String, Object> result) {
//转发到页面展示数据
List<String> userLists = new ArrayList<>();
userLists.add("zl");
userLists.add("fj");
userLists.add("lfc");
userLists.add("ywq");
result.put("userList",userLists);
return "freemarkerIndex03";
freemarkerIndex03.ftl
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8"/>
<title></title>
</head>
<body>
<#list userList as user>
${user}
</#list>
</body>
</html>
3.5 使用thymeleaf渲染Web页面
1 .什么是thymeleaf
thymeleaf
是一款用于渲染XML/XHTML/HTML5
内容的模板引擎,类似JSP
,Velocity
,FreeMaker
等,它也可以轻易的与Spring MVC
等Web
框架进行集成作为Web
应用的模板引擎。
2.Maven依赖
<!--引入thymeleaf的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
3.配置文件新增
新建application.yml
文件
###ThymeLeaf配置
spring:
thymeleaf:
#prefix:指定模板所在的目录
prefix: classpath:/templates/
#check-tempate-location: 检查模板路径是否存在
check-template-location: true
#cache: 是否缓存,开发模式下设置为false,避免改了模板还要重启服务器,线上设置为true,可以提高性能。
cache: true
suffix: .html
encoding: UTF-8
mode: HTML5
4.案例代码
com.zhaoli.entity.UserEntity
package com.zhaoli.entity;
public class UserEntity {
private String userName;
private Integer age;
}
com.zhaoli.controller.ThymeleafController
package com.zhaoli.controller;
import com.zhaoli.entity.UserEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
@Controller
public class ThymeleafController {
@RequestMapping("/thymeleaf")
public String thymeleaf(Map<String, Object> result) {
result.put("user", new UserEntity("赵立", 20));
return "thymeleaf";
}
}
在src/main/resources/
创建一个templates
文件夹,用来存 *html
<!DOCTYPE html>
<!--需要在HTML文件中加入以下语句: -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Show User</title>
</head>
<body>
<table>
姓名:<span th:text="${user.userName}"></span>
年龄:<span th:text="${user.age}"></span>
</table>
</body>
</html>
5.高级写法
循环语句:
com.zhaoli.entity.UserEntity
同上
com.zhaoli.controller.ThymeleafController
@RequestMapping("/thymeleaf02")
public String thymeleaf02(Map<String, Object> result) {
List<UserEntity> userLists = new ArrayList<>();
userLists.add(new UserEntity("赵立", 21));
userLists.add(new UserEntity("fj", 20));
result.put("userList", userLists);
return "thymeleaf02";
}
thymeleaf02.html
<!DOCTYPE html>
<!--需要在HTML文件中加入以下语句: -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Show User</title>
</head>
<body>
<table>
<ul th:each="user:${userList}">
<li th:text="${user.userName}"></li>
<li th:text="${user.age}"></li>
</ul>
</table>
</body>
</html>
If判断:
<span th:if="${user.age>17}">已经成年啦</span>
<span th:if="${user.age<17}">未成年</span>
详细可以更多语法可以查看https://www.thymeleaf.org/documentation.html
4.数据库访问
4.1 springboot整合使用JdbcTemplate
1.pom文件引入
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入freeMarker的依赖包. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--引入thymeleaf的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- SpringBoot 整合 jdbc 模板框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- SpringBoot 整合 mysql 驱动类 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
</dependencies>
2.application.yml
新增配置
##springboot整合使用JdbcTemplate
datasource:
url: jdbc:mysql://localhost:3306/mayikt_springboot?serverTimezone=UTC&useSSL=false
username: root
password: 20020806
driver-class-name: com.mysql.jdbc.Driver
3.数据库表结构
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '用户名称',
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
4.案例代码
com.zhaoli.entity.UserEntity
public class MayiktUserEntity {
private Integer id;
private String userName;
private Integer age;
}
com.zhaoli.service.UserService
package com.zhaoli.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 插入数据到 users 表
*/
@RequestMapping("/inserUser")
public String inserUser(String userName, Integer age) {
int update = jdbcTemplate.update("INSERT INTO users VALUES(null,?,?);", userName, age);
return update > 0 ? "success" : "fail";
}
}
com.zhaoli.App
App类
package com.zhaoli;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
4.2 springboot整合使用mybatis
1.pom文件引入
<!-- springboot 整合mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
2.application.yml
新增配置
##springboot整合使用mybatis
datasource:
url: jdbc:mysql://localhost:3306/mayikt_springboot?serverTimezone=UTC&useSSL=false
username: root
password: 20020806
driver-class-name: com.mysql.jdbc.Driver
3.案例代码
数据库表结构同上
com.zhaoli.entity.UserEntity
同上
com.zhaoli.mapper.UserMapper
接口
package com.zhaoli.mapper;
import com.zhaoli.entity.MayiktUserEntity;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
@Insert("INSERT INTO users VALUES(null,#{userName},#{age});")
int insertUser(@Param("userName") String userName, @Param("age")Integer age);
@Select("SELECT id,name AS userName,age FROM users WHERE id=#{id};")
MayiktUserEntity selectByUserId(@Param("id")Integer id);
}
com.zhaoli.service.UserService
package com.zhaoli.service;
import com.zhaoli.entity.MayiktUserEntity;
import com.zhaoli.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserService {
@Autowired
private UserMapper userMapper;
/**
* mybatis 查询
*/
@RequestMapping("/mybatisfinbyId")
public MayiktUserEntity mybatisfinbyId(Integer id){
return userMapper.selectByUserId(id);
}
/**
* mybatis 插入
*/
@RequestMapping("/mybatisInser")
public String mybatisInser(String userName, Integer age) {
int inser = userMapper.insertUser(userName, age);
return inser > 0 ? "success" : "fail";
}
}
com.zhaoli.App
App类
package com.zhaoli;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.zhaoli.mapper")//将 mapper 包下的类、接口注入到ioc 容器中
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
4.3 springboot整合多数据源
1.Maven相关依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot 整合mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- SpringBoot 整合 mysql 驱动类 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
</dependencies>
2.数据库表结构
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL COMMENT '用户名称',
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
CREATE TABLE `order_number` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
3.配置文件中新增两个数据源
resources.application.yml
spring:
datasource:
###会员数据库
member:
jdbc-url: jdbc:mysql://localhost:3306/user?serverTimezone=UTC&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
###订单数据库
order:
jdbc-url: jdbc:mysql://localhost:3306/order?serverTimezone=UTC&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
备注:如果是SpringBoot2
配置多数据源 ,报如下错误:
jdbcUrl is required with driverClassName.
或者Cause: java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.] with root cause
解决方案:
spring.datasource.url
和spring.datasource.driverClassName
,换成
spring.datasource.jdbc-url
和spring.datasource.driver-class-name
4.数据库数据源相关配置
会员数据源
@Configuration
@MapperScan(basePackages = "com.zhaoli.member.mapper", sqlSessionFactoryRef = "memberSqlSessionFactory")
public class MemberDataSourceConfig {
/**
* 将会员db注册到容器中
*
* @return
*/
@Bean(name = "memberDataSource")
@ConfigurationProperties(prefix = "spring.datasource.member")
public DataSource memberDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 将会员SqlSessionFactory注册到容器中
*
* @param dataSource
* @return
* @throws Exception
*/
@Bean(name = "memberSqlSessionFactory")
public SqlSessionFactory memberSqlSessionFactory(@Qualifier("memberDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(memberDataSource());
return sqlSessionFactoryBean.getObject();
}
/**
* 创建会员管理器
*
* @param dataSource
* @return
*/
@Bean(name = "memberTransactionManager")
public DataSourceTransactionManager memberTransactionManager(@Qualifier("memberDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/**
* 创建订单sqlSesion模版
*
* @param sqlSessionFactory
* @return
* @throws Exception
*/
@Bean(name = "memberSqlSessionTemplate")
public SqlSessionTemplate menberSqlSessionTemplate(
@Qualifier("memberSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
订单数据源
@Configuration
@MapperScan(basePackages = "com.zhaoli.order.mapper", sqlSessionFactoryRef = "orderSqlSessionFactory")
public class OrderDataSourceConfig {
/**
* 将订单db注册到容器中
*
* @return
*/
@Bean(name = "orderDataSource")
@ConfigurationProperties(prefix = "spring.datasource.order")
public DataSource orderDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 将订单SqlSessionFactory注册到容器中
*
* @param dataSource
* @return
* @throws Exception
*/
@Bean(name = "orderSqlSessionFactory")
public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(orderDataSource());
return sqlSessionFactoryBean.getObject();
}
/**
* 创建订单管理器
*
* @param dataSource
* @return
*/
@Bean(name = "orderTransactionManager")
public DataSourceTransactionManager orderTransactionManager(@Qualifier("orderDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/**
* 创建订单sqlSesion模版
*
* @param sqlSessionFactory
* @return
* @throws Exception
*/
@Bean(name = "orderSqlSessionTemplate")
public SqlSessionTemplate menberSqlSessionTemplate(
@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
5.创建分包Mapper
会员mapper
public interface MemberMapper {
@Insert("insert into users values(null,#{name},#{age});")
public int addUser(@Param("name") String name, @Param("age") Integer age);
}
订单mapper
public interface OrderMapper {
@Insert("insert into order_number values(null,#{number});")
int inserOrder(@Param("number") String number);
}
6.启动项目
@SpringBootApplication
public class App{
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
5.事务管理
5.1 Springboot整合事务管理
springboot默认集成事务,只主要在方法上加上@Transactional
即可
5.2 SpringBoot分布式事务管理
使用springboot+jta+atomikos
分布式事物Transactional
管理
1.多数据源分布式事务案例
@Service
public class MemberService {
@Autowired
private MemberMapper memberMapper;
@Autowired
private OrderMapper orderMapper;
@Transactional(transactionManager = "memberTransactionManager")
public int addUser(String userName, Integer age) {
int result = memberMapper.addUser(userName, age);
orderMapper.inserOrder(userName);
int j = 1 / age;
return result;
}
}
2.Maven相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
3.新增配置文件信息
spring:
datasource:
###会员数据库
member:
url: jdbc:mysql://localhost:3306/user?serverTimezone=UTC&useSSL=false
username: root
password: root
borrowConnectionTimeout: 30
loginTimeout: 30
maintenanceInterval: 60
maxIdleTime: 60
maxLifetime: 20000
maxPoolSize: 25
minPoolSize: 3
uniqueResourceName: orderDatasource
###订单数据库
order:
url: jdbc:mysql://localhost:3306/order?serverTimezone=UTC&useSSL=false
username: root
password: root
borrowConnectionTimeout: 30
loginTimeout: 30
maintenanceInterval: 60
maxIdleTime: 60
maxLifetime: 20000
maxPoolSize: 25
minPoolSize: 3
uniqueResourceName: memberDatasource
4.读取配置文件信息
@ConfigurationProperties(prefix = "spring.datasource.member")
@Data
public class MemberConfig {
private String url;
private String userName;
private String passWord;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
private String uniqueResourceName;
}
@ConfigurationProperties(prefix = "spring.datasource.order")
@Data
public class OrderConfig {
private String url;
private String userName;
private String passWord;
private int minPoolSize;
private int maxPoolSize;
private int maxLifetime;
private int borrowConnectionTimeout;
private int loginTimeout;
private int maintenanceInterval;
private int maxIdleTime;
private String testQuery;
private String uniqueResourceName;
}
5.创建多数据源
@Configuration
@MapperScan(basePackages = "com.zhaoli.member.mapper", sqlSessionTemplateRef = "memberSqlSessionTemplate")
public class MemberDataSourceConfig {
//@Configuration xml MemberDataSourceConfig.xml
/**
* 创建我们的DataSource
*/
@Bean("memberDataSource")
public DataSource memberDataSource(MemberConfig memberConfig) throws SQLException {
// 1.创建MysqlXADataSource
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(memberConfig.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(memberConfig.getPassWord());
mysqlXaDataSource.setUser(memberConfig.getUserName());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
// 2.将本地事务注册到创 Atomikos全局事务
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName(memberConfig.getUniqueResourceName());
xaDataSource.setMinPoolSize(memberConfig.getMinPoolSize());
xaDataSource.setMaxPoolSize(memberConfig.getMaxPoolSize());
xaDataSource.setMaxLifetime(memberConfig.getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(memberConfig.getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(memberConfig.getLoginTimeout());
xaDataSource.setMaintenanceInterval(memberConfig.getMaintenanceInterval());
xaDataSource.setMaxIdleTime(memberConfig.getMaxIdleTime());
xaDataSource.setTestQuery(memberConfig.getTestQuery());
return xaDataSource;
}
/**
* 创建我们的SqlSessionFactory
*/
@Bean(name = "memberSqlSessionFactory")
public SqlSessionFactory memberSqlSessionFactory(@Qualifier("memberDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
/**
* 创建订单sqlSesion模版
*/
@Bean(name = "memberSqlSessionTemplate")
public SqlSessionTemplate memberSqlSessionTemplate(
@Qualifier("memberSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
@Configuration
@MapperScan(basePackages = "com.zhaoli.order.mapper", sqlSessionTemplateRef = "orderSqlSessionTemplate")
public class OrderDataSourceConfig {
//@Configuration xml orderDataSourceConfig.xml
/**
* 创建我们的DataSource
*/
@Bean("orderDataSource")
public DataSource orderDataSource(OrderConfig orderConfig) throws SQLException {
// 1.创建MysqlXADataSource
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setUrl(orderConfig.getUrl());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setPassword(orderConfig.getPassWord());
mysqlXaDataSource.setUser(orderConfig.getUserName());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
// 2.将本地事务注册到创 Atomikos全局事务
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName(orderConfig.getUniqueResourceName());
xaDataSource.setMinPoolSize(orderConfig.getMinPoolSize());
xaDataSource.setMaxPoolSize(orderConfig.getMaxPoolSize());
xaDataSource.setMaxLifetime(orderConfig.getMaxLifetime());
xaDataSource.setBorrowConnectionTimeout(orderConfig.getBorrowConnectionTimeout());
xaDataSource.setLoginTimeout(orderConfig.getLoginTimeout());
xaDataSource.setMaintenanceInterval(orderConfig.getMaintenanceInterval());
xaDataSource.setMaxIdleTime(orderConfig.getMaxIdleTime());
xaDataSource.setTestQuery(orderConfig.getTestQuery());
return xaDataSource;
}
/**
* 创建我们的SqlSessionFactory
*/
@Bean(name = "orderSqlSessionFactory")
public SqlSessionFactory orderSqlSessionFactory(@Qualifier("orderDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean.getObject();
}
//
// /**
// * 创建会员管理器
// */
// @Bean(name = "orderTransactionManager")
// public DataSourceTransactionManager orderTransactionManager(@Qualifier("orderDataSource") DataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
/**
* 创建订单sqlSesion模版
*/
@Bean(name = "orderSqlSessionTemplate")
public SqlSessionTemplate orderSqlSessionTemplate(
@Qualifier("orderSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
如果多数据源使用事务报错的话
cted single matching bean but found 2: memberTransactionManager,orderTransactionManager
@Transactional(transactionManager = "memberTransactionManager")
明确指定使用那个事务管理器即可
6.启动加载配置
@EnableConfigurationProperties({OrderConfig.class, MemberConfig.class})
6.整合热部署
6.1 Spring Boot集成lombok让代码更简洁
1.需要安装Idea整合 整合Lombok插件
2.搜索Lombok插件即可
3.点击install然后,安装成功之后,点击 重启idea 即可。
整合lombok注意事项
- 需要在idea中安装lombok插件;-----没有做
- 引入到lombok依赖;
加上了注解,根本就没有生成get和set方法。
原理:
实际上在开发写代码的时候 是不需要写get和set方法,但是在编译class文件中,帮你自动生成好这样get和set方法 放入到class文件中。
4.添加lombok依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
5.打印日志
//App 是类的名称,在那个类中用就写那个类
private static Logger log = Logger.getLogger(App.class);
直接在类上加上@Slf4j
就不用写上面这行代码
6.其他特性
注解 | 描述 |
---|---|
@Data | 自动生成getter/setter方法,toString() 方法,equals() 方法,hashCode() 方法,不带参数的构造方法。 |
@NonNull | 避免NullPointerException ,确保非空对象引用。 |
@CleanUp | 自动管理需要关闭的资源,无需手动在finally 块中调用close() 方法。 |
@Setter | 自动生成setter方法。 |
@Getter | 自动生成getter方法。 |
@ToString | 自动生成toString() 方法。 |
@EqualsAndHashCode | 从对象的字段生成hashCode() 和equals() 方法的实现。 |
@NoArgsConstructor | 自动生成无参构造方法。 |
@RequiredArgsConstructor | 根据final或@NotNull修饰的属性自动生成构造方法。 |
@AllArgsConstructor | 自动生成包含所有属性的构造方法。 |
@Value | 用于注解不可变的final类。 |
@Builder | 自动生成复杂对象的构造器API。 |
@SneakyThrows | 捕获并隐藏检查型异常,需谨慎使用。 |
@Synchronized | 将方法或代码块标记为同步的,保证线程安全。 |
@Getter(lazy=true) | 延迟加载属性值,仅适用于Java 8及以上版本。 |
@Log | 支持不同的日志框架,例如使用@Log4j 注解引入Log4j日志对象。 |
6.2 Spring Boot整合热部署框架
1.什么是热部署
修改java类或页面或者静态文件,不需要手动重启
原理:类加载器
适合于本地开发环境
2. Maven依赖
<!--SpringBoot热部署配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
3. Idea工具设置
-
`“File” -> “Settings” -> “Build,Execution,Deplyment” -> “Compiler”,选中打勾 “Build project automatically”
-
组合键:“Shift+Ctrl+Alt+/” ,选择 “Registry” ,选中打勾 “compiler.automake.allow.when.app.running”
新版本的IDEA在这里设置
4.效果演示
按住保存键,自动帮我实现重启项目
7.整合配置文件
1. 在springboot
整合配置文件,分成两大类:
application.properties
application.yml
或者是
Bootstrap.properties
Bootstrap.yml
相对于来说yml
文件格式写法更加精简,减少配置文件的冗余性。
2. 加载顺序:
bootstrap.yml
先加载 application.yml
后加载
bootstrap.yml
用于应用程序上下文的引导阶段。
bootstrap.yml
由父Spring ApplicationContext
加载。
3.区别:
bootstrap.yml
和 application.yml
都可以用来配置参数。
bootstrap.yml
用来程序引导时执行,应用于更加早期配置信息读取。可以理解成系统级别的一些参数配置,这些参数一般是不会变动的。一旦bootStrap.yml
被加载,则内容不会被覆盖。
application.yml
可以用来定义应用级别的, 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。
分布式配置中心:
Properties
在线转换yml
格式网址:https://www.toyaml.com/index.html
7.1使用@value注解和@ConfigurationProperties注解
@value
注解
@Value("${test.name}")//可以获取到配置文件中对应的值
private String name;
@ConfigurationProperties
注解
Maven依赖
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
com.zhaoli.entity
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "test")
public class TestUserEntity {
private String addres;
private String age;
private String name;
//省略了get set toString
}
配置文件
test:
addres: www.zhaoli.com
age: 22
name: test
com.zhaoli.service
@Autowired
private TestUserEntity userEntity;
@RequestMapping("/getNameAndAgeAddres")
public String getNameAndAgeAddres() {
return userEntity.toString();
}
7.2配置文件占位符
在SpringBoot
的配置文件中,我们可以使用SpringBoot
提供的的一些随机数
${random.value}
、${random.int}
、${random.long}
${random.int(10)}
表示获取10以内的随机数、${random.int[1024,65536]}
-${app.name:默认值}
来制定找不到属性时的默认值
7.3多环境配置和核心配置
resources.templates.application.yml
里面指定读取文件的名称
spring:
profiles:
active: (dev或者test或者prd)
application-dev.yml
:开发环境
application-test.yml
:测试环境
application-prd.yml
:生产环境
核心配置
server:
## 设定端口号
port: 8081
servlet:
## 设置 springboot 项目访问路径
context-path: /mayikt
Springboot 默认的情况下整合tomcat容器
8.日志管理
8.1使用logback记录日志
Springboot
已经默认帮你整合好了logback
日志输出文件在当前项目路径log
文件夹下
1.Maven依赖
<!-- 添加lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2.在resources
包下新建一个log
包,在包下建 logback.xml
在里面进行配置(拷贝到里面即可)
<configuration>
<!--本文主要输出日志为控制台日志,系统日志,sql日志,异常日志-->
<!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,,,, -->
<!--控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %p (%file:%line\)- %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--系统info级别日志-->
<!--<File> 日志目录,没有会自动创建-->
<!--<rollingPolicy>日志策略,每天简历一个日志文件,或者当天日志文件超过64MB时-->
<!--encoder 日志编码及输出格式-->
<appender name="fileLog"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>log/file/fileLog.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>log/file/fileLog.log.%d.%i</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 64 MB -->
<maxFileSize>64 MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>
%d %p (%file:%line\)- %m%n
</pattern>
<charset>UTF-8</charset>
<!-- 此处设置字符集 -->
</encoder>
</appender>
<!--sql日志-->
<appender name="sqlFile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>log/sql/sqlFile.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>log/sql/sqlFile.log.%d.%i</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 64 MB -->
<maxFileSize>64 MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!--对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。-->
<encoder>
<!--用来设置日志的输入格式-->
<pattern>
%d %p (%file:%line\)- %m%n
</pattern>
<charset>UTF-8</charset>
<!-- 此处设置字符集 -->
</encoder>
</appender>
<!--异常日志-->
<appender name="errorFile"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>log/error/errorFile.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>log/error/errorFile.%d.log.%i</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 64 MB -->
<maxFileSize>64 MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!--对记录事件进行格式化。负责两件事,一是把日志信息转换成字节数组,二是把字节数组写入到输出流。-->
<encoder>
<!--用来设置日志的输入格式-->
<pattern>
%d %p (%file:%line\)- %m%n
</pattern>
<charset>UTF-8</charset>
<!-- 此处设置字符集 -->
</encoder>
<!--
日志都在这里 过滤出 error
使用 try {}catch (Exception e){} 的话异常无法写入日志,可以在catch里用logger.error()方法手动写入日志
-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 日志输出级别 -->
<!--All\DEBUG\INFO\WARN\ERROR\FATAL\OFF-->
<!--打印info级别日志,分别在控制台,fileLog,errorFile输出
异常日志在上面由过滤器过滤出ERROR日志打印
-->
<root level="INFO">
<appender-ref ref="fileLog" />
<appender-ref ref="console" />
<appender-ref ref="errorFile" />
</root>
<!--打印sql至sqlFile文件日志-->
<logger name="com.dolphin.mapper" level="DEBUG" additivity="false">
<appender-ref ref="console" />
<appender-ref ref="sqlFile" />
</logger>
</configuration>
3.在 resources
目录下的 application.yml
文件内新增 application
配置
###指定读取logback配置文件
logging:
config: classpath:log/logback.xml
4.测试案例
@RestController
@Slf4j
public class MyIndexService {
/**
* 演示打印日志
*/
@RequestMapping("/getNameAndAge")
public String getNameAndAge(String name,Integer age){
log.info("name:{},age:{}",name,age);
return name+","+age;
}
}
5.日志级别
ALL
最低等级的,用于打开所有日志记录。
TRACE designates finer-grained informational events than the DEBUG.Since:1.2.12
,很低的日志级别,一般不会使用。
DEBUG
指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
INFO
消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
WARN
表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示
ERROR
指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
FATAL
指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
OFF
最高等级的,用于关闭所有日志记录。
6.日志目录
8.2使用log4j记录日志
日志级别 | 描述 | 输出源 | 格式 |
---|---|---|---|
TRACE | 追踪,程序每推进一步可以写入一个TRACE级别的日志,通常用于非常详细的执行流程跟踪。 | CONSOLE | SimpleLayout |
DEBUG | 调试,用于记录调试信息,通常是最低级别,帮助开发者理解程序运行情况。 | FILE | HTMLLayout |
INFO | 重要信息,用于记录应用程序运行的关键信息,是使用较为频繁的日志级别。 | PatternLayout | |
WARN | 警告,记录可能需要注意的信息,这些信息虽然不是错误,但可能会导致问题或需要注意的情况。 | ||
ERROR | 错误,记录应用程序中的错误信息,这类信息表明应用程序中出现了问题。 | CONSOLE | |
FATAL | 致命错误,记录非常严重的错误信息,通常指应用程序无法继续运行下去的情况。 | FILE | |
机制:如果一条日志信息的级别大于等于配置文件的级别,则记录这条日志信息。 | |||
输出源:日志可以输出到控制台(CONSOLE)或文件(FILE)。 | |||
格式:日志输出的格式可以是简单的(SimpleLayout),HTML表格形式(HTMLLayout),或者自定义格式(PatternLayout)。 |
1.Maven依赖
<!-- spring boot start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<!-- 排除自带的logback依赖 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- springboot-log4j -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
<!-- spring boot start -->
这段代码是使用Maven
构建项目时,添加spring-boot-starter
依赖的一种方式,并且通过exclusions
排除了其中的logback
依赖。
当你使用类似的配置时,spring-boot-starter
依赖会被添加到项目中,并且排除了 spring-boot-starter-logging
。这样,你可以根据自己的需求选择适合的日志框架进行使用
这个排除logback
依赖的操作可以用来替换其他的日志框架,或者使用自定义的日志框架进行日志的输出
2.新建log4j
配置文件
在 resources
目录下新建文件log4j.properties
记得根据自己的项目修改里面的日志输出路径
#log4j.rootLogger=CONSOLE,info,error,DEBUG
log4j.rootLogger=DEBUG,error,CONSOLE,info
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.layout=org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n
log4j.appender.info.datePattern='.'yyyy-MM-dd
log4j.appender.info.Threshold = info
log4j.appender.info.append=true
log4j.appender.info.File=E:/code/log/info.log
log4j.logger.error=error
log4j.appender.error=org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.layout=org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n
log4j.appender.error.datePattern='.'yyyy-MM-dd
log4j.appender.error.Threshold = error
log4j.appender.error.append=true
log4j.appender.error.File=E:/code/log/error.log
log4j.logger.DEBUG=DEBUG
log4j.appender.DEBUG=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DEBUG.layout=org.apache.log4j.PatternLayout
log4j.appender.DEBUG.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n
log4j.appender.DEBUG.datePattern='.'yyyy-MM-dd
log4j.appender.DEBUG.Threshold = DEBUG
log4j.appender.DEBUG.append=true
log4j.appender.DEBUG.File=E:/code/log/dubug.log
log4j代码
private static final Logger logger = LoggerFactory.getLogger(IndexController.class);
3.application
# 指定log4j.properties配置文件路径
logging:
config: classpath:log4j.properties
8.3使用AOP统一处理Web请求日志
一般项目都是基于AOP实现 或者elk实现的
它可以在我们的方法的前后实现拦截 减少打印日志代码的冗余性的问题
1.Maven依赖
<!-- springboot-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.AOP切面相关配置(直接拷贝即可)
package com.zhaoli.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
@Aspect
@Component
@Slf4j
public class WebLogAspect {
// private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
/**
* 切入点
* com.zhaoli.controller.* 拦截该包下的所有类
* com.zhaoli.controller.*.* 拦截该包下的所有类中的所有方法
* (..)所有的方法参数
*/
@Pointcut("execution(public * com.zhaoli.controller.*.*(..))")
public void webLog() {
}
/**
* 前置通知 请求方法之前做拦截
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
log.info("URL : " + request.getRequestURL().toString());
log.info("HTTP_METHOD : " + request.getMethod());
log.info("IP : " + request.getRemoteAddr());
Enumeration<String> enu = request.getParameterNames();
while (enu.hasMoreElements()) {
String name = (String) enu.nextElement();
log.info("name:{},value:{}", name, request.getParameter(name));
}
}
/**
* 目标方法请求之后 打印(响应)信息
*/
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
log.info("RESPONSE : " + ret);
}
}
9.其他内容
9.1使用@Scheduled创建定时任务
在Spring Boot
的主类(App
类即启动类)中加入@EnableScheduling
注解,启用定时任务的配置
package com.zhaoli.task;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class ScheduledTasks {
/**
* 每隔三秒执行到 taskService()
*/
@Scheduled(fixedRate = 3000)
public void taskService(){
log.info("<<定时任务执行>>"+System.currentTimeMillis());
}
}
加粗部分一般使用@Scheduled(cron = "0/2 * * * * *")
写法:
https://www.bejson.com/othertools/cron/
可以在此网站中查找双引号中的使用规范
9.2使用@Async实现异步调用
在 Spring Boot 应用的主类(App类即启动类)上添加 @EnableAsync 注解,用于启用 Spring 的异步执行功能。
需要执行异步方法上加入 @Async
具体来说,@Async
注解用于告诉 Spring
框架将被注解的方法放入一个任务执行器(Task Executor)中执行,而不是使用调用线程进行同步执行。任务执行器负责管理执行异步任务的线程池或者其他执行策略。
异步应用场景
@Async
实际就是多线程封装的
异步线程执行方法有可能会非常消耗cpu
的资源,所以大的项目建议使用
Mq
异步实现。
整合线程池
异步注解配置类
com.zhaoli.config.ThreadPoolConfig
(配置线程池,可直接拷贝)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class ThreadPoolConfig {
/**
* 每秒需要多少个线程处理?
* tasks/(1/taskcost)
*/
private int corePoolSize = 3;
/**
* 线程池维护线程的最大数量
* (max(tasks)- queueCapacity)/(1/taskcost)
*/
private int maxPoolSize = 3;
/**
* 缓存队列
* (coreSizePool/taskcost)*responsetime
*/
private int queueCapacity = 10;
/**
* 允许的空闲时间
* 默认为60
*/
private int keepAlive = 100;
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(corePoolSize);
// 设置最大线程数
executor.setMaxPoolSize(maxPoolSize);
// 设置队列容量
executor.setQueueCapacity(queueCapacity);
// 设置允许的空闲时间(秒)
//executor.setKeepAliveSeconds(keepAlive);
// 设置默认线程名称
executor.setThreadNamePrefix("thread-");
// 设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
代码案例
@Async("taskExecutor")
是一个Spring
框架中的注解,用于将方法标记为异步执行的方法。
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class MemberServiceAsync {
@Async("taskExecutor") //指定线程池名称
public String smsAsync() {
log.info(">02<");
try {
log.info(">正在发送短信..<");
Thread.sleep(3000);
} catch (Exception e) {
}
log.info(">03<");
return "短信发送完成!";
}
}
注意失效问题
注意:如果异步注解写当前自己类,有可能aop会失效,无法拦截注解,最终导致异步注解失效,需要经过代理类调用接口;
所以需要将异步的代码单独抽取成一个类调用接口。
9.3全局捕获异常
@ExceptionHandler
表示拦截异常
@ControllerAdvice
是 controller
的一个辅助类,最常用的就是作为全局异常处理的切面类
@ControllerAdvice
可以指定扫描范围
@ControllerAdvice
约定了几种可行的返回值,如果是直接返回 model
类的话,需要使用 @ResponseBody
进行 json
转换
- 返回 String,表示跳到某个
view
- 返回
modelAndView
- 返回
model + @ResponseBody
全局捕获异常配置类
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class MayiktExceptionHandler {
/**
* 拦截运行异常出现的错误~~~
*/
@ExceptionHandler(RuntimeException.class)
@ResponseBody
public Map<Object, Object> exceptionHandler() {
Map<Object, Object> map = new HashMap<>();
map.put("error", "500");
map.put("msg", "系统出现错误~");
return map;
}
}
没使用之前
给用户显示这个页面很不友好
使用全局捕获异常配置类之后的页面
9.4发布打包
使用mvn package
打包
进入到项目所在目录 输入 mvn clean package
进行打包等待打包完成
显示这个即打包完成
在项目的目录中的 target
包下生成一个java-jar
包
使用java –jar
包的全路径地址
如果报错没有主清单,在pom
文件中新增
<!-- 设置打包主清单属性 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- 启动类 -->
<mainClass>com.zhaoli.App</mainClass>
<excludes>
<exclude>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclude>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
重新打包再运行
启动
成功运行