SpringBoot学习笔记
1、通过Maven创建
pom文件中加入:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.0.RELEASE</version> <relativePath/> </parent> <properties> <java.version>1.8</java.version> </properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2、IDEA的脚手架创建(推荐)
SpringBoot的配置文件
1、application.properties格式
2、application.yml格式(层次清楚,推荐使用,但是缩进较为严格)
配置文件存放位置:
1、当前项目根目录中
2、当前项目根目录下的一个/config子目录中
3、项目resources根路径中
4、项目resources根路径下的/config子文件夹中
配置文件中的占位符:
1、语法:${}
2、占位符作用以及生成随机数:
BootStrap配置文件:
SpringBoot的HelloWorld:
@RestController //等于@Controller+@Responsebody public class HelloController { @RequestMapping("/hello") public String show() { return "helloworld"; } }
SpringBoot在Controller层中的常用注解:
1、@RestController:相当于@Controller+@ResponseBody注解的结合,使用后Controller无法返回页面,返回的就是return中的内容
2、@GetMapping:就是@RequestMapping(method=RequestMethod.GET)的缩写,@PostMapping、@PutMapping、@DeleteMapping效果类似
SpringBoot整合WEB层技术:
1、整合Servlet
方式一:
1.1通过注解扫描
1.1.1创建一个servlet
package comzhaojianhui.cn.springbootdemo.servlet; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "first",urlPatterns = "/first") public class MyServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response){ System.out.println("do GET"); } }
1.1.2修改启动类:启动类上加上@ServletComponentScan注解
整合filter:
1、创建一个filter
package comzhaojianhui.cn.springbootdemo.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter(filterName = "firstfilter",urlPatterns = "/firstfilter") public class FirstFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("进入first filter"); filterChain.doFilter(servletRequest, servletResponse); System.out.println("离开first filter"); } }
2、启动类加上@ServletComponentScan注解
整合Listener
1、创建一个listener
package comzhaojianhui.cn.springbootdemo.listener; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class FirstListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { } @Override public void contextDestroyed(ServletContextEvent sce) { } }
2、启动类加上@ServletComponentScan注解
SpringBoot访问静态资源:static目录存放静态资源(例如css/html/js/jquery),templates目录存放Thymeleaf模板页面
如果Controller要实现static目录中html页面的视图跳转,不需要加上static目录,示例如下:
package comzhaojianhui.cn.springbootdemo.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class IndexController { @RequestMapping("/page") public String hello() { return "index.html";//直接返回要跳转到的页面即可,页面包含.html } }
配置文件中加上:
server: port: 8888 spring: thymeleaf: prefix: classpath:/static/ suffix: .html
热部署:
1、加入jar包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>
2、打开顶部工具栏 File -> Settings -> Default Settings -> Build -> Compiler 然后勾选 Build project automatically
3、同时按住 Ctrl + Shift + Alt + / 然后进入Registry ,勾选自动编译并调整延时参数
4、
静态资源存放的其他位置(classpath指的就是resources):
自定义静态资源位置:
SpringBoot的文件上传:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>文件上传</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit" value="上传"/> </form> </body> </html>
package comzhaojianhui.cn.springbootdemo.controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; @RestController public class FileLoad { /** * 文件上传 */ @PostMapping("/upload") public String fileup(MultipartFile file) throws IOException { System.out.println(file.getOriginalFilename()); file.transferTo(new File("F:/" + file.getOriginalFilename())); return "ok"; } }
server: port: 8888 spring: thymeleaf: prefix: classpath:/static/ suffix: .html #配置单个文件大小限制 servlet: multipart: max-file-size: 50MB #一次请求中上传文件总容量大小 max-request-size: 50MB datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatisstudy?serverTimezone=UTC&characterEncoding=utf8&useSSL=false username: root password: 1314520 type: com.alibaba.druid.pool.DruidDataSource
SpringBoot整合freemarker:
1、添加pom
<!--整合freemarker--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
SpringBoot整合Mybatis:
1、添加pom:
<!--Mybatis启动器--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> <!--数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> <!--druid连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency>
2、配置数据源
server: port: 8080 spring: thymeleaf: prefix: classpath:/templates/ suffix: .html cache: false mode: HTML servlet: multipart: max-request-size: 50MB max-file-size: 20MB datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatisstudy?serverTimezone=UTC&characterEncoding=utf8&useSSL=false username: root password: 1314520 type: com.alibaba.druid.pool.DruidDataSource mybatis: mapper-locations: classpath:/mapper/*.xml
3、pom中配置generator插件:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!--配置generator插件--> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.5</version> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency> </dependencies> <!--指定配置文件路径--> <configuration> <configurationFile>${project.basedir}/src/main/resources/mbg.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build>
配置完成后双击下图中的选项即可生成:
配置资源拷贝插件:
<!--配置资源拷贝插件--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.yml</include> <include>**/*.properties</include> </includes> </resource> </resources>
修改启动类添加@MapperScan注解完成mapper接口和映射文件的扫描,示例如下:
当mapper.xml映射配置文件放在resources目录下的mapper文件夹时,需要使用
mybatis: mapper-locations: classpath:/mapper/*.xml
resultType起别名:
异常处理
1、自定义错误页面:如果我们需要将所有的异常统一跳到自定义的错误页面,需要在resources的templates目录下创建error.html,只能叫error,不能换名字!
2、通过@ExceptionHandler处理异常
3、通过@ControllerAdvice和@ExceptionHandler定义异常类处理
4、通过@SimpleMappingExceptionResolver处理,示例如下(错误页面存放与templates目录下):只能传递异常页面,无法传递异常信息
package comzhaojianhui.cn.springbootdemo.Exception; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import java.util.Properties; @Configuration public class GlobalEx2 { @Bean public SimpleMappingExceptionResolver get() { SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties prop = new Properties(); /** * 参数一:异常类型且是全名 * 参数二:视图名 */ prop.put("java.lang.NullPointerException", "error2"); prop.put("java.lang.ArithmeticException", "error3"); resolver.setExceptionMappings(prop); return resolver; } }
5、自定义HadlerExceptionResolver处理对象处理:可以传递异常页面和信息
package comzhaojianhui.cn.springbootdemo.Exception; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Configuration public class Global3 implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { ModelAndView mv = new ModelAndView(); //判断异常类型进行视图跳转 if (e instanceof NullPointerException) { mv.setViewName("error4"); } if (e instanceof ArithmeticException) { mv.setViewName("error5"); } mv.addObject("error", e.toString()); return mv; } }
SpringBoot整合junit单元测试示例:
SpringBoot服务端数据校验:
1、对实体对象的校验
NotNull:多用于对Integer校验
NotBlank:对字符串做非空校验
NotEmpty:对集合类型做非空校验
2、在controller中开启校验规则
package comzhaojianhui.cn.springbootdemo.controller; import comzhaojianhui.cn.springbootdemo.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; @Controller @RequestMapping("/user") public class UserController { @RequestMapping("/adduser") public String add(@Validated User user, BindingResult result) { if (result.hasErrors()) { /* List<ObjectError> list = result.getAllErrors(); for (Object err : list) { FieldError fieldError = (FieldError) err; String fieldName = fieldError.getField(); String msg = fieldError.getDefaultMessage(); System.out.println(fieldName + " " + msg); }*/ return "addUser"; } System.out.println(user); return "ok"; } }
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form th:action="@{/user/adduser}" method="post"> <input type="text" name="name"/><span th:errors="${user.name}"/><br/> <input type="text" name="id"/><span th:errors="${user.id}"/> </form> </body> </html>
自定义错误提示信息:
1、注解中定义错误信息
public class User { @NotBlank(message = "名字不能为空") private String name; @NotNull(message = "id不能为空") private Integer id; }
2、配置文件中定义提示信息,配置文件名必须是ValidationMessages.properties
userid.notnull=用户ID不能为空1122 username.notnull=用户姓名不能为空11
package comzhaojianhui.cn.springbootdemo.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; @Data @AllArgsConstructor @NoArgsConstructor public class User { @NotBlank(message = "{username.notnull}") private String name; @NotNull(message = "{userid.notnull}") private Integer id; }
解决页面跳转异常:
package comzhaojianhui.cn.springbootdemo.controller; import comzhaojianhui.cn.springbootdemo.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; /** * 跳转页面的方法 * 解决异常的方式:在跳转页面方法中注入对应pojo对象 */ @Controller public class PageController { @RequestMapping("/{page}") public String showPage(@PathVariable String page, User user) { return page; } }
修改参数key的名称:
其他校验规则:
SpringBoot中对controller中其他参数的校验示例:
全局异常中添加:
SpringBoot的度量指控与健康检查
1、引入pom:
<!--Actuator启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
2、配置文件添加:
使用可视化监控工具:SpringBoot Admin:
1、创建一个基于springboot的服务端项目
2、服务端添加依赖:
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> <version>2.3.0</version> </dependency>
3、修改配置文件:
4、修改启动类
搭建客户端:
1、添加Pom
<!-- https://mvnrepository.com/artifact/de.codecentric/spring-boot-admin-starter-client --> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.3.0</version> </dependency>
2、修改配置文件:
SpringBoot日志管理(默认是logback):
屏蔽指定包的日志输出:
SpringBoot的打包方式:双击install即可
运行:java -jar 文件名
SpringBoot多环境配置:
SpringBoot在Linux下的运行:
SpringBoot开发定时任务
示例:
1、启动类中加注解@EnableScheduling
package comzhaojianhui.cn.springbootdemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling//开启定时任务 public class SpringbootdemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootdemoApplication.class, args); } }
2、编写的方法中添加@Scheduled(cron = "0/3 * * * * ?")注解,括号里写cron表达式,表示几秒钟执行一次(此例中3秒一次),cron表达式参考网站:https://cron.qqe2.com/
package comzhaojianhui.cn.springbootdemo.config; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; import java.util.Date; @Component public class OrderTask { @Scheduled(cron = "0/3 * * * * ?") public void autoCloseOrder() { Date date = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); String nowtime = dateFormat.format(date); System.out.println("执行定时任务,当前时间为:" + nowtime); } }
执行后效果图如下:
定时任务关闭超期未支付订单会存在的弊端:
- 1、会有时差,导致程序不严谨:例如10:39下单,11:00检查不足一小时;12点检查,超过1小时多余39分钟
- 2、不支持集群,单机使用无问题,使用集群后就会有多个定时任务。 解决方案:只用一台计算机节点,单独用来运行所有的定时任务
- 3、会对数据库全表搜索,影响数据库性能:select * from xxx
- 小结:定时任务仅仅适用于小型轻量级项目、传统项目。对于大型项目:可用消息队列如:MQ->RabbitMQ、kafka、ZeroMQ... 延时任务(队列) 例如:10:12下单的,未付款状态,11:12检查,如果状态还是未支付,则直接关闭
任务调度之Quartz:
常用api:
开发步骤:
1、导入quartz的jar包:
<!--开发定时任务--> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz-jobs</artifactId> <version>2.3.0</version> </dependency>
2、入门案例:
package comzhaojianhui.cn.springbootdemo.config; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class QuartzMain { public static void main(String[] args) throws Exception { //1、调度器Scheduler,从工厂中获取调度实例 Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); //2、任务实例JodDetail QuartzJob.class为加载任务类,与QuartzJob类完成绑定 JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity("job1", "group1").build();//参数1:任务的名称(唯一实例) 参数2:任务组的名称 //3、触发器Trigger startNow为马上启动触发器 Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1"). startNow().withSchedule(SimpleScheduleBuilder .simpleSchedule().repeatSecondlyForever(5)).build(); //每5秒重复执行一次 //让调度器关联任务和触发器,保证按照触发器定义的条件执行任务 scheduler.scheduleJob(jobDetail, trigger); //启动调度 scheduler.start(); } }
package comzhaojianhui.cn.springbootdemo.config; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; import java.util.Date; public class QuartzJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { //自定义任务:比如输出当前时间 Date date = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = sf.format(date); //工作内容 System.out.println("执行定时任务,当前时间为:" + dateString); } }
效果图:
Job和JobDetail介绍:
Job:工作任务调度的接口,任务类需要实现该接口。该接口中定义execute方法,在里面编写任务执行的业务逻辑。
Job实例在Quartz中的生命周期:每次调度器执行Job时,它在调用execute方法前会创建一个新Job实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收。
JobDetail:为Job实例提供了许多设置属性,调度器需要借助JobDetail对象来添加Job实例。
JobDetail的重要属性:name、group、jobClass、jobDataMap
常用属性示例:
System.out.println("name:" + jobDetail.getKey().getName()); System.out.println("group:" + jobDetail.getKey().getGroup()); System.out.println("class:" + jobDetail.getKey().getClass());
结果图:
JobExecutionContext介绍:
- 当Scheduler调用一个job,就会将JobExecutionContext传递给Job的execute()方法。
- Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据。
JobDataMap介绍:
1、使用Map获取
- 在进行任务调度时,JobDataMap存储在JobExecutionContext中,非常方便获取
- JobDataMap可以用来装载任何可序列化的数据对象,当Job实例对象被执行时这些参数对象会传递给他。
- JobDataMap实现了JDK的Map接口,并且添加了非常方便的方法用来存取基本数据类型。
示例代码:
有状态的job(任务类上加@PersistJobDataAfterExecution)和无状态的job:
有状态的job可以理解为多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,而默认的无状态的job每次调用时都会创建一个新的JobDataMap
举例: