1、接口统一响应类和响应枚举
统一响应类:
package com.sangeng.domain; import com.fasterxml.jackson.annotation.JsonInclude; import com.sangeng.enums.AppHttpCodeEnum; import java.io.Serializable; @JsonInclude(JsonInclude.Include.NON_NULL) public class ResponseResult<T> implements Serializable { private Integer code; private String msg; private T data; public ResponseResult() { this.code = AppHttpCodeEnum.SUCCESS.getCode(); this.msg = AppHttpCodeEnum.SUCCESS.getMsg(); } public ResponseResult(Integer code, T data) { this.code = code; this.data = data; } public ResponseResult(Integer code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } public ResponseResult(Integer code, String msg) { this.code = code; this.msg = msg; } public static ResponseResult errorResult(int code, String msg) { ResponseResult result = new ResponseResult(); return result.error(code, msg); } public static ResponseResult okResult() { ResponseResult result = new ResponseResult(); return result; } public static ResponseResult okResult(int code, String msg) { ResponseResult result = new ResponseResult(); return result.ok(code, null, msg); } public static ResponseResult okResult(Object data) { ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMsg()); if(data!=null) { result.setData(data); } return result; } public static ResponseResult errorResult(AppHttpCodeEnum enums){ return setAppHttpCodeEnum(enums,enums.getMsg()); } public static ResponseResult errorResult(AppHttpCodeEnum enums, String msg){ return setAppHttpCodeEnum(enums,msg); } public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){ return okResult(enums.getCode(),enums.getMsg()); } private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String msg){ return okResult(enums.getCode(),msg); } public ResponseResult<?> error(Integer code, String msg) { this.code = code; this.msg = msg; return this; } public ResponseResult<?> ok(Integer code, T data) { this.code = code; this.data = data; return this; } public ResponseResult<?> ok(Integer code, T data, String msg) { this.code = code; this.data = data; this.msg = msg; return this; } public ResponseResult<?> ok(T data) { this.data = data; return this; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
统一响应状态枚举:
package com.sangeng.enums; public enum AppHttpCodeEnum { // 成功 SUCCESS(200,"操作成功"), // 登录 NEED_LOGIN(401,"需要登录后操作"), NO_OPERATOR_AUTH(403,"无权限操作"), SYSTEM_ERROR(500,"出现错误"), USERNAME_EXIST(501,"用户名已存在"), PHONENUMBER_EXIST(502,"手机号已存在"), EMAIL_EXIST(503, "邮箱已存在"), REQUIRE_USERNAME(504, "必需填写用户名"), LOGIN_ERROR(505,"用户名或密码错误"); int code; String msg; AppHttpCodeEnum(int code, String errorMessage){ this.code = code; this.msg = errorMessage; } public int getCode() { return code; } public String getMsg() { return msg; } }
实现案例:
@RestController @RequestMapping("/article") public class ArticleController { @Autowired private ArticleService articleService; @GetMapping("/hotArticleList") public ResponseResult hotArticleList(){ ResponseResult result = articleService.hotArticleList(); return result; } }
2、Bean拷贝
List<Article> articles = page.getRecords(); List<HotArticleVo> articleVos = new ArrayList<>(); for (Article article : articles) { HotArticleVo vo = new HotArticleVo(); //bean拷贝工具类。把article中的属性值拷贝到vo中也有的同名属性中去 BeanUtils.copyProperties(article,vo); articleVos.add(vo); }
升级:Bean拷贝工具类封装:
public class BeanCopyUtils { private BeanCopyUtils() { // 工具类,不需要构造实例 } public static <V> V copyBean(Object source,Class<V> clazz) { //创建目标对象 V result = null; try { result = clazz.newInstance(); //实现属性copy BeanUtils.copyProperties(source, result); } catch (Exception e) { e.printStackTrace(); } //返回结果 return result; } public static <O,V> List<V> copyBeanList(List<O> list,Class<V> clazz){ return list.stream() .map(o -> copyBean(o, clazz)) .collect(Collectors.toList()); } }
3、字面值处理
实际项目中都不允许直接在代码中使用字面值。都需要定义成常量来使用。这种方式有利于提高代码的可维护性。
public class SystemConstants { /** * 文章是草稿 */ public static final int ARTICLE_STATUS_DRAFT = 1; /** * 文章是正常分布状态 */ public static final int ARTICLE_STATUS_NORMAL = 0; }
4、MP分页实现
支持分页插件:
/** * @Author 三更 B站: https://space.bilibili.com/663528522 */ @Configuration public class MbatisPlusConfig { /** * 3.4.0之后版本 * @return */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return mybatisPlusInterceptor; } }
service中使用:
//最多只查询10条 Page<Article> page = new Page(1,10); page(page,queryWrapper); List<Article> articles = page.getRecords();
5、实体类中临时字段
在Article实体类中增加一个属性,但是该字段不存在于数据库表中,仅是为了业务上使用方便。
@TableField(exist = false) private String categoryName;
6、FastJson配置
@Bean//使用@Bean注入fastJsonHttpMessageConvert public HttpMessageConverter fastJsonHttpMessageConverters() { //1.需要定义一个Convert转换消息的对象 FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); SerializeConfig.globalInstance.put(Long.class, ToStringSerializer.instance); fastJsonConfig.setSerializeConfig(SerializeConfig.globalInstance); fastConverter.setFastJsonConfig(fastJsonConfig); HttpMessageConverter<?> converter = fastConverter; return converter; } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(fastJsonHttpMessageConverters()); }
7、配置MP字段自动填充
@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { Long userId = null; try { userId = SecurityUtils.getUserId(); } catch (Exception e) { e.printStackTrace(); userId = -1L;//表示是自己创建 } this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("createBy",userId , metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); this.setFieldValByName("updateBy", userId, metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime", new Date(), metaObject); this.setFieldValByName(" ", SecurityUtils.getUserId(), metaObject); } }
用注解标识哪些字段在什么情况下需要自动填充
/** * 创建人的用户id */ @TableField(fill = FieldFill.INSERT) private Long createBy; /** * 创建时间 */ @TableField(fill = FieldFill.INSERT) private Date createTime; /** * 更新人 */ @TableField(fill = FieldFill.INSERT_UPDATE) private Long updateBy; /** * 更新时间 */ @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
8、实现项目启动时预处理
如果希望在SpringBoot应用启动时进行一些初始化操作可以选择使用CommandLineRunner来进行处理。
我们只需要实现CommandLineRunner接口,并且把对应的bean注入容器。把相关初始化的代码重新到需要重新的方法中。
这样就会在应用启动的时候执行对应的代码。
@Component public class TestRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("程序初始化"); } }
9、定时任务
定时任务的实现方式有很多,比如XXL-Job等。但是其实核心功能和概念都是类似的,很多情况下只是调用的API不同而已。
这里就先用SpringBoot为我们提供的定时任务的API来实现一个简单的定时任务,让大家先对定时任务里面的一些核心概念有个大致的了解。
https://www.bejson.com/othertools/cron/
;
实现步骤
① 使用@EnableScheduling注解开启定时任务功能
我们可以在配置类上加上@EnableScheduling
@SpringBootApplication @MapperScan("com.sangeng.mapper") @EnableScheduling public class SanGengBlogApplication { public static void main(String[] args) { SpringApplication.run(SanGengBlogApplication.class,args); } }
② 确定定时任务执行代码,并配置任务执行时间
使用@Scheduled注解标识需要定时执行的代码。注解的cron属性相当于是任务的执行时间。目前可以使用 0/5 * * * * ? 进行测试,代表从0秒开始,每隔5秒执行一次。
注意:对应的bean要注入容器,否则不会生效。
@Component public class TestJob { @Scheduled(cron = "0/5 * * * * ?") public void testJob(){ //要执行的代码 System.out.println("定时任务执行了"); } }
10、使用Redis
引入依赖:
<!--redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
依赖注入:
@Autowired public RedisTemplate redisTemplate;
配置类:
package com.sangeng.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean @SuppressWarnings(value = { "unchecked", "rawtypes" }) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }
使用:
package com.sangeng.utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component; import java.util.*; import java.util.concurrent.TimeUnit; @SuppressWarnings(value = { "unchecked", "rawtypes" }) @Component public class RedisCache { @Autowired public RedisTemplate redisTemplate; /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 */ public <T> void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value); } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 */ public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { redisTemplate.opsForValue().set(key, value, timeout, timeUnit); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */ public boolean expire(final String key, final long timeout) { return expire(key, timeout, TimeUnit.SECONDS); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @param unit 时间单位 * @return true=设置成功;false=设置失败 */ public boolean expire(final String key, final long timeout, final TimeUnit unit) { return redisTemplate.expire(key, timeout, unit); } /** * 获得缓存的基本对象。 * * @param key 缓存键值 * @return 缓存键值对应的数据 */ public <T> T getCacheObject(final String key) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); return operation.get(key); } /** * 删除单个对象 * * @param key */ public boolean deleteObject(final String key) { return redisTemplate.delete(key); } /** * 删除集合对象 * * @param collection 多个对象 * @return */ public long deleteObject(final Collection collection) { return redisTemplate.delete(collection); } /** * 缓存List数据 * * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */ public <T> long setCacheList(final String key, final List<T> dataList) { Long count = redisTemplate.opsForList().rightPushAll(key, dataList); return count == null ? 0 : count; } /** * 获得缓存的list对象 * * @param key 缓存的键值 * @return 缓存键值对应的数据 */ public <T> List<T> getCacheList(final String key) { return redisTemplate.opsForList().range(key, 0, -1); } /** * 缓存Set * * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) { BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); Iterator<T> it = dataSet.iterator(); while (it.hasNext()) { setOperation.add(it.next()); } return setOperation; } /** * 获得缓存的set * * @param key * @return */ public <T> Set<T> getCacheSet(final String key) { return redisTemplate.opsForSet().members(key); } /** * 缓存Map * * @param key * @param dataMap */ public <T> void setCacheMap(final String key, final Map<String, T> dataMap) { if (dataMap != null) { redisTemplate.opsForHash().putAll(key, dataMap); } } /** * 获得缓存的Map * * @param key * @return */ public <T> Map<String, T> getCacheMap(final String key) { return redisTemplate.opsForHash().entries(key); } /** * 往Hash中存入数据 * * @param key Redis键 * @param hKey Hash键 * @param value 值 */ public <T> void setCacheMapValue(final String key, final String hKey, final T value) { redisTemplate.opsForHash().put(key, hKey, value); } /** * 获取Hash中的数据 * * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */ public <T> T getCacheMapValue(final String key, final String hKey) { HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash(); return opsForHash.get(key, hKey); } /** * 删除Hash中的数据 * * @param key * @param hkey */ public void delCacheMapValue(final String key, final String hkey) { HashOperations hashOperations = redisTemplate.opsForHash(); hashOperations.delete(key, hkey); } /** * 获取多个Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */ public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) { return redisTemplate.opsForHash().multiGet(key, hKeys); } /** * 获得缓存的基本对象列表 * * @param pattern 字符串前缀 * @return 对象列表 */ public Collection<String> keys(final String pattern) { return redisTemplate.keys(pattern); } }
11、swagger 快速入门
4.3.1 引入依赖
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency>
4.3.2 启用Swagger2
在启动类上或者配置类加 @EnableSwagger2 注解
@SpringBootApplication @MapperScan("com.sangeng.mapper") @EnableScheduling @EnableSwagger2 public class SanGengBlogApplication { public static void main(String[] args) { SpringApplication.run(SanGengBlogApplication.class,args); } }
4.3.3 测试
访问:http://localhost:7777/swagger-ui.html 注意其中localhost和7777要调整成实际项目的域名和端口号。
4.4 具体配置
4.4.1 Controller配置
4.4.1 @Api 注解
属性介绍:
tags 设置标签
description 设置描述信息
@RestController @RequestMapping("/comment") @Api(tags = "评论",description = "评论相关接口") public class CommentController { }
4.4.2 接口配置
4.4.2.1 接口描述配置@ApiOperation
@GetMapping("/linkCommentList") @ApiOperation(value = "友链评论列表",notes = "获取一页友链评论") public ResponseResult linkCommentList(Integer pageNum,Integer pageSize){ return commentService.commentList(SystemConstants.LINK_COMMENT,null,pageNum,pageSize); }
4.4.2.2 接口参数描述
@ApiImplicitParam 用于描述接口的参数,但是一个接口可能有多个参数,所以一般与 @ApiImplicitParams 组合使用。
@GetMapping("/linkCommentList") @ApiOperation(value = "友链评论列表",notes = "获取一页友链评论") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNum",value = "页号"), @ApiImplicitParam(name = "pageSize",value = "每页大小") } ) public ResponseResult linkCommentList(Integer pageNum,Integer pageSize){ return commentService.commentList(SystemConstants.LINK_COMMENT,null,pageNum,pageSize); }
4.4.3 实体类配置
4.4.3.1 实体的描述配置@ApiModel
@ApiModel用于描述实体类。
@Data @AllArgsConstructor @NoArgsConstructor @ApiModel(description = "添加评论dto") public class AddCommentDto{ //.. }
4.4.3.2 实体的属性的描述配置@ApiModelProperty
@ApiModelProperty用于描述实体的属性
@ApiModelProperty(notes = "评论类型(0代表文章评论,1代表友链评论)") private String type;
4.4.4 文档信息配置
@Configuration public class SwaggerConfig { @Bean public Docket customDocket() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.sangeng.controller")) .build(); } private ApiInfo apiInfo() { Contact contact = new Contact("团队名", "http://www.my.com", "my@my.com"); return new ApiInfoBuilder() .title("文档标题") .description("文档描述") .contact(contact) // 联系方式 .version("1.1.0") // 版本 .build(); } }
12、AOP日志记录
-
相当于是批量对原有的功能进行增强,这个时候就非常适合用AOP来进行实现;
-
自定义
AOP
注解:@Target({ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface OperationLog { // 传入需要获取的参数,可在default设置默认值 String moduleName() default "内容管理"; // 操作类型 String operateType(); }
-
定义一个切面类,加上@Component注解,@Aspect注解加入配置管理:
@AfterThrowing(value = "@annotation(operationLog)", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, OperationLog operationLog, Exception e) { handleLog(joinPoint, operationLog,e);// 方法中获取想要的参数,并存入数据库等操作 }
-
在controller方法上加上自定义注解,传入一些参数即可(有的参数需要从请求中获取):
@GetMapping("/test") @OperationLog(moduleName = "测试管理",operateType = "更新") public Result Hello(HttpServletRequest request){ HashMap<Object, Object> result = new HashMap<>(); result.put("code",200); result.put("msg","success"); return Result.success(result); }