
这里面的创建方式不做过多说明,只需要在官网里面创建好了,然后下载解压,就可以了,我这里直接使用编辑器创建
springboot搭建项目官网



选完之后,idea就会去拉取相应的jar包,创建结果如下:
启动项目



编写controller

重新启动主方法,输入请求
这样就创建成功了一个springboot项目

上面这玩意儿,我不想看到它


在项目的resources资源目录下,新建一个banner文件
再运行项目主方法

<!--这个jar包就是为了实体类中使用@ConfigurationProperties(prefix = "person")这个注解而不报红--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>@ConfigurationProperties注解实现给实体类属性赋值


依赖
<!--JSR303校验的依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>使用jsr303检验

空检查 @Null 验证对象是否为null @NotNull 验证对象是否不为null, 无法查检长度为0的字符串 @NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格. @NotEmpty 检查约束元素是否为NULL或者是EMPTY.Booelan检查 @AssertTrue 验证 Boolean 对象是否为 true @AssertFalse 验证 Boolean 对象是否为 false长度检查 @Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 @Length(min=, max=) Validates that the annotated string is between min and max included.日期检查 @Past 验证 Date 和 Calendar 对象是否在当前时间之前,验证成立的话被注释的元素一定是一个过去的日期 @Future 验证 Date 和 Calendar 对象是否在当前时间之后 ,验证成立的话被注释的元素一定是一个将来的日期 @Pattern 验证 String 对象是否符合正则表达式的规则,被注释的元素符合制定的正则表达式,regexp:正则表达式 flags: 指定 Pattern.Flag 的数组,表示正则表达式的相关选项。数值检查 建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为”“,Integer为null @Min 验证 Number 和 String 对象是否大等于指定的值 @Max 验证 Number 和 String 对象是否小等于指定的值 @DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度 @DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度 @Digits 验证 Number 和 String 的构成是否合法 @Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。 @Range(min=, max=) 被指定的元素必须在合适的范围内 @Range(min=10000,max=50000,message=”range.bean.wage”) @Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证) @CreditCardNumber信用卡验证 @Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。 @ScriptAssert(lang= ,script=, alias=) @URL(protocol=,host=, port=,regexp=, flags=)

还有一种标准的配置,即:采用多个yml文件,如:application-test.yml 就是测试环境的配置、appilication-dev.yml 就是开发环境的配置、appilication-pro.yml 就是生产环境配置


这种需要导入相应的启动器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>编写controller

测试

官网学习地址
一张图看明白:

解读:
前端交给我们的页面,是html页面。如果是我们以前开发,我们需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等
jsp支持非常强大的功能,包括能写Java代码,但是,SpringBoot是以jar的方式,不是war,第二,我们用的还是嵌入式的Tomcat,所以,springboot现在默认是不支持jsp的
那不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,那怎么办?
SpringBoot推荐使用模板引擎:
模板引擎,jsp就是一个模板引擎,还有用的比较多的FreeMaker、Velocity,再多的模板引擎,他们的思想都是一样的,SpringBoot推荐使用thymeleaf
模板引擎的作用就是我们来写一个页面模板,比如有些值,是动态的,我们写一些表达式。而这些值从哪来?就是我们在后台封装一些数据。然后把这个模板和这个数据交给模板引擎,模板引擎按照我们封装的数据把这表达式解析出来、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容从而最后显示出来,这就是模板引擎。
不管是jsp还是其他模板引擎,都是这个思想。只不过,不同模板引擎之间,他们可能语法有点不一样。其他的就不介绍了,这里主要介绍一下SpringBoot给我们推荐的Thymeleaf模板引擎,这模板引擎,是一个高级语言的模板引擎,他的这个语法更简单。而且功能更强大

提取出来看一下,从而在springboot中演示一下
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>怎么使用thymeleaf?



测试
成功跳过去了

编写后台,存入数据

在前台获取数据

表空间约束链接如下,这个在thymeleaf官网中有
xmlns:th="http://www.thymeleaf.org"测试:


后台

前台

测试

其他的玩法都差不多


webjars 和 getStaticLocations()使用jQuery做演示
<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.4.1</version></dependency>


getStaticLocations(),点进去看一下
发现是如下这么一个方法
public String[] getStaticLocations() { return this.staticLocations; }staticLocations

"classpath:/META-INF/resources/", <!--这个就不多说明,指的就是再建一个META-INF文件夹,里面再建一个resources目录,参照Java基础中的web项目目录-->"classpath:/resources/","classpath:/static/","classpath:/public/"
测试
删掉resources中的资源文件,继续测试


资源放置建议:
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>编写application.yml
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_spring?useUnicode=true&characterEncoding=utf-8 username: root password: "072413"测试
依赖
<!--要玩druid的话,需要导入下面这个依赖 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency>修改yml文件

测试
<!-- 玩druid实现监控日志,需要web启动器支持,因为:druid的statViewServlet本质是继承了servlet 因此:需要web的依赖支持 / servlet支持--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>编写配置
package cn.xiegongzi.config;// 这个类是为了延伸druid的强大功能 ————— 监控后台// 注意:这个需要spring的web启动器支持,即:这个监控后台的本质StatViewServlet就是servlet,所以需要servlet支持import com.alibaba.druid.support.http.StatViewServlet;import org.springframework.boot.web.servlet.ServletRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.HashMap;@Configurationpublic class DruidConfig { @Bean public ServletRegistrationBean StatViewServlet() { ServletRegistrationBean bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); HashMap<String, String> initParameters = new HashMap<>(); // 下面这些参数可以在 com.alibaba.druid.support.http.StatViewServlet // 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到 initParameters.put("loginUsername", "zixieqing"); // 登录日志监控的用户名 initParameters.put("loginPassword", "072413"); // 登录密码 initParameters.put("allow", "`localhost`"); // 运行谁可以访问日志监控 bean.setInitParameters(initParameters); return bean; }}测试


导入依赖
<!-- mybatis-spring-boot-starter是第三方( mybatis )jar包,不是spring官网的 spring自己的生态是:spring-boot-stater-xxxx--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>编写实体
package cn.xiegongzi.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;@Data@AllArgsConstructor@NoArgsConstructorpublic class User implements Serializable { private Integer id; private String username; private String password;}编写dao / mapper层
package cn.xiegongzi.mapper;import cn.xiegongzi.entity.User;import org.apache.ibatis.annotations.Mapper;import java.util.List;/** @Mapper 这个注解是mybati-spring提供的,即:和自动装配是一样的效果* 还可以用:* @Repository 是spring本身提供的* * 以及:在启动类( main )中使用@mapperScan扫包* */@Mapperpublic interface IUserMapper { List<User> findALLUser();}编写xml的sql语句

编写yml
# 编写连接池spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_spring?useUnicode=true&characterEncoding=utf-8 username: root password: "072413" type: com.alibaba.druid.pool.DruidDataSource# 把实现类xml文件添加进来mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: cn.xiegongzi.entity # 给实体类配置别名 configuration: map-underscore-to-camel-case: true # 开启驼峰命名映射
测试

@insert() 、 @delete() 、 @update() 、 @select()注解,然后小括号中编写sql字符串即可

依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version></dependency>测试

导入依赖
<!--swagger所需要的依赖--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.8.0</version> </dependency> <!--这个依赖是为了渲染swagger文档页面的( 为了好看一点罢了 ) ,swagger真正的依赖是上面两个--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.8.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.75</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>编写swagger配置文件

package cn.xiegongzi.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.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration // 表明当前类是一个配置类,并把当前类丢到spring容器中去 @EnableSwagger2 // 开启swagger功能 public class SwaggerConfig { @Bean public Docket createRestApi() { // http://ip地址:端口/项目名/swagger-ui.html#/ ApiInfo apiInfo = new ApiInfoBuilder() // 网站标题 即:生成的文档网址标题 .title( "悠忽有限公司" ) // 网站描述 即:对生成文档的描述 .description( "这是一个很nice的接口文档" ) // 版本 .version( "9.0" ) // 联系人 .contact( new Contact("紫邪情","https://www.cnblogs.com/xiegongzi/","110" ) ) // 协议 http / https都可以 .license( "tcp" ) // 协议url 即:进入到swagger文档页面的地址 .licenseUrl( "http://localhost:8080/" ) .build(); // swagger版本 return new Docket( DocumentationType.SWAGGER_2 ) // 请求映射路径 就是:controller中有一个接口,然后前台访问的那个接口路径 // 这个可以在生成的文档中进行调试时看到 .pathMapping( "/" ) // 根据pathMapping去进行查询( 做相应的操作 ) .select() // 扫描包 即:哪些地方可以根据我们的注解配置帮我们生成文档 .apis( RequestHandlerSelectors.basePackage( "cn.xiegongzi" ) ) .paths( PathSelectors.any() ) .build() .apiInfo( apiInfo ); } }编写yml文件
spring: datasource: # 注意这里加了cj啊,即:MySQL驱动用的是8.x的 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_spring?useUnicode=true&characterEncoding=utf-8 username: root # 注意:在yml中,这种自己写的内容最好用字符串写法,以后玩Redis也是一样,不然有时出现坑,即:密码无效 / 这里面填入的值没有解析出来,不匹配 password: "072413"编写实体类
package cn.xiegongzi.entity; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @AllArgsConstructor @NoArgsConstructor/** * @ApiModel(description = "描述") * 表明这个实体类就是需要的数据名和类型 * 后台接收前端的参数是一个对象时使用( controller写的是@RequestBody OrderPaidDTO orderPaid ),即:后端接收参数封装成了一个xxxxDTO( PO、BO、Entity、DTO、DAO含义和关系是什么,自行百度 ) * 这个东西可以先不加,在做增加、修改时可以用这个测试一下,从而去swagger中看效果*/ @ApiModel(description = "用户信息") public class User implements Serializable { // 数据属性配置,这里面可以跟几个属性,常见的是value、required、dataType、hidden,在待会后面附加的链接中有解释 @ApiModelProperty private Integer id; @ApiModelProperty private String username; @ApiModelProperty private String phone; }编写mapper
package cn.xiegongzi.mapper; import cn.xiegongzi.entity.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; import java.util.List; @Mapper public interface IUserMapper { @Select("select * from user") List<User> findAllUser(); }编写service接口和实现类

编写controller

package cn.xiegongzi.controller; import cn.xiegongzi.service.IUserService; import com.alibaba.fastjson.JSON; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController /* @Api 表示当前类可以被生成一个swagger文档 可以跟参数tags,参数表示:这整个接口的名字( 前端是接口,后端是controller控制层 ) */ @Api(tags = "用户管理接口集") public class UserController { @Autowired private IUserService userService; /* @ApiImplicitParam 这个注解是对请求参数做限制用的,如:请求时要求前台传递一个id,那么:在这个注解里面:就可以声明这个参数的类型( 对象类型中要求属性限制,可以使用@ApiModelProperty 也可以使用 符合jsr303规范的数据检验方式 ) */ // 遵循restful风格 要是使用@RequestMapping的话,会生成多个接口swagger文档( 即:对应post、get.... ) @GetMapping("/swaggger/doc") // value这个接口的名字;notes 对这个接口的描述 @ApiOperation(value = "获取全部用户接口" , notes = "获取全部的用户") public String findAllUser() { return JSON.toJSONString( userService.findAllUser() ); } }测试


数据库表字段信息

导入依赖
<!--导入jpa需要的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!--项目需要的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.75</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>编写yml文件
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_spring?useUnicode=true&characterEncoding=utf-8 username: root password: "072413" jpa:# 这里可以不用hibernate,还可以用hikari( 这个在前面整合jdbc时见过,就是当时输出的那句话 ) hibernate:# 指定为update,每次启动项目检测表结构有变化的时候会新增字段,表不存在时会新建表 ddl-auto: update# 如果指定create,则每次启动项目都会清空数据并删除表,再新建# 这里面还可以跟:create-drop/create/none naming: # 指定jpa的自动表生成策略,驼峰自动映射为下划线格式 implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl # 默认就是这个# physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl# 注掉的这种是:不用驼峰名字,直接把实体类的大写字母变小写就完了 show-sql: true # 在控制台显示sql语句( 不是真的sql语句,而是相当于:说明 ),默认是false# 使用INNODB引擎 properties.hibernate.dialect: org.hibernate.dialect.MySQL55Dialect database-platform: org.hibernate.dialect.MySQL55Dialect# 使用JPA创建表时,默认使用的存储引擎是MyISAM,通过指定数据库版本,可以使用InnoDB编写实体类
package cn.xiegongzi.entity;import lombok.Data;import org.springframework.data.annotation.Id;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import java.io.Serializable;@Data// @AllArgsConstructor// @NoArgsConstructor@Entity/** @Entity * 表明:当前类和数据库中的这个同类名的数据库表形成ORM映射关系 * 要是数据库中没有这个表,那么:根据yml配置的ddl-auto: update 就会自动帮我们生成*/public class ZiXieQing implements Serializable { @javax.persistence.Id @Id // 表明这个属性是数据库表中的主键 @GeneratedValue(strategy = GenerationType.IDENTITY) // 表示:自增 默认是auto,即:和数据库中的auto_increment是一样的 private int id; @Column( length = 15 ) // 生成数据库中的列字段,里面的参数不止这些,还可以用其他的,对应数据库列字段的那些操作 // 可以点进源码看一下 private String name; // public ZiXieQing() { // } public ZiXieQing(int id, String name) { this.id = id; this.name = name; }}附:@Column注解中可以支持的属性
@Target({ElementType.METHOD, ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface Column { String name() default ""; boolean unique() default false; boolean nullable() default true; boolean insertable() default true; boolean updatable() default true; String columnDefinition() default ""; String table() default ""; int length() default 255; int precision() default 0; int scale() default 0;}编写mapper
package cn.xiegongzi.mapper;import cn.xiegongzi.entity.ZiXieQing;import org.springframework.data.jpa.repository.JpaRepository;@Repository/** * 注:这里别用@Mapper这个注解,因为:@mapper是mybatis提供的注解 * JpaRepository相对mybatis来说就是是外部的东西。因此:并不能支持@mapper注解 */public interface ZiXieQingMapper extends JpaRepository<ZiXieQing , Integer> { /* JpaRepository这里面有默认的一些方法,即:增删查改... JpaRepository<ZiXieQing , Integer> 本来样子是:JpaRepository<T , ID> T 表示:自己编写的实体类 类型 ID 表示: 实体类中id字段的类型 注:本示例中,实体类中id是int 因为要弄自增就必须为int,不然和数据库映射时对不上 */}附:JpaRepository中提供的方法

编写service接口和实现类

编写controller

测试

现在去看一下数据库

生成出来了,完成
导入依赖
<!--mybatis-plus需要的依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency>编写yml
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_spring?useUnicode=true&characterEncoding=utf-8 username: root password: "072413" type: com.alibaba.druid.pool.DruidDataSourcemybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # mybatis-plus配置日志 map-underscore-to-camel-case: true # 开启驼峰映射 即:实体类属性名和数据库字段采用驼峰映射 auto-mapping-behavior: full # 自动映射字段 mapper-locations: classpath:mapper/*.xml # 如果使用了mybatis和mybatis-plus 那么这里就可以把mybatis的实现类xml集成进来 # 但是:最好别这种做,用了mybatis就别用mybatis-plus,二者最好只用其一注:别把mybatis和mybatis-plus一起集成到spring中,否则:很容易出问题,虽然:mybatis-plus是mybatis的增强版,既然是增强版,那么就不会抛弃它原有的东西,只会保留原有的东西,然后新增功能,但是:mybatis和mybatis-plus集成到一起之后很容易造成版本冲突,因此:对于单个系统模块 / 单个系统来说,建议二者只选其一集成 ( PS:当然事情不是绝对的 我说的是万一,只是操作不当很容易触发错误而已,但是:二者一起集成也是可以的,当出现报错时可以解决掉,不延伸了 ,这不是这里该做的事情 )
编写实体类
package cn.xiegongzi.entity;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;@Data@AllArgsConstructor@NoArgsConstructor@TableName(value = "user") // 表名注解public class User implements Serializable { @TableId(type = IdType.AUTO) // 表示主键,这个主键是一个Long类型的值( 即:snowflake雪花算法 ) private Integer id; @TableField("username") // 数据库字段名 就是:当实体类中的字段和数据库字段不一样时可以使用 private String name; private String phone;}编写mapper
package cn.xiegongzi.mapper;import cn.xiegongzi.entity.User;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import org.apache.ibatis.annotations.Mapper;@Mapper // 不想在每个mapper层都写这个注解,那把@MapperScan("cn.xiegongzi.mapper") 在启动类中加入这个注解也可以实现public interface IUserMapper extends BaseMapper<User> { /* BaseMapper 和 JPA一样,内部有很多方法 , 即:CRUD.....,还有分页( 分页就是page()这个方法 ) BaseMapper原来的样子是:BaseMapper<T> T表示实体类 类型 */}附:BaseMapper<T>提供的方法如下:
测试

其他的知识,在mybatis-plus官网中都有
Caffeine这个东西要更简单一点,而且不需要借助xml文件,而ehcache需要借助xml文件@CacheConfig(cacheNames = "users")public class UserService{}@Cacheable来代替@Cacheable(value = "user", key = "#userId")User selectUserById( Integer userId );@Cacheable注解的属性
调用之后才做判断,所以:这个属性可以通过封装的result进行判断 )org.springframewart.cahce.intercceptor.KeyGenerator接口,并使用改参数来绑定org.springframewaork.cache.interceptor.CacheResolver接口来实现自己的缓存解析器写数据的方法上,如:新增 / 修改方法,调用方法时会自动把对应的数据放入缓存,@CachePut的参数和@Cacheable差不多@CachePut(value="user", key = "#userId")public User save(User user) { users.add(user); return user;}@CacheEvict(value = "user", key = "#userId")void delete( Integer userId);@Cacheable一样的参数之外,还有另外两个参数:@Caching( put = { @CachePut(value = "user", key = "#userId"), @CachePut(value = "user", key = "#username"), @CachePut(value = "user", key = "#userAge"), })依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>在
application.yml配置文件中加入配置
cache: ehcache: # 配置ehcache.xml配置文件所在地 config: class:ehcache.xml在主启动类开启缓存功能
@SpringBootAllication@EnableCachingpublic class Starter { public static void main(String[] args) { SpringApplication.run(Starter.class); }}编写
ehcache.xml配置文件
resources目录下新建rhcache.xml,并编写如下内容:<ehcache name="myCache"> <!--缓存磁盘保存路径--> <diskStore path = "D:/test/cache"/> <!--默认的缓存配置 maxElementsInMemory 缓存最大数目 eternal 对象是否永久有效 一旦设置了,那么timeout将不再起作用 timeToIdleSeconds 设置对象在实效前能允许的闲置时间( 单位:秒 ),默认值是0,即:可闲置时间无穷大 仅当eternal=“false"对象不是永久有效时使用 timeToLiveSeconds 设置对象失效前能允许的存活时间( 单位:秒 ) 最大时间介于创建时间和失效时间之间 maxElementsOnDisk 磁盘最大缓存个数 diskExpiryThreadIntervalSeconds 磁盘失效时,线程运行时间间隔,默认是120秒 memoryStoreEvictionPolicy 当达到设定的maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存 默认策略是LRU( 即:最近最少使用策略 ) 还可以设定的策略: FIFO 先进先出策略 LFU 最近最少被访问策略 LRU 最近最少使用策略 缓存的元素有一个时间戳,当缓存容量满了,同时又需要腾出地方来缓存新的元素时, 那么现有缓存元素中的时间戳 离 当前时间最远的元素将被清出缓存 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> <!--下面的配置是自定义缓存配置,可以复制粘贴,用多套 name 起的缓存名 overflowToDisk 当系统宕机时,数据是否保存到上面配置的<diskStore path = "D:/test/cache"/>磁盘中 diskPersistent 是否缓存虚拟机重启期数据 另外的配置项: clearOnFlush 内存数量最大时是否清除 diskSpoolBufferSizeMB 设置diskStore( 即:磁盘缓存 )的缓冲区大小,默认是30MB 每个Cache都应该有自己的一个缓冲区 --> <cache name="users" eternal="false" maxElementsInMemory="100" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU" /></ehcache>@Cacheable注解举例查询条件是单个时( service实现类中直接开注解 )
// 这里的value值就是前面xml中配置的哪个cache name值@Cacheable(value="users", key = "#username")public User queryUserByUsername(String username) { return userMapper.selectUserByUsername(username);}查询条件是多个时( service实现类中直接开注解 )
// 这里的UserDAO.username+就是封装的UserDAO,里面的属性有username、userage、userPhone@Cache(value="users", key = "#UserDAO.username+'-'+#UserDAO.userage+'-'+#UserDAO.userPhone")public User queryUserByUsername(UserDAO userDAO) { return userMapper.selectUserByUserDAO(userDAO);}其他的注解也都是差不多的


即:数组下标为0的位置不放值,然后把树结构的数据放在对应位置
这种小顶堆需要明白三个点:

后续的定时任务框架就是基于这个做的,如:Spring中有一个@Scheduleed( cron = "x x x x ...." )注解,它的这个cron时间表达式就是基于这种分量时间轮
使用多个轮子
底层原理就是:小顶堆,只是它的底层用了一个taskQueue任务队列来充当小顶堆中的哪个数组,存取找的逻辑都是和小顶堆一样的
有着弊端:
run(),也就是单线程 - 缺点:任务阻塞( 阻塞原因:任务超时 )
package com.tuling.timer;import java.util.Date;import java.util.Timer;import java.util.TimerTask;public class TimerTest { public static void main(String[] args) { Timer t = new Timer();// 任务启动 for (int i=0; i<2; i++){ TimerTask task = new FooTimerTask("foo"+i); t.scheduleAtFixedRate(task,new Date(),2000);// 任务添加 10s 5次 4 3 // 预设的执行时间nextExecutorTime 12:00:00 12:00:02 12:00:04 // schedule 真正的执行时间 取决上一个任务的结束时间 ExecutorTime 03 05 08 丢任务(少执行了次数) // scheduleAtFixedRate 严格按照预设时间 12:00:00 12:00:02 12:00:04(执行时间会乱) // 单线程 任务阻塞 任务超时 } }}class FooTimerTask extends TimerTask { private String name; public FooTimerTask(String name) { this.name = name; } public void run() { try { System.out.println("name="+name+",startTime="+new Date()); Thread.sleep(3000); System.out.println("name="+name+",endTime="+new Date()); // 因为是单线程,所以解决办法:使用线程池执行 } catch (InterruptedException e) { e.printStackTrace(); } }}底层原理就是timer + 线程池来做到的
如下的Executors.newScheduledThreadPool(5);创建线程池的方法在高并发情况下,最好别用

package com.tuling.pool;import java.util.Date;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class ScheduleThreadPoolTest { public static void main(String[] args) { // 这种线程池叫做垃圾 - 了解即可 // 缺点:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); for (int i=0;i<2;i++){ scheduledThreadPool.scheduleAtFixedRate(new Task("task-"+i),0,2, TimeUnit.SECONDS); } }}class Task implements Runnable{ private String name; public Task(String name) { this.name = name; } public void run() { try { System.out.println("name="+name+",startTime="+new Date()); Thread.sleep(3000); System.out.println("name="+name+",endTime="+new Date()); } catch (InterruptedException e) { e.printStackTrace(); } }}@Scheduled注解来进行任务配置,那么就需要在主启动类上加上@EnableScheduling // 开启定时任务注解这个注解的几个属性
cron表达式说明

| 通配符 | 意义 |
|---|---|
? | 表示不指定值,即不关心某个字段的取值时使用 需要注意的是,月份中的日期和星期可能会起冲突,因此在配置时这两个得有一个是 ? |
* | 表示所有值,例如:在秒的字段上设置 * ,表示每一秒都会触发 |
, | 用来分开多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发 |
- | 表示区间,例如在秒上设置 "10-12",表示 10,11,12秒都会触发 |
/ | 用于递增触发,如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50) |
# | 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六,(用 在母亲节和父亲节再合适不过了) |
L | 表示最后的意思 在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会自动判断是否是润年 在周字段上表示星期六,相当于"7"或"SAT"(注意周日算是第一天) 如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表 示"本月最后一个星期五" |
W | 表示离指定日期的最近工作日(周一至周五) 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发,如果15号正好在工作日(周一至周五),则就在该天触发 如果指定格式为 "1W",它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,"W"前只能设置具体的数字,不允许区间"-") |
L 和 W组合 | 如果在日字段上设置"LW",则表示在本月的最后一个工作日触发(一般指发工资 ) |
周字段的设置 | 若使用英文字母是不区分大小写的 ,即 MON 与mon相同 |
cron表达式举例
“0 0 12 * * ?” 每天中午12点触发“0 15 10 ? * *” 每天上午10:15触发“0 15 10 * * ?”“0 15 10 * * ? *”“0 15 10 * * ? 2005” 2005年的每天上午10:15 触发“0 0/5 14 * * ?” 在每天下午2点到下午2:55期间的每5分钟触发“0 0-5 14 * * ?” 在每天下午2点到下午2:05期间的每1分钟触发“0 10,44 14 ? 3 WED” 每年三月的星期三的下午2:10和2:44触发“0 15 10 ? * MON-FRI” 周一至周五的上午10:15触发“0 15 10 ? * 6L” 每月的最后一个星期五上午10:15触发“0 15 10 ? * 6L 2002-2005” 2002年至2005年的每月的最后一个星期五上午10:15触发“0 15 10 ? * 6#3” 每月的第三个星期五上午10:15触发0 23-7/2,8 * * * 晚上11点到早上8点之间每两个小时,早上八点0 11 4 * 1-3 每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点逻辑
import redis.clients.jedis.Jedis;import utils.JedisUtils;import java.time.Instant;import java.util.Set;public class DelayQueueExample { // zset key private static final String _KEY = "myTaskQueue"; public static void main(String[] args) throws InterruptedException { Jedis jedis = JedisUtils.getJedis(); // 30s 后执行 long delayTime = Instant.now().plusSeconds(30).getEpochSecond(); jedis.zadd(_KEY, delayTime, "order_1"); // 继续添加测试数据 jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_2"); jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_3"); jedis.zadd(_KEY, Instant.now().plusSeconds(7).getEpochSecond(), "order_4"); jedis.zadd(_KEY, Instant.now().plusSeconds(10).getEpochSecond(), "order_5"); // 开启定时任务队列 doDelayQueue(jedis); } /** * 定时任务队列消费 * @param jedis Redis 客户端 */ public static void doDelayQueue(Jedis jedis) throws InterruptedException { while (true) { // 当前时间 Instant nowInstant = Instant.now(); long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond(); // 上一秒时间 long nowSecond = nowInstant.getEpochSecond(); // 查询当前时间的所有任务 Set<String> data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond); for (String item : data) { // 消费任务 System.out.println("消费:" + item); } // 删除已经执行的任务 jedis.zremrangeByScore(_KEY, lastSecond, nowSecond); Thread.sleep(1000); // 每秒查询一次 } }}逻辑
config set notify-keyspace-events Ex 的命令手动开启,开启之后定时任务的代码如下import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPubSub;import utils.JedisUtils;public class TaskExample { public static final String _TOPIC = "__keyevent@0__:expired"; // 订阅频道名称 public static void main(String[] args) { Jedis jedis = JedisUtils.getJedis(); // 执行定时任务 doTask(jedis); } /** * 订阅过期消息,执行定时任务 * @param jedis Redis 客户端 */ public static void doTask(Jedis jedis) { // 订阅过期消息 jedis.psubscribe(new JedisPubSub() { @Override public void onPMessage(String pattern, String channel, String message) { // 接收到消息,执行定时任务 System.out.println("收到消息:" + message); } }, _TOPIC); }}
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>定义job
public class MyJob implements Job { private Logger log = LoggerFactory.getLogger(MyJob.class); @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); TriggerKey triggerKey = jobExecutionContext.getTrigger().getKey(); log.info("触发器:{},所属组:{},执行时间:{},执行任务:{}", triggerKey.getName(),triggerKey.getGroup(),dateFormat.format(new Date()),"hello SpringBoot Quartz..."); }}编写QuartzConfig
public class QuartzConfig { @Bean public JobDetail jobDetail() { return JobBuilder.newJob(MyJob.class) .storeDurably() .build(); } @Bean public Trigger trigger01() { SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() // 每一秒执行一次 .withIntervalInSeconds(1) // 永久重复,一直执行下去 .repeatForever(); return TriggerBuilder.newTrigger() // 参数1、trigger名字;参数2、当前这个trigger所属的组 - 参考时间轮存储任务,那个刻度后面是怎么存的任务 .withIdentity("trigger01", "group1") .withSchedule(scheduleBuilder) // 哪一个job,上一个方法中bean注入 .forJob("jobDetail") .build(); } /** * 每两秒触发一次任务 */ @Bean public Trigger trigger02() { return TriggerBuilder .newTrigger() .withIdentity("triiger02", "group1") // cron时间表达式 .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ? *")) .forJob("jobDetail") .build(); }}最后:SpringBoot还有很多内容,那些也就是整合各种框架而已,到了现在整合了这么多,那也有点门路了,其实都差不多是同样的套路:1、引入SpringBoot整合的对应框架依赖;2、编写对应的配置;3、使用对应的注解 / 编写业务逻辑。差不多都是这样的套路,因此:到时需要时直接面向百度编程即可
另外:SpringBoot原理篇链接:https://www.cnblogs.com/xiegongzi/p/15522405.html