一、SpringBoot介绍 1.1 spring缺点分析 Spring是一个非常优秀的轻量级框架,以ioc(控制反转)和aop(面向切面)为思想内核,极大简化了JAVA企业级项目的开发。 虽然Spring的组件代码是轻量级的,但它的配置
Spring是一个非常优秀的轻量级框架,以ioc(控制反转)和aop(面向切面)为思想内核,极大简化了JAVA企业级项目的开发。
虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。使用Spring进行项目开发需要在配置文件中写很多代码,所有这些配置都代表了开发时的损耗。
除此之外,Spring项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。比如Spring5.0以上只能使用Junit4.12以上的版本。
Spring的缺点:
- 配置过于繁琐。
- 引入的依赖过多,版本控制复杂。
SpringBoot对Spring的缺点进行改善和优化,基于约定大于配置的思想,简化了Spring的开发,所谓简化是指简化了Spring中大量的配置文件和繁琐的依赖引入,提供了大量的默认配置。所以SpringBoot是一个服务于框架的框架,它不是对Spring功能的增强,而是提供了一种快速使用Spring框架的方式。
SpringBoot的优点:
SpringBoot项目自动提供最优配置,同时可以修改默认值满足特定的要求。
SpringBoot的依赖是基于功能的,而不是普通项目的依赖是基于jar包的。SpringBoot将完成一个功能所需要的所有坐标打包到一起,并完成了版本适配,我们在使用某功能时只需要引入一个依赖即可。
注意:如果是jdk8最好创建的Spring Boot的版本是2.xxxx。
SpringBoot项目必须继承spring-boot-starter-parent,即所有的SpringBoot项目都是spring-boot-starter-parent的子项目。spring-boot-starter-parent中定义了常用配置、依赖、插件等信息,供SpringBoot项目继承使用。
org.springframework.boot spring-boot-starter-parent 3.1.0
SpringBoot项目中可以定义起步依赖,起步依赖不是以jar包为单位,而是以功能为单位。
org.springframework.boot spring-boot-starter-WEB org.springframework.boot spring-boot-starter-test test
spring-boot-Maven-plugin插件是将项目打包成jar包的插件。该插件打包后的SpringBoot项目无需依赖web容器,可以直接使用JDK运行
org.springframework.boot spring-boot-maven-plugin
启动类的作用是启动SpringBoot项目,运行启动类的main方法即可启动SpringBoot项目。在运行web项目的时候可以将项目放到内置的Tomcat中运行,不需要使用外置的tomcat。
package com.zj.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
由于SpringBoot极大简化了Spring配置,所以只有一个application.properties配置文件,且Spring的自动配置功能使得大部分的配置都有默认配置,该文件的功能是覆盖默认配置信息,该文件不写任何信息都可以启动项目。
之前搭建的SpringBoot项目已经都整合了springMVC,我们编写一个控制器进行测试:
@Controllerpublic class MyController { @RequestMapping("/hello") @ResponseBody public String hello(){ System.out.println("hello springboot!"); return "hello springboot!"; }}
启动类在启动时会做注解扫描(@Controller、@Service、@Repository......),扫描位置为同包或者子包下的注解,所以我们要在启动类同级或同级包下编写代码。
按住Ctrl
点击pom.xml
中的spring-boot-starter-parent
,跳转到了spring-boot-starter-parent
的pom.xml
,发现spring-boot-starter-parent
的父工程是spring-boot-dependencies
。
如果在pom文件中ctrl键突然失效了,无法查看源码,那可能是没有下载文档。
2 自动配置
3 核心注解
由于SpringBoot项目没有web.xml文件,所以无法在web.xml中注册web组件,SpringBoot有自己的方式注册web组件。
编写servlet
@WebServlet("/first")public class FirstServlet extends httpservlet { public void doGet(HttpServletRequest request, HttpServletResponse response){ System.out.println("First Servlet........"); }}
启动类扫描web组件
@WebServlet("/first")public class FirstServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response){ System.out.println("First Servlet........"); }}
编写servlet
public class SecondServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response){ System.out.println("Second Servlet........"); }}
使用配置类注册servlet
@Configurationpublic class ServletConfig { //ServletReGIStrationBean可以注册Servlet组件,将其放入Spring容器中即可注册Servlet @Bean public ServletRegistrationBean getServletRegistrationBean(){ // 注册Servlet组件 ServletRegistrationBean bean = new ServletRegistrationBean(new SecondServlet()); // 添加Servlet组件访问路径 bean.addUrlMappings("/second"); return bean; }}
编写filter
//过滤的是first请求@WebFilter(urlPatterns = "/first")public class FirstFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException, IOException { System.out.println("进入First Filter"); filterChain.doFilter(servletRequest,servletResponse); System.out.println("离开First Filter"); } @Override public void destroy() { }}
启动类扫描web组件
@SpringBootApplication//SpringBoot启动时扫描注册@WebServlet、@WebFilter注解标注的Web组件@ServletComponentScanpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
编写filter
@Configurationpublic class FilterConfig { @Bean public FilterRegistrationBean getFilterRegistrationBean(){ // 注册filter组件 FilterRegistrationBean bean = new FilterRegistrationBean(new SecondFilter()); // 添加过滤路径 bean.addUrlPatterns("/second"); return bean; }}
编写Listener
这里选择注册上下文监听器
@WebListenerpublic class FirstListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("First Listener Init......"); } @Override public void contextDestroyed(ServletContextEvent sce) { }}
启动类扫描web组件
@SpringBootApplication//SpringBoot启动时扫描注册@WebServlet、@WebFilter、@WebListener注解标注的Web组件@ServletComponentScanpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
编写Listener
public class SecondListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { System.out.println("Second Listener Init......"); } @Override public void contextDestroyed(ServletContextEvent sce) { }}
使用配置类注册Listener
@Configurationpublic class ListenerConfig { @Bean public ServletListenerRegistrationBean getServletListenerRegistrationBean(){ ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean(new SecondListener()); return bean; }}
SpringBoot项目中没有WebApp目录,只有src目录。在src/main/resources
下面有static
和templates
两个文件夹。SpringBoot默认在static
目录中存放静态资源,而templates
中放动态页面。
SpringBoot通过/resources/static
目录访问静态资源,在resources/static
中编写html页面:
html页面
测试html 我的HTML
目录结构
在SpringBoot中不推荐使用jsP作为动态页面,而是默认使用Thymeleaf编写动态页面。templates目录是存放Thymeleaf页面的目录,稍后。
除了/resources/static
目录,SpringBoot还会扫描以下位置的静态资源:
我们还可以在配置文件自定义静态资源位置,例如在resources目录下创建suibian文件:
在SpringBoot配置文件进行自定义静态资源位置配置:
spring: web: resources: static-locations: classpath:/suibian/,classpath:/static/
注意:
- 该配置会覆盖默认静态资源位置,如果还想使用之前的静态资源位置,还需要配置在后面。
- SpringBoot2.5之前的配置方式为:
spring.resources.static-locations
Thymeleaf是一款用于渲染XML/HTML5内容的模板引擎,类似JSP。它可以轻易的与Springmvc等Web框架进行集成作为Web应用的模板引擎。在SpringBoot中推荐使用Thymeleaf编写动态页面。
Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。
Thymeleaf在有网络和无网络的环境下皆可运行,它即可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。没有数据时,Thymeleaf的模板可以静态地运行;当有数据返回到页面时,Thymeleaf标签会动态地替换掉静态内容,使页面动态显示。
创建springboot项目,并选择添加的依赖。
在template目录编写html页面
thymeleaf入门 默认信息
需要注意的是使用thyme leaf需要在html页面中添加
thymeleaf语法检查比较严格,有些时候写对了还是爆红,我们只需要将语法检查取消就行了:
template中的动态html文件不能直接访问,需要编写Controller跳转到页面中
@Controllerpublic class PageController { // 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(Model model){ model.addAttribute("msg","Hello Thymeleaf"); }}
启动项目,访问http://localhost:8080/index
语法 | 作用 |
---|---|
th:text | 将model中的值作为内容放入标签中(其实就是request域中的数据,session、application域中的数据都行) |
th:value | 将model中的值放入input 标签的value 属性中 |
1、准备模型数据
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(HttpServletRequest model){ model.setAttribute("msg","Hello Thymeleaf"); }
在视图展示model中的值
默认信息
启动项目,访问http://localhost:8080/index
Thymeleaf提供了一些内置对象可以操作数据,内置对象可直接在模板中使用,这些对象是以#引用的,操作字符串的内置对象为strings。
方法 | 说明 |
---|---|
${#strings.isEmpty(key)} | 判断字符串是否为空,如果为空返回true,否则返回false |
${#strings.contains(msg,'T')} | 判断字符串是否包含指定的子串,如果包含返回true,否则返回false |
${#strings.startsWith(msg,'a')} | 判断当前字符串是否以子串开头,如果是返回true,否则返回false |
${#strings.endsWith(msg,'a')} | 判断当前字符串是否以子串结尾,如果是返回true,否则返回false |
${#strings.length(msg)} | 返回字符串的长度 |
${#strings.indexOf(msg,'h')} | 查找子串的位置,并返回该子串的下标,如果没找到则返回-1 |
${#strings.substring(msg,2,5)} | 截取子串,用法与JDK的subString 方法相同 |
${#strings.toUpperCase(msg)} | 字符串转大写 |
${#strings.toLowerCase(msg)} | 字符串转小写 |
html
控制器
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(HttpServletRequest model){ model.setAttribute("msg","Hello Thymeleaf"); }
访问
操作时间的内置对象为dates
方法 | 说明 |
---|---|
${#dates.fORMat(key)} | 格式化日期,默认的以浏览器默认语言为格式化标准 |
${#dates.format(key,'yyyy/MM/dd')} | 按照自定义的格式做日期转换 |
${#dates.year(key)} | 取年 |
${#dates.month(key)} | 取月 |
${#dates.day(key)} | 取日 |
html
控制器
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(Model model){ //Date参数:130表示距离1900后30年也就是2030年 model.addAttribute("date",new Date(130,1,1)); }
访问
语法 | 作用 |
---|---|
th:if | 条件判断 |
html
性别:女 性别:男
控制器
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(Model model){ model.addAttribute("sex","女"); }
访问
语法 | 作用 |
---|---|
th:switch/th:case | th:switch/th:case与Java中的switch语句等效。th:case="*" 表示Java中switch的default,即没有case的值为true时显示th:case="*" 的内容。 |
1、html
ID为1 ID为2 ID为3 ID为*
控制器
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(Model model){ model.addAttribute("id","12"); }
访问
语法 | 作用 |
---|---|
th:each | 迭代器,用于循环迭代集合 |
pojo
public class User { private String id; private String name; private int age; //get/set/构造略}
2、控制器
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(Model model){ List users = new ArrayList<>(); users.add(new User("1","谷梦琪",23)); users.add(new User("2","邴梓童",22)); users.add(new User("3","孔新月",25)); model.addAttribute("users",users); }
3、html
ID Name Age
访问
thymeleaf将遍历的状态变量封装到一个对象中,通过该对象的属性可以获取状态变量:
状态变量 | 含义 |
---|---|
index | 当前迭代器的索引,从0开始 |
count | 当前迭代对象的计数,从1开始 |
size | 被迭代对象的长度 |
odd/even | 布尔值,当前循环是否是偶数/奇数,从0开始 |
first | 布尔值,当前循环的是否是第一条,如果是返回true,否则返回false |
last | 布尔值,当前循环的是否是最后一条,如果是则返回true,否则返回false |
html
控制器
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(Model model){ List users = new ArrayList<>(); users.add(new User("1","谷梦琪",23)); users.add(new User("2","邴梓童",22)); users.add(new User("3","孔新月",25)); model.addAttribute("users",users); }
访问
控制器
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(Model model){ Map map = new HashMap<>(); map.put("user1",new User("1","谷梦琪",23)); map.put("user2",new User("2","邴梓童",22)); map.put("user3",new User("3","孔新月",25)); model.addAttribute("map",map); }
html
ID Name Age Key
访问
thymeleaf也可以获取request,session,application域中的数据,方法如下:
控制器
// 页面跳转,无返回值时默认跳转到和访问路径相同的页面。 @GetMapping("/index") public void showPage(HttpServletRequest request, HttpSession session){ request.setAttribute("req","HttpServletRequest"); session.setAttribute("ses","HttpSession"); session.getServletContext().setAttribute("app","application"); }
html
访问
在Thymeleaf中路径的写法为@{路径}
百度
控制器
//访问index.html @RequestMapping("/index") public void index(){ } // 返回JSON格式的数据 @GetMapping("/show") @ResponseBody public String showPage(String id,String name){ return id + "-" + name; }
html
静态传参1静态传参2
控制器
//访问index.html @RequestMapping("/index") public void index(Model model){ model.addAttribute("id",1); model.addAttribute("name", "张三 "); } // 返回json格式的数据 @GetMapping("/show") @ResponseBody public String showPage(String id,String name){ return id + "-" + name; }
html
动态传参1动态传参2
控制器
//访问index.html @RequestMapping("/index") public void index(Model model){ model.addAttribute("id",1); model.addAttribute("name", "张三 "); } // restful风格 @GetMapping("/show/{id}/{name}") @ResponseBody public String showPage(@PathVariable String id, @PathVariable String name){ return id + "-" + name; }
html
restful风格传参
在Springboot配置文件中可以进行Thymeleaf相关配置,但是一般不需要配置,使用默认的就行
配置项 | 含义 |
---|---|
spring.thymeleaf.prefix | 视图前缀 |
spring.thymeleaf.suffix | 视图后缀 |
spring.thymeleaf.encoding | 编码格式 |
spring.thymeleaf.servlet.content-type | 响应类型 |
spring.thymeleaf.cache=false | 页面缓存,配置为false则不启用页面缓存,方便测试 |
spring: thymeleaf: prefix: classpath:/templates/ suffix: .html encoding: UTF-8 cache: false servlet: content-type: text/html
热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。即修改完代码后不需要重启项目即可生效。在SpringBoot中,可以使用DevTools工具实现热部署
添加DevTools依赖
org.springframework.boot spring-boot-devtools true
在idea中设置自动编译
点击File-->Settings
在Idea设置自动运行
快捷键Ctrl+Shift+Alt+/
后点击Registry
,勾选complier.automake.allow.when.app.running
Spring整合MyBatis时需要进行大量配置,而SpringBoot整合MyBatis则可以简化很多配置:
准备数据库
创建SpringBoot项目,添加MyBatis起步依赖和Mysql驱动依赖
编写实体类
public class User { private int id; private String username; private String sex; private String address; private int account;}
编写Mapper接口
@Mapperpublic interface UserMapper { List findUsers();}
在resources目录下编写mapper映射文件(接口和映射文件所在的文件的路径保持一致,并且映射文件所在的目录文件要一级一级建)
select * from user;
编写配置文件
#数据源spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc://mysql:///mybatis?serverTimezone=UTC username: root passWord: 123456mybatis: #Mybatis配置映射文件的位置 mapper-locations: com/zj/mapper/*Mapper.xml #实体类起别名 type-aliases-package: com.zj.pojo#日志格式logging: pattern: console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n'
编写测试类
package com.zj.mapper;import com.zj.mapper.UserMapper;import com.zj.pojo.User;import org.junit.jupiter.api.Test;import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;import java.util.List;@SpringBootTest //springboot测试类注解,在运行测试代码时加载容器public class UserMapperTest { @Resource private UserMapper userMapper; @Test public void testUsers(){ List users = userMapper.findUsers(); for (User user : users) { System.out.println(user); } }}
SpringBoot自带了validation工具可以从后端对前端传来的参数进行校验,用法如下:
引入validation
起步依赖,或者在创建项目的时候引入该模块。
控制器
@Controller@Validated //该控制器开启参数校验public class TestController { @RequestMapping("/test1") @ResponseBody //在参数校验,表示该参数不为空。当参数为空时会报异常 public String test1(@NotBlank(message = "用户名不能为空") String name){ return "test1:"+name; }}
当抛出ConstraintViolationException
异常后,我们可以使用SpringMVC的异常处理器,也可以使用SpringBoot自带的异常处理机制。
当程序出现了异常,SpringBoot会使用自带的BasicErrorController
对象处理异常。该处理器会默认跳转到/resources/templates/error.html页面()。
引入依赖,在参数校验的基础上还需要thymeleaf依赖。
2、编写错误页面
错误页面 只要出现异常都会跳转到该页面服务器开小差了!
控制器
@Controller@Validated //该控制器开启参数校验public class TestController { @RequestMapping("/test1") @ResponseBody //在参数校验,表示该参数不为空。当参数为空时会报异常 public String test1(@NotBlank(message = "用户名不能为空") String name){ return "test1:"+name; }}
访问
注解 | 作用 |
---|---|
@NotNull | 判断包装类是否为null |
@NotBlank | 判断字符串是否为null或者是空串(去掉首尾空格) |
@NotEmpty | 判断集合是否为空(在判断泛型是基本数据类型的时候还需要加@RequestParam) |
@Length | 判断字符的长度(最大或者最小) |
@Min | 判断数值最小值 |
@Max | 判断数值最大值 |
判断邮箱是否合法 |
1、控制器
package com.zj.controller;import org.hibernate.validator.constraints.Length;import org.springframework.stereotype.Controller;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import javax.validation.constraints.*;import java.util.List;@Controller@Validated //该控制器开启参数校验public class TestController { @RequestMapping("/test1") @ResponseBody //在参数校验,表示该参数不为空。当参数为空时会报异常 public String test1(@NotBlank(message = "用户名不能为空") @Length(min = 1,max = 5) String name, @NotNull @Min(1) @Max(100) Integer age, @NotEmpty @RequestParam List address, @NotBlank @Email String email){ return "请求成功"; }}
访问
SpringBoot也可以校验对象参数中的每个属性,用法如下:
1、添加实体类
public class Student { @NotNull(message = "id不能为空") private Integer id; @NotBlank(message = "姓名不能为空") private String name; // 省略getter/setter/tostring}
控制器
@RequestMapping("/test2") @ResponseBody // 校验的对象参数前添加@Validated,并将异常信息封装到BindingResult对象中 public String t3(@Validated Student student, BindingResult result) { // 判断是否有参数异常 if (result.hasErrors()) { // 所有参数异常 List list = result.getAllErrors(); // 遍历参数异常,输出异常信息 for (ObjectError err : list) { FieldError fieldError = (FieldError) err; System.out.println(fieldError.getDefaultMessage()); } return "参数异常"; } System.out.println(student); return "请求成功!"; }
访问
Spring Boot Actuator可以帮助程序员监控和管理SpringBoot应用,比如健康检查、内存使用情况统计、线程使用情况统计等。我们在SpringBoot项目中添加Actuator功能,即可使用Actuator监控项目,用法如下:
在被监控的项目中添加Actuator起步依赖
org.springframework.boot spring-boot-starter-actuator
编写配置文件
#开启所有监控端点management.endpoints.web.exposure.include=*
访问项目:http://localhost:8080/actuator
通过URL可以调用actuator的功能:
URL | 查看的数据 |
---|---|
/env | 环境属性 |
/health | 健康检查 |
/mappings | 显示所有的@RequestMapping路径 |
/loggers | 日志 |
/info | 定制信息 |
/metrics | 查看内存、CPU核心等系统参数 |
/trace | 用户请求信息 |
例如查询健康数据,访问http://localhost:8080/actuator/health
Actuator使用JSON格式展示了大量指标数据,不利于我们查看,我们可以使用可视化工具Spring Boot Admin查看actuator生成指标数据。Spring Boot Admin是一个独立的项目,我们需要创建并运行该项目。
创建SpringBoot项目,添加SpringMVC和Spring Boot Admin服务端起步依赖
2、修改配置文件
# 端口号server.port=8081#日志格式logging.pattern.console=%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n
修改启动类
@SpringBootApplication@EnableAdminServer //开启Spring Boot Admin服务端public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
在被监控的项目中连接Spring Boot Admin项目,才能使用Spring Boot Admin查看指标数据。
被监控项目添加Spring Boot Admin客户端起步依赖
de.codecentric spring-boot-admin-starter-client 2.6.0
修改配置文件
#连接服务端spring.boot.admin.client.url=http://localhost:8081
此时Spring Boot Admin即可连接被监控的项目
SpringBoot默认使用Logback组件作为日志管理。Logback是log4j创始人设计的一个开源日志组件。在SpringBoot项目中我们不需要额外的添加Logback的依赖,因为在spring-boot-parent
中已经包含了Logback的依赖。
1、在/resources
下添加Logback配置文件logback.xml
%d{MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n ${LOG_HOME}/server.%d{yy99-MM-dd}.log 30 %d{HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 10MB
注:Logback配置文件名为logback-test.xml或logback.xml,如果classpath下没有这两个文件,LogBack会自动进行最小化配置。
在代码中打印日志
package com.zj.controller;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controllerpublic class LoGController { private final static Logger logger = LoggerFactory.getLogger(LogController.class); @RequestMapping("/log") @ResponseBody public String showLogMessage(){ //每次访问在控制台、日志文件中输出日志 logger.info("记录日志:logMessage方法执行"); return "hello Logback"; }}
访问
如果日志过多,可以屏蔽一些包的日志,在配置文件中配置
#屏蔽org包中的日志输出
logging.level.org=off
补充:Log4j2安全漏洞
在2021年12月,Log4j2爆出了极其严重的安全漏洞,攻击者可以让记录的日志包含指定字符串,从而执行任意程序。很多大型网站,如百度等都是此次Log4j漏洞的受害者,很多互联网企业连夜做了应急措施。
Log4j2.0到2.14.1全部存在此漏洞,危害范围极其广泛,Log4j2.15.0-rc1中修复了这个 bug。
因Log4j2漏洞的反复无常,导致某些公司已经切换到Logback来记录日志,但在Log4j2漏洞爆出后,Logback也爆出漏洞:在Logback1.2.7及之前的版本中,具有编辑配置文件权限的攻击者可以制作恶意配置,允许从LDAP服务器加载、执行任意代码。
解决方案为将Logback升级到安全版本:
Logback1.2.9+
SpringBoot2.6.2以上的Logback版本已经升到了1.2.9,Log4j2的版本也升到了2.17.0,所以我们使用SpringBoot2.6.2以上版本无需担心Log4j2和Logback安全漏洞。
SpringBoot项目是依赖于Maven构建的,但打包时如果只依赖Maven打包工具则会打包不完整,我们还需要在SpringBoot项目中引入SpringBoot打包插件 ,有些版本是自动带着该插件的。
org.springframework.boot spring-boot-maven-plugin
如果加上这个插件仍无法打包的话再加上下面的这个插件:
org.apache.maven.plugins maven-resources-plugin 3.1.0
打包好的jar在target目录下:
运行jar包:
进入jar包所在目录,使用cmd打开命令行窗口
输入命令:java -jar jar包名
在真实开发中,在不同环境下运行项目往往会进行不同的配置,比如开发环境使用的是开发数据库,测试环境使用的是测试数据库,生产环境使用的是生产数据库。SpringBoot支持不同环境下使用不同的配置文件,用法如下:
配置文件名:
application-环境名.properties/yml
application-dev.properties/yml 开发环境配置文件
# 开发环境端口号为8080server: port: 8080
application-test.properties/yml 测试环境配置文件
# 测试环境端口号为8081server: port: 8081
application-prod.properties/yml 生产环境配置文件
# 生产环境端口号为80server: port: 80
运行jar包时选择环境:
java -jar jar包名 --spring.profiles.active=环境名
为了节约资源,在生产环境中我们更多的是使用Docker容器部署SpringBoot应用,首先我们准备Docker环境:
# 关闭运行的防火墙systemctl stop firewalld.service# 禁止防火墙自启动systemctl disable firewalld.service
由于在SpringBoot中嵌入了Web容器,所以在制作SpringBoot项目的镜像时无需依赖Web容器,基于JDK制作镜像即可,接下来我们使用Dockerfile制作镜像:
cd /opt
使用rz前提是下载lrsz,yum -y install lrzsz
# 基于JDK11FROM openjdk:8# 作者MAINTAINER zj# 拷贝到容器opt目录ADD sb_logback.jar /opt#保留端口EXPOSE 8080# 启动容器后执行的命令CMD java -jar /opt/sb_logback.jar
docker build -t sb_logback .
docker run -d -p 8080:8080 sb_logback
除了DockerFile,我们还可以使用Maven插件制作镜像。使用方法如下:
# 修改docker配置文件vim /lib/systemd/system/docker.service# 在ExecStart=后添加配置,远程访问docker的端口为2375ExecStart=/usr/bin/dockerd-current -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock \ --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current \ --default-runtime=docker-runc \ --exec-opt native.cgroupdriver=systemd \ --userland-proxy-path=/usr/libexec/docker/docker-proxy-current \ --init-path=/usr/libexec/docker/docker-init-current \ --seccomp-profile=/etc/docker/seccomp.json \ $OPTIONS \ $DOCKER_STORAGE_OPTIONS \ $DOCKER_NETWORK_OPTIONS \ $ADD_REGISTRY \ $BLOCK_REGISTRY \ $INSECURE_REGISTRY \ $REGISTRIES# 重启dockersystemctl daemon-reloadsystemctl restart docker
docker-maven-plugin
插件 com.spotify docker-maven-plugin 1.2.2 http://192.168.25.101:2375 openjdk:8 zj /opt ${project.build.directory} ${project.build.finalName}.jar / ["java", "-jar", "${project.build.finalName}.jar","--spring.profiles.active=dev"] true ${project.artifactId} ${project.version}
定时任务即系统在特定时间执行一段代码,它的场景应用非常广泛:
购买游戏的月卡会员后,系统每天给会员发放游戏资源。
管理系统定时生成报表。
定时清理系统垃圾。
......
定时任务的实现主要有以下几种方式:
创建SpringBoot项目,在启动类开启定时任务。
@SpringBootApplication@EnableScheduling //开启定时任务public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
编写定时任务类
@Componentpublic class MyTask { //定时方法,每秒实行一次 @Scheduled(cron="* * * * * *") public void task1(){ //当前时间 System.out.println("当前时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); }}
运行
Spring Task依靠Cron表达式配置定时规则。Cron表达式是一个字符串,分为6或7个域,每一个域代表一个含义,以空格隔开。有如下两种语法格式:
Seconds
Minutes
Hours
DayofMonth
Month
DayofWeek
Year
Seconds
Minutes
Hours
DayofMonth
Month
DayofWeek
Seconds(秒):域中可出现 ,
-
*
/
四个字符,以及0-59的整数
*
:表示匹配该域的任意值,在Seconds域使用*
,表示每秒钟都会触发,
:表示列出枚举值。在Seconds域使用5,20
,表示在5秒和20秒各触发一次。
@Scheduled(cron="5,15,30,40 * * * * *") //每分钟的第5,15,30,40秒执行一次
-
:表示范围。在Seconds域使用5-20
,表示从5秒到20秒每秒触发一次
@Scheduled(cron="5-20 * * * * *") //定时方法,每分钟的第5到20秒执行
/
:表示起始时间开始触发,然后每隔固定时间触发一次。在Seconds域使用5/20
, 表示5秒触发一次,25秒,45秒分别触发一次。
@Scheduled(cron="5/10 * * * * *")//定时方法,在当前分钟的第5开始执行,每间隔10秒执行一次
Minutes(分):域中可出现
,
-
*
/
四个字符,以及0-59的整数Hours(时):域中可出现
,
-
*
/
四个字符,以及0-23的整数
DayofMonth(日期):域中可出现
,
-
*
/
?
L
W
C
八个字符,以及1-31的整数
C
:表示和当前日期相关联。在DayofMonth域使用5C
,表示在5日后的那一天触发,且每月的那天都会触发。比如当前是10号,那么每月15号都会触发。
@Scheduled(cron="0 0 0 5c * * *")//今天是1号,那么本月的6号0点和以后每月的6号0点都执行
L
:表示最后,在DayofMonth域使用L
,表示每个月的最后一天触发。
@Scheduled(cron="0 0 0 L * * *")//本月的最后一天0点执行
W
:表示工作日,在DayofMonth域用15W
,表示最接近这个月第15天的工作日触发,如果15号是周六,则在14号即周五触发;如果15号是周日,则在16号即周一触发;如果15号是周二则在当天触发。注:
- 该用法只会在当前月计算,不会到下月触发。比如在DayofMonth域用
31W
,31号是周日,那么会在29号触发而不是下月1号。- 在DayofMonth域用
LW
,表示这个月的最后一个工作日触发。
Month(月份):域中可出现
,
-
*
/
四个字符,以及1-12的整数或JAN-DEC的单词缩写
@Scheduled(cron="0 0 0 * 6-8 * *")//6-8月每天晚上的0点执行
DayofWeek(星期):可出现
,
-
*
/
?
L
#
C
八个字符,以及1-7的整数或SUN-SAT 单词缩写,1代表星期天,7代表星期六
C
:在DayofWeek域使用2C
,表示在2日后的那一天触发,且每周的那天都会触发。比如当前是周一,那么每周三都会触发。L
:在DayofWeek域使用L
,表示在一周的最后一天即星期六触发。在DayofWeek域使用5L
,表示在一个月的最后一个星期四触发。#
:用来指定具体的周数,#
前面代表星期几,#
后面代表一个月的第几周,比如5#3
表示一个月第三周的星期四。?
:在无法确定是具体哪一天时使用,用于DayofMonth和DayofWeek域。例如在每月的20日零点触发1次,此时无法确定20日是星期几,写法如下:0 0 0 20 * ?
;或者在每月的最后一个周日触发,此时无法确定该日期是几号,写法如下:0 0 0 ? * 1L
Year(年份):域中可出现
,
-
*
/
四个字符,以及1970~2099的整数。该域可以省略,表示每年都触发。
含义 | 表达式 |
---|---|
每隔5分钟触发一次 | 0 0/5 * * * * |
每小时触发一次 | 0 0 * * * * |
每天的7点30分触发 | 0 30 7 * * * |
周一到周五的早上6点30分触发 | 0 30 7 ? * 2-6 |
每月最后一天早上10点触发 | 0 0 10 L * ? |
每月最后一个工作日的18点30分触发 | 0 30 18 LW * ? |
2030年8月每个星期六和星期日早上10点触发 | 0 0 10 ? 8 1,7 2030 |
每天10点、12点、14点触发 | 0 0 10,12,14 * * * |
朝九晚五工作时间内每半小时触发一次 | 0 0 0/30 9-17 ? * 2-6 |
每周三中午12点触发一次 | 0 0 12 ? * 4 |
每天12点触发一次 | 0 0 12 * * * |
每天14点到14:59每分钟触发一次 | 0 * 14 * * * |
每天14点到14:59每5分钟触发一次 | 0 0/5 14 * * * |
每天14点到14:05每分钟触发一次 | 0 0-5 14 * * * |
每月15日上午10:15触发 | 0 15 10 15 * ? |
每月最后一天的上午10:15触发 | 0 15 10 L * ? |
每月的第三个星期五上午10:15触发 | 0 15 10 ? * 6#3 |
@Scheduled写在方法上方,指定该方法定时执行。常用参数如下:
cron:cron表达式,定义方法执行的时间规则。
fixedDelay:任务立即执行,之后每隔多久执行一次,单位是毫秒,上一次任务结束后计算下次执行的时间。
// 立即执行,任务结束后每5秒执行一次@Scheduled(fixedDelay=5000)public void task1() throws InterruptedException { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); Thread.sleep(1000); System.out.println(sdf.format(new Date()));}
// 立即执行,之后每5秒执行一次@Scheduled(fixedRate=5000)public void task2() throws InterruptedException { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); Thread.sleep(1000); System.out.println(sdf.format(new Date()));}
// 项目启动3秒后执行,之后每5秒执行一次。@Scheduled(fixedRate=5000,initialDelay = 3000)public void task3() throws InterruptedException { SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); Thread.sleep(1000); System.out.println(sdf.format(new Date()));}
Spring Task定时器默认是单线程的,如果项目中使用多个定时器,使用一个线程会造成效率低下。代码如下:
@Scheduled(cron="* * * * * *")private void task1() throws InterruptedException { System.out.println(Thread.currentThread().getId()+"线程执行任务1"); Thread.sleep(5000);}@Scheduled(cron="* * * * * *")private void task2() { System.out.println(Thread.currentThread().getId()+"线程执行任务2");}
任务1较浪费时间,会阻塞任务2的运行。此时我们可以给Spring Task配置线程池。
@Configurationpublic class SchedulingConfig implements SchedulingConfigurer { public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { // 创建线程池 taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5)); }}
此时任务1不会阻塞任务2的运行。
来源地址:https://blog.csdn.net/weixin_56349691/article/details/131056509
--结束END--
本文标题: SpringBoot与MybatisPlus SpringBoot
本文链接: https://www.lsjlt.com/news/433878.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0