spriingBoot简介
SpringBoot是由Pivotal团队研发的,SpringBoot并不是一门新技术,只是将之前常用的Spring,SpringMVC,data-jpa等常用的框架封装到了一起,帮助你隐藏这些框架的整合细节,实现敏捷开发。
Spring Boot是基于约定优于配置的,主要作用就是用来简化Spring应用的初始搭建以及开发过程!
后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。
1.2 SpringBoot的特点
1.基于Spring的开发提供更快的入门体验。
2.开箱即用,没有代码生成,也无需XML配置,同时也可以修改默认值来满足特定的需求。
3.提供了一些大型项目中常见的非功能性特性,外部配置等。
4.SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。
5.SpringBoot中整合第三方框架时,只需要导入相应的starter依赖包,就自动整合了
6.SpringBoot默认只有一个.properties的配置文件,不推荐使用xml,后期会采用.yml的文件去编写配置信息。
7.SpringBoot工程在部署时,采用的是jar包的方式,内部自动依赖Tomcat容器,提供了多环境的配置。
8.后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。
1.3 SpringBoot的核心功能
1.起步依赖
起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
2.自动配置
Spring Boot的自动配置是一个运行时(更准确地说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不该用哪个。该过程是Spring自动完成的。
二、SpringBoot快速入门
网站创建地址:https://start.spring.io/
项目创建完成! 此时pom.xml文件中会自动导入springboot所需依赖,并且在src下会生成一个配置类。 注意:若pom.xml中依赖无法下载,需要修改maven工程对应的settings.xml文件,找到settings.xml文件中的镜像配置,原因是maven中央仓库下载不下来springboot关联的架包,所以建议使用阿里云的镜像. <mirrors> <!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | --> <mirror> <id>nexus-aliyun</id> <mirrorOf>*</mirrorOf> <name>Nexus-aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </mirror> </mirrors>
运行配置类,看到如下页面,表示启动成功!
手动编写Controller进行进一步测试(注意:需要将controller类,放在启动类的子包中或者同级包下)
package com.qf.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping("/login")
public String login(){
System.out.println("登录");
return "success";
}
}
重新启动配置类,访问:http://localhost:8080/login
三、SpringBoot热部署配置
为了方便开发,可以在创建项目时手动勾选热部署,或导入该依赖,就不需要每次重启配置类
<!--热部署配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
配置自动编译
最后Shift+Ctrl+Alt+/,选择Registry(选完之后再次查看一下是否勾选上)
再次重新运行一次配置类即可!
四、SpringBoot中的默认配置
可以从jar包中找到SpringBoot的默认配置文件位置
SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置,SpringBoot默认会从Resources目录下加载application.properties或application.yml(application.yaml)文件。
其中,application.properties文件是键值对类型的文件,除此之外,SpringBoot还可以使用yml文件进行配置,YML文件格式是YAML (YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。YML文件是以数据为核心的,比传统的xml方式更加简洁,YML文件的扩展名可以使用.yml或者.yaml。
application.properties方式修改默认配置
application.yml方式修改默认配置(注意:yml文件中空格表示层级关系)
yml文件支持的配置
#普通数据的配置 name: jack #对象的配置 user: username: rose password: 123 #配置数组 array: beijing, tianjin, shanghai #配置集合 yangl: test: name: tom arr: 1,jack,2,tom list1: #这种对象形式的,只能单独写一个对象去接收,所以无法使用@value注解获取 - zhangsan - lisi list2: - driver: mysql port: 3306 - driver: oracle port: 1521 map: key1: value1 key2: value2
把yml文件中配置的内容注入到成员变量中,
第一种方式,创建UserController,使用@Value注解方式注入
package com.qf.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class UserController {
@Value("${name}")
private String name;
@Value("${user.username}")
private String username;
@Value("${user.password}")
private String password;
@Value("${array}")
private String [] array;
@RequestMapping("/test")
public String[] test(){
System.out.println(name);
System.out.println(username);
System.out.println(password);
System.out.println(array[0]);
return array;
}
}
第二种方式,使用@ConfigurationProperties注解方式,提供GET/SET方法
创建YmlController
package com.qf.controller;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@RestController
@ConfigurationProperties(prefix = "yangl.test")
public class YmlController {
private String name;
private String[] arr;
private List<String> list1;
private List<Map<String,String>> list2;
private Map<String,String> map;
@RequestMapping("/yml")
public void yml(){
System.out.println(name);
System.out.println(Arrays.toString(arr));
System.out.println(list1);
System.out.println(list2);
System.out.println(map);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getArr() {
return arr;
}
public void setArr(String[] arr) {
this.arr = arr;
}
public List<String> getList1() {
return list1;
}
public void setList1(List<String> list1) {
this.list1 = list1;
}
public List<Map<String, String>> getList2() {
return list2;
}
public void setList2(List<Map<String, String>> list2) {
this.list2 = list2;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
如果使用@ConfigurationProperties注解时提示以下信息
导入以下依赖即可(也可以不导入)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
五、SpringBoot整合MyBatis【重点】
5.1 导入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
5.2 在application.properties中配置数据库信息
#配置数据库 spring.datasource.username=root spring.datasource.password=root spring.datasource.url=jdbc:mysql://localhost:3306/wyy_music?serverTimezone=Asia/Shanghai&characterEncoding=utf8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #连接池 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource #mybatis #指定mapper.xml文件的位置 mybatis.mapper-locations=classpath:mapper/*.xml #指定控制台输出日志 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
也可使用yml方式配置,需要创建application.yml
spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/wyy_music?serverTimezone=Asia/Shanghai&characterEncoding=utf8 driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource mybatis: mapper-locations: classpath:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
5.3 使用之前的Music表,创建实体类
Music
package com.qf.pojo;
import lombok.Data;
@Data
public class Music {
private Integer musicId;
private String musicName;
private String musicAlbumName;
private String musicAlbumPicurl;
private String musicMp3url;
private String musicArtistName;
private Integer sheetId;
}
5.4 创建mapper
MusicMapper
package com.qf.mapper;
import com.qf.pojo.Music;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface MusicMapper {
List<Music> findAll();
}
5.5 在src\main\resources\mapper路径下创建对应的MusicMapper.xml文件
MusicMapper.xml
<?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.qf.mapper.MusicMapper"> <resultMap id="musicMap" type="com.qf.pojo.Music"> <id property="musicId" column="music_id"></id> <result property="musicName" column="music_name"></result> <result property="musicAlbumName" column="music_album_name"></result> <result property="musicAlbumPicurl" column="music_album_picUrl"></result> <result property="musicMp3url" column="music_mp3Url"></result> <result property="musicArtistName" column="music_artist_name"></result> <result property="sheetId" column="sheet_id"></result> </resultMap> <sql id="BaseSql"> select music_id,music_name,music_album_name,music_album_picUrl,music_mp3Url,music_artist_name,sheet_id from tb_music </sql> <select id="findAll" resultMap="musicMap"> <include refid="BaseSql"></include> </select> </mapper>
5.6 创建service
MusicService
package com.qf.service;
import com.qf.pojo.Music;
import java.util.List;
public interface MusicService {
List<Music> findAll();
}
5.7 创建serviceImpl
MusicServiceImpl
package com.qf.service.impl;
import com.qf.mapper.MusicMapper;
import com.qf.pojo.Music;
import com.qf.service.MusicService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class MusicServiceImpl implements MusicService {
@Autowired
private MusicMapper musicMapper;
@Override
public List<Music> findAll() {
return musicMapper.findAll();
}
}
5.8创建controller
MusicController
package com.qf.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qf.pojo.Music;
import com.qf.service.MusicService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("music")
public class MusicController {
@Autowired
private MusicService musicService;
@RequestMapping("findAll")
public List<Music> findAll(){
return musicService.findAll();
}
}
5.9启动配置类(注意:需要在启动类上配置@MapperScan并扫描Mapper对应的包名)
package com.qf;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.qf.mapper")//扫描mapper
public class Springboot03Application {
public static void main(String[] args) {
SpringApplication.run(Springboot03Application.class, args);
}
}
启动测试即可!
5.10分页插件的使用
5.10.1导入依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.1</version> </dependency>
5.10.2添加方法
//分页
@RequestMapping("findByPage")
public PageInfo findByPage(
@RequestParam(value = "pageNum",required = false,defaultValue = "1")Integer pageNum,
@RequestParam(value = "pageSize",required = false,defaultValue = "2")Integer pageSize){
PageHelper.startPage(pageNum,pageSize);
List<Music> musicList = musicService.findAll();
PageInfo<Music> musicPageInfo = new PageInfo<>(musicList);
return musicPageInfo;
}
5.10.3在application.properties中添加配置
#配置分页 pagehelper.helper-dialect=mysql pagehelper.reasonable=true pagehelper.support-methods-arguments=true
启动测试即可!
5.11 SpringBoot工程打包部署
双击package执行打包命令,如果打包时报错,需要修改pom.xml文件中的build标签
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
改为:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.5.4.RELEASE</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.4</version> </plugin> </plugins> </build>
然后再次打包,在对应的磁盘目录找到对应的xxx.jar文件
部署:
修改项目中的数据库相关配置,然后启动Linux上的mysql,把xxx.jar文件拷贝到Linux中的任意目录
java -jar xxx.jar Linux上启动springboot工程 java -jar -Dserver.port=1234 xxx.jar Linux上指定端口号启动springboot工程
后台启动:
使用以上命令启动成功后,输入:ctrl + z,输入:bg,输入:exit;
六、SpringBoot中的异常处理
6.1 创建自定义异常类
MyException
package com.qf.exception;
public class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
6.2 在Controller测试异常
在Controller添加方法
//测试异常
@RequestMapping("add")
public String add(){
musicService.add();
return "success";
}
//测试自定义异常
@RequestMapping("delete")
public String deleteUser() throws MyException {
musicService.delete();
return "success";
}
6.3 创建Service接口以及实现类
在Service添加方法
void add(); void delete()throws MyException;
在ServiceImpl添加方法
@Override
public void add() {
System.out.println("add...");
int i = 1/0;
}
@Override
public void delete() throws MyException {
System.out.println("delete...");
throw new MyException("自定义异常");
}
6.4 创建全局异常处理类
MyExceptionHandler
package com.qf.exception;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(Exception.class)
public Map<String,Object> hander1(Exception e, HttpServletRequest request){
System.out.println("hander1...");
Map<String, Object> map = new HashMap<>();
map.put("code",-1);
map.put("msg",e.getMessage());
map.put("url",request.getRequestURL());
return map;
}
@ExceptionHandler(MyException.class)
public Map<String,Object> hander2(MyException e, HttpServletRequest request){
System.out.println("hander2...");
Map<String, Object> map = new HashMap<>();
map.put("code",-1);
map.put("msg",e.getMessage());
map.put("url",request.getRequestURL());
return map;
}
}
访问测试即可!
七、SpringBoot中的过滤器(Listener操作同理)
7.1 创建过滤器
LoginFilter
package com.qf.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("进入LoginFilter之前...");
//放行
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("进入LoginFilter之后...");
}
}
7.2 创建Controller中添加方法测试
package com.qf.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("user")
public class UserController {
//测试过滤器
@RequestMapping("login")
public String testFilter(){
System.out.println("登录");
return "success";
}
}
7.3 在启动类添加@ServletComponentScan注解
package com.qf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@MapperScan("com.qf.mapper")//扫描mapper
@ServletComponentScan//Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册
public class Springboot02Application {
public static void main(String[] args) {
SpringApplication.run(Springboot02Application.class, args);
}
}
访问测试即可!
八、SpringBoot中的拦截器
8.1 创建自定义拦截器
package com.qf.interceptor;
import org.omg.PortableInterceptor.Interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
//进入controller方法之前调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;//true表示放行,false表示不放行
}
//调用完controller之后,视图渲染之前
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
//页面跳转之后,整个流程执行之后,一般用于资源清理操作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
8.2 创建拦截器配置类
package com.qf.interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//设置拦截器并指定拦截路径
//registry.addInterceptor(new MyInterceptor()).addPathPatterns("/user/login");
//registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");//拦截所有
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/login");//指定不拦截
//添加自定义拦截器
WebMvcConfigurer.super.addInterceptors(registry);
}
}
访问对应方法测试即可!
8.3 跨域拦截器配置(补充)
配置后不需要在每一个Controller上面写@CrossOrigin("*")注解了
8.3.1 编写拦截器
package com.qf.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class CorsInterceptor implements HandlerInterceptor{
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
// 此处配置的是允许任意域名跨域请求,可根据需求指定
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "86400");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization");
return true;
}
}
8.3.2 编写拦截器配置类
package com.qf.config;
import com.qf.interceptor.CorsInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private CorsInterceptor corsInterceptor;
/**
* 配置springboot拦截注册
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(corsInterceptor).addPathPatterns("/**");
}
}
九、SpringBoot整合Thymeleaf
9.1导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
9.2在application.properties文件添加配置
########## 配置thymeleaf ########## spring.thymeleaf.cache=false spring.thymeleaf.encoding=utf-8 spring.thymeleaf.prefix=classpath:/templates spring.thymeleaf.suffix=.html spring.thymeleaf.mode=HTML5 spring.thymeleaf.servlet.content-type=text/html
9.3创建实体类
package com.qf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private Date birthday;
}
9.4创建Controller
package com.qf.controller;
import com.qf.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.Date;
@Controller
@RequestMapping("user")
public class UserController {
@RequestMapping("findAll")
public String findAll(Model model){
//模拟数据库查询
ArrayList<User> users = new ArrayList<>();
users.add(new User(1001,"张三",new Date()));
users.add(new User(1002,"李四",new Date()));
users.add(new User(1003,"王五",new Date()));
users.add(new User(1004,"赵六",new Date()));
model.addAttribute("users",users);
return "/list";
}
@RequestMapping("findById")
public String findById(Integer uid,Model model){
System.out.println(uid);
//模拟数据库查询
User db_user = new User(uid,"杰克",new Date());
model.addAttribute("user",db_user);
return "/queryOne";
}
}
9.5在templates目录下创建list.html以及queryOne.html
list.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:if="${users!=null}">
<table border="1" width="600">
<tr th:each="user,state : ${users}">
<td th:text="${state.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
</tr>
</table>
<hr>
<table border="1" width="600">
<!-- 第二个变量,可以获取遍历的元素的状态-->
<tr th:each="user,state : ${users}">
<td th:text="${state.index}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
</tr>
</table>
<hr>
<table border="1" width="600">
<!--如果不设置表示状态的变量,默认遍历的元素的变量名+Stat
表示状态的变量-->
<tr th:each="user : ${users}">
<td th:text="${userStat.count}"></td>
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-
dd HH:mm:ss')}"></td>
</tr>
</table>
</div>
<hr color="red">
<table border="1" width="600">
<tr>
<th>序号</th>
<th>姓名</th>
<th>生日</th>
<th>详情</th>
</tr>
<tr th:each="user : ${users}">
<td th:text="${userStat.count}"></td>
<td th:text="${user.username}"></td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td><a th:href="@{/user/findById(uid=${user.id})}">查询</a></td>
</tr>
</table>
</body>
</html>
queryOne.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:object="${user}" action="http://www.baidu.com">
<!-- *{...}选择表达式一般跟在th:object后,直接选择object中的属性-->
<input type="hidden" th:id="*{id}" name="id">
用户名:<input type="text" th:value="*{username}" name="username" /><br /><br />
生日:<input type="date" th:value="${#dates.format(user.birthday, 'yyyy-MM-dd HH:mm:ss')}" name="birthday"/><br /><br />
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
</body>
</html>
十、SpringBoot整合FreeMarker
10.1导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
10.2在application.properties文件添加配置
#配置freemarker spring.freemarker.charset=UTF-8 spring.freemarker.content-type=text/html #缓存 spring.freemarker.cache=false #加载路径(前缀) spring.freemarker.template-loader-path=classpath:/templates #后缀(不能省略) spring.freemarker.suffix=.ftl
10.3创建实体类
package com.qf.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
private Integer age;
private String address;
}
10.4在templates目录下创建student.ftl文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table border="1" width=600>
<tr>
<th>index</th>
<th>id</th>
<th>name</th>
<th>age</th>
<th>address</th>
</tr>
<#list students as student>
<#if student_index % 2 == 0>
<tr bgcolor="red">
<#else>
<tr bgcolor="yellow">
</#if>
<td>${student_index}</td>
<td>${student.id}</td>
<td>${student.name}</td>
<td>${student.age}</td>
<td>${student.address}</td>
</tr>
</#list>
</table>
</body>
</html>
10.5创建controller
package com.qf.controller;
import com.qf.pojo.Student;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@Controller
@RequestMapping("student")
public class StudentController {
@RequestMapping("findAll")
public String findAll(Model model){
System.out.println("findAll...");
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,"张三",20,"二七区"));
students.add(new Student(2,"李四",21,"金水区"));
students.add(new Student(3,"王五",22,"中原区"));
students.add(new Student(4,"赵六",23,"经开区"));
model.addAttribute("students",students);
return "/student";
}
//网页静态化,提高访问效率
//生成静态页面
@RequestMapping("createHtml")
@ResponseBody
public String createHtml() throws Exception {
//准备数据
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,"张三",20,"二七区"));
students.add(new Student(2,"李四",21,"金水区"));
students.add(new Student(3,"王五",22,"中原区"));
students.add(new Student(4,"赵六",23,"经开区"));
//创建Map
HashMap<String, Object> map = new HashMap<>();
map.put("students",students);
//创建配置类
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
//设置字符集
configuration.setDefaultEncoding("utf-8");
//设置要加载的模版目录
configuration.setDirectoryForTemplateLoading(new File("D:\\java2111\\springboot-03\\src\\main\\resources\\templates"));
//获取某一个模板
Template template = configuration.getTemplate("student.ftl");
//创建输出流
FileWriter fileWriter = new FileWriter("D:\\java2111\\springboot-03\\src\\main\\resources\\static\\student.html");
//创建页面
template.process(map,fileWriter);
//关流
fileWriter.close();
return "success";
}
}
启动工程,先访问 http://localhost:8080/student/createHtml,然后点击IDEA查看static是否生成student.html页面,如果生成后,则可以直接访问 http://localhost:8080/student.html 查看页面
十一、SpringBoot整合Redis
11.1导入依赖
<!-- 配置redis所需要的依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.3</version> </dependency>
11.2创建application.yml文件并添加配置
spring: redis: host: 127.0.0.1 port: 6379 database: 0 lettuce: pool: max-active: 100
11.3创建User类
package com.qf.pojo;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private Integer id;
private String name;
private String password;
}
11.4导入Redis序列化配置类
package com.qf.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig{
/**
* springboot 默认帮我们创建的RedisTemplate的key和value的序列化方式是jdk默认的方式,
* 我们有时候手动向redis中添加的数据可能无法被查询解析出来,所以我们需要修改序列化方式
* @param connectionFactory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringRedisSerializer); //设置key的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);//设置hash类型的数据的key的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//非final类型的数据才会被序列化
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);//设置value的序列化方式为json
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
11.5导入Redis工具类
package com.qf.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
*/
public boolean expire(String key, long time, TimeUnit timeUnit) {
try {
if (time > 0) {
redisTemplate.expire(key, time, timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time, TimeUnit timeUnit) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time, timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time, TimeUnit timeUnit) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time, timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, TimeUnit timeUnit, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time, timeUnit);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time,TimeUnit timeUnit) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time,timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time,TimeUnit timeUnit) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time,timeUnit);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
11.6在项目自带的测试类中测试
package com.qf;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.pojo.User;
import com.qf.utils.RedisUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
@SpringBootTest
class Springboot04RedisApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private RedisUtil redisUtil;
@Test
void contextLoads() {
//对应类型
redisTemplate.opsForValue();//String类型
redisTemplate.opsForList();//List类型
redisTemplate.opsForSet();//Set类型
redisTemplate.opsForZSet();//ZSet类型
redisTemplate.opsForHash();//Hash类型
//redisTemplate测试
// User user = new User();
// user.setId(1001);
// user.setName("张三");
// user.setPassword("123");
//
// redisTemplate.opsForValue().set("user",user);
//
// User db_user = (User)redisTemplate.opsForValue().get("user");
// System.out.println(db_user);
//stringRedisTemplate测试
// User user = new User();
// user.setId(1001);
// user.setName("张三");
// user.setPassword("123");
//
// //转Json
// String jsonUser = null;
// try {
// jsonUser = new ObjectMapper().writeValueAsString(user);
// } catch (JsonProcessingException e) {
// e.printStackTrace();
// }
// //保存数据
// stringRedisTemplate.opsForValue().set("user",jsonUser,30, TimeUnit.SECONDS);
// //获取数据
// String db_jsonUser = stringRedisTemplate.opsForValue().get("user");
// System.out.println(db_jsonUser);
//redisUtil测试
User user = new User();
user.setId(1001);
user.setName("张三");
user.setPassword("123");
redisUtil.set("user",user);
User db_user = (User)redisUtil.get("user");
System.out.println(db_user);
}
}
十二、SpringBoot整合Quartz以及异步方法调用
12.1 异步方法调用
1.创建AsyncController
package com.qf.controller;
import com.qf.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("async")
public class AsyncController {
@Autowired
private AsyncService asyncService;
@RequestMapping("testAsync")
public String testAsync(){
asyncService.testAsync();
return "AsyncController";
}
}
2.创建AsyncService
package com.qf.service;
public interface AsyncService {
void testAsync();
}
3.创建AsyncServiceImpl
package com.qf.service.impl;
import com.qf.service.AsyncService;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncServiceImpl implements AsyncService {
@Async//设置当前方法为异步方法
@Override
public void testAsync() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("AsyncServiceImpl");
}
}
4.在启动上添加开启异步注解,然后测试
package com.qf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync//使用异步方法
public class Springboot05QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot05QuartzApplication.class, args);
}
}
12.2使用@Scheduled注解实现定时任务
1.创建任务类,多个任务执行时,默认使用单线程,可以通过异步方法使其为多线程
package com.qf.task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import java.util.Date;
//任务类
@Configuration
public class Tasks {
private Logger logger = LoggerFactory.getLogger(Tasks.class);
@Async
@Scheduled(cron = "*/2 * * * * ?")
public void task1(){
//System.out.println("task1:"+new Date().toLocaleString());
logger.info("task1:"+new Date().toLocaleString());
}
@Async
@Scheduled(cron = "*/3 * * * * ?")
public void task2(){
//System.out.println("task2:"+new Date().toLocaleString());
logger.info("task2:"+new Date().toLocaleString());
}
}
2.在启动上添加开启任务注解,然后测试
package com.qf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableAsync//使用异步方法
@EnableScheduling//开启任务
public class Springboot05QuartzApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot05QuartzApplication.class, args);
}
}
12.3 定时发送邮件案例
1.导入依赖
<!-- 邮箱 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
2.使用MailUtils工具类进行测试
package com.qf.utils;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
import java.util.Random;
/**
* 发邮件工具类
*/
public final class MailUtils {
private static final String USER = "729953102@qq.com"; // 发件人邮箱地址
private static final String PASSWORD = "kmrmcaazztfjbefj"; // qq邮箱的客户端授权码(需要发短信获取)
/**
* @param to 收件人邮箱地址
* @param text 邮件正文
* @param title 标题
*/
/* 发送验证信息的邮件 */
public static boolean sendMail(String to, String text, String title) {
try {
final Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.host", "smtp.qq.com");
// 发件人的账号
props.put("mail.user", USER);
//发件人的密码
props.put("mail.password", PASSWORD);
// 构建授权信息,用于进行SMTP进行身份验证
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
// 用户名、密码
String userName = props.getProperty("mail.user");
String password = props.getProperty("mail.password");
return new PasswordAuthentication(userName, password);
}
};
// 使用环境属性和授权信息,创建邮件会话
Session mailSession = Session.getInstance(props, authenticator);
// 创建邮件消息
MimeMessage message = new MimeMessage(mailSession);
// 设置发件人
String username = props.getProperty("mail.user");
InternetAddress form = new InternetAddress(username);
message.setFrom(form);
// 设置收件人
InternetAddress toAddress = new InternetAddress(to);
message.setRecipient(Message.RecipientType.TO, toAddress);
// 设置邮件标题
message.setSubject(title);
// 设置邮件的内容体
message.setContent(text, "text/html;charset=UTF-8");
// 发送邮件
Transport.send(message);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
//随机生成num个数字验证码
public static String getValidateCode(int num) {
Random random = new Random();
String validateCode = "";
for (int i = 0; i < num; i++) {
//0 - 9 之间 随机生成 num 次
int result = random.nextInt(10);
validateCode += result;
}
return validateCode;
}
//测试
public static void main(String[] args) throws Exception {
//给指定邮箱发送邮件
MailUtils.sendMail("729953102@qq.com", "你好,这是一封测试邮件,无需回复。", "测试邮件随机生成的验证码是:" + getValidateCode(6));
System.out.println("发送成功");
}
}
3.编写邮箱任务类
package com.qf.task;
import com.qf.utils.MailUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;
@Configuration
public class TaskMail {
//指定时间进行发送邮件
@Scheduled(cron = "10 55 * * * ?")
public void sendMail(){
MailUtils.sendMail("729953102@qq.com", "你好,这是一封测试邮件,无需回复。", "测试邮件随机生成的验证码是:" + MailUtils.getValidateCode(6));
}
}
启动项目测试即可!
12.4整合quartz,首先导入依赖
1.导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
2.编写任务类
package com.qf.quartz.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("MyJob:"+ new Date().toLocaleString());
}
}
3.编写配置类
package com.qf.quartz;
import com.qf.quartz.job.MyJob;
import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
public class QuartzConfig {
//任务
@Bean
public JobDetailFactoryBean getJobDetailFactoryBean(){
JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
//指定任务
jobDetailFactoryBean.setJobClass(MyJob.class);
return jobDetailFactoryBean;
}
//触发器
@Bean
public CronTriggerFactoryBean getCronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){
CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
JobDetail jobDetail = jobDetailFactoryBean.getObject();
//指定任务详情
cronTriggerFactoryBean.setJobDetail(jobDetail);
//指定Cron表达式
cronTriggerFactoryBean.setCronExpression("*/3 * * * * ?");
return cronTriggerFactoryBean;
}
//调度器
@Bean
public SchedulerFactoryBean getSchedulerFactoryBean(CronTriggerFactoryBean cronTriggerFactoryBean){
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
CronTrigger cronTrigger = cronTriggerFactoryBean.getObject();
//指定触发器
schedulerFactoryBean.setTriggers(cronTrigger);
return schedulerFactoryBean;
}
}
4.启动测试类测试即可
十三、SpringBoot日志
SpringBoot默认使用的日志是Logback,官方建议日志文件命名为:logback-spring.xml
13.1在resources目录下创建logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。
默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="true">
<!-- 定义变量,可通过 ${log.path}和${CONSOLE_LOG_PATTERN} 得到变量值 -->
<property name="log.path" value="D:/log" />
<property name="CONSOLE_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} |-[%-5p] in %logger.%M[line-%L] -%m%n"/>
<!-- 输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- Threshold=即最低日志级别,此appender输出大于等于对应级别的日志
(当然还要满足root中定义的最低级别)
-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<!-- 日志格式(引用变量) -->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 追加到文件中 -->
<appender name="file1" class="ch.qos.logback.core.FileAppender">
<file>${log.path}/mylog1.log</file>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 滚动追加到文件中 -->
<appender name="file2" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/mylog2.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录
文件超过最大尺寸后,会新建文件,然后新的日志文件中继续写入
如果日期变更,也会新建文件,然后在新的日志文件中写入当天日志
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 新建文件后,原日志改名为如下 %i=文件序号,从0开始 -->
<fileNamePattern>${log.path}/newlog-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 每个日志文件的最大体量 -->
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1kb</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!-- 日志文件保留天数,1=则只保留昨天的归档日志文件 ,不设置则保留所有日志-->
<maxHistory>1</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="file1"/>
<appender-ref ref="file2"/>
</root>
</configuration>
启动工程,进行测试
十四、SpringBoot整合Swagger
14.1 Swagger介绍
现在开发,很多采用前后端分离的模式,前端只负责调用接口,进行渲染,前端和后端的唯一联系,变成了API接口。因此,API文档变得越来越重要。swagger是一个方便我们更好的编写API文档的框架,而且swagger可以模拟http请求调用。
大部分采取的方式:Vue + SpringBoot,Vue通过js渲染页面,后端把数据传递给js,早期前端只负责写页面,然后把写好的HTML页面给后端,后端使用模板引擎(Jsp,Thymeleaf、 freemarker)进行开发。
前后端分离的好处:各自开发,相对独立,松耦合,前后端通过API进行交互,后端提供接口给前端,前端去调用该接口,但可能会导致前后端团队人员不能做到及时协商,出现一些问题。解决方式:早期使用实时更新文档,但非常繁琐,后来又使用postman来进行一些测试。
swagger是目前流行的Api框架,官网:API Documentation & Design Tools for Teams | Swagger
14.2导入依赖
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency>
14.3创建配置类
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
}
然后启动测试运行,访问:http://localhost:8080/swagger-ui.html,看到如下页面:
14.4手动配置实例,修改SwaggerConfig配置类
package com.qf.swagger.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
//配置Swagger的Bean实例
@Bean
public Docket createDocket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo());
}
//配置API的基本信息(会在http://项目实际地址/swagger-ui.html页面显示)
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("测试API文档标题")
.description("测试api接口文档描述")
.termsOfServiceUrl("http://www.baidu.com")
.version("1.0")
.build();
}
}
再次启动测试运行,访问:http://localhost:8080/swagger-ui.html,看到如下页面:
14.5创建实体类
package com.qf.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel("用户")
public class User {
@ApiModelProperty("编号")
private String uid;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
14.6创建controller
package com.qf.controller;
import com.qf.entity.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
//@Api(description = "用户接口")
@Api(tags = "用户接口")
@RestController
@RequestMapping("/user")
public class UserController {
@ApiOperation("查询单个用户")
@RequestMapping("/findById")
public User findById(@RequestParam @ApiParam("用户ID") String uid){
User user = new User();
user.setUid(uid);
user.setUsername("张三");
user.setPassword("123");
return user;
}
@ApiOperation("删除单个用户")
@PostMapping("/delete")
public User delete(String uid){
User user = new User();
user.setUid(uid);
user.setUsername("李四");
user.setPassword("456");
return user;
}
@ApiOperation("查询所有用户")
@GetMapping("/findAll")
public List<User> findAll(){
User user1 = new User();
user1.setUid("1001");
user1.setUsername("张三");
user1.setPassword("123");
User user2 = new User();
user2.setUid("1002");
user2.setUsername("李四");
user2.setPassword("456");
ArrayList<User> users = new ArrayList<>();
users.add(user1);
users.add(user2);
return users;
}
}
14.7修改SwaggerConfig配置类
package com.qf.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2//开启Swagger2
public class SwaggerConfig {
//配置Swagger的Bean实例
@Bean
public Docket createDocket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(createApiInfo())
.groupName("yangl")////分组名称(可以创建多个Docket就有多个组名)
.enable(true)//enable表示是否开启Swagger
.select()
//RequestHandlerSelectors指定扫描的包
.apis(RequestHandlerSelectors.basePackage("com.qf.controller"))
.build();
}
//配置API的基本信息(会在http://项目实际地址/swagger-ui.html页面显示)
public ApiInfo createApiInfo(){
return new ApiInfoBuilder()
.title("测试标题")
.description("测试描述")
.termsOfServiceUrl("http://www.baidu.com")
.build();
//return ApiInfo.DEFAULT;
}
}
Swagger通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息
@Api:修饰整个类,描述Controller的作用
@ApiOperation:描述一个类的一个方法,或者说一个接口
@ApiModel:用对象来接收参数 ,修饰类
@ApiModelProperty:用对象接收参数时,描述对象的一个字段
@ApiResponse:HTTP响应其中1个描述
@ApiResponses:HTTP响应整体描述,一般描述错误的响应
@ApiIgnore:使用该注解忽略这个API
@ApiError :发生错误返回的信息
@ApiParam:单个参数描述
@ApiImplicitParam:一个请求参数,用在方法上
@ApiImplicitParams:多个请求参数
十五、SpringBoot整合knife4j
15.1 knife4j介绍
官方文档:knife4j
knife4j可以理解swagger的升级版,采用的是后端Java代码和Ui都混合在一个Jar包里面的方式提供给开发者使用,Knife4j不仅仅将前身的Ui皮肤通过Vue技术栈进行了重写,也增加了更多个性化的特性增强功能,基于springfox项目以及OpenAPI的规范,目前主要支持以Java开发为主,并且是依赖于大环境下使用的
Spring MVC、Spring Boot、Spring Cloud框架.
15.2 导入依赖
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency>
15.3创建配置类
package com.qf.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
@Bean(value = "defaultApi2")
public Docket defaultApi2() {
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.apiInfo(new ApiInfoBuilder()
//.title("swagger-bootstrap-ui-demo RESTful APIs")
.description("# swagger-bootstrap-ui-demo RESTful APIs")
.termsOfServiceUrl("http://www.baidu.com/")
.contact(new Contact("张三","www.zhangs.com","zhangs@qq.com"))
.version("1.0")
.build())
//分组名称
.groupName("2.X版本")
.select()
//这里指定Controller扫描包路径
.apis(RequestHandlerSelectors.basePackage("com.qf.controller"))
//.paths(PathSelectors.any())
.build();
return docket;
}
}
15.4 创建controller
package com.qf.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
@Api(tags = "首页模块")
@RestController
public class IndexController {
@ApiImplicitParam(name = "name",value = "姓名",required = true)//参数说明
@ApiOperation(value = "向客人问好")
@GetMapping("/sayHi")
public ResponseEntity<String> sayHi(@RequestParam(value = "name")String name){
return ResponseEntity.ok("Hi:"+name);//ResponseEntity返回对应数据
}
}
本文详细介绍了SpringBoot的核心功能、快速入门、异常处理、整合MyBatis、Thymeleaf、Quartz、邮件服务、日志处理等方面的知识,包括配置、依赖、控制器、服务和模板的使用,以及各种场景下的实践操作,旨在帮助读者深入理解和应用SpringBoot。

1638

被折叠的 条评论
为什么被折叠?



