场景:基于Spring Boot使用Java调用http请求的6种方式。服务端发布一个POST请求和2个GET请求。使用6种方式实现的客户端都调用服务端发布的这3个方法。可以直观感受和比对6种http请求的客户端。

项目实体类属性不存入数据库排除方式

  1. @Transient
    该注解只适用于hibernate框架,在实体类(pojo)属性上使用、表示数据库表中没有这个字段就忽略;
  2. @TableField
    该注解只适用于mybatis-plus框架:
    @TableField(exist = false):表示该属性不为数据库表字段,但又是必须使用的。
    @TableField(exist = true):表示该属性为数据库表字段。

阿里EasyExcel大数据导入用法

pom文件依赖

1
2
3
4
5
6
<!--阿里巴巴EasyExcel依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>

导入(读)数据–第一步创建一个监听器ExcelDataListener.java继承AnalysisEventListener类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 1.编写一个监听器 ExcelDataListener.java继承AnalysisEventListener类
// 重写invoke方法
package com.xiaogui.log.utils;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.Cell;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson.JSON;
import com.xiaogui.log.mapper.LogMapper;
import com.xiaogui.log.vo.resp.RespLogInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.*;

@Component
@Slf4j
public class ExcelDataListener extends AnalysisEventListener<RespLogInfo> {
/**
* 每隔10条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 10;
List<与Excel表头对应的Entity> list = new ArrayList<>();
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。
* 当然如果不用存储这个对象没用。
*/
private final RespLogInfoService respLogInfoService;

/**
* 如果使用了spring,请使用这个构造方法。
* 每次创建Listener的时候需要把spring管理的类传进来
*/
public ExcelDataListener(RespLogInfoService respLogInfoService) {
this.respLogInfoService = respLogInfoService;
}

/**
* 这个每一条数据解析都会来调用
*/
@Override
public void invoke(与Excel表头对应的Entity data, AnalysisContext context) {
log.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
respLogInfoService.saveOrUpdateBatch(list);
// 存储完成清理 list
list.clear();
}
}

/**
* 所有数据解析完成了 都会来调用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
respLogInfoService.saveOrUpdateBatch(list);
log.info("所有数据解析完成!");
}
}

实体类

1
2
3
public class RespLogInfo implements Serializable {
@ExcelProperty(index = 0)//对应excel第几列
private String type;//开支类型 信用卡等

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package easyExcel;

import com.alibaba.excel.EasyExcel;
import com.easyexcel.listener.EasyExcelOrderListener;
import com.easyexcel.pojo.ExcelOrder;
import org.junit.Test;
/***
* easyExcel测试类
***/
public class ExcelReadTest {
@Test
public void excelRead(){
//String fileName = "文件路径/订单表.xlsx";//文件路径
//默认读取第一个sheet
//EasyExcel.read(fileName, ExcelOrder.class,new EasyExcelOrderListener()).sheet().doRead();
//或者如下方法
EasyExcel.read(file.getInputStream, RespLogInfo.class,new FileListener(this)).sheet().doRead();
}
}

两个实体类互相copy

1
BeanUtils.copyProperties(被copyEntity,targetEntity);

实体Entity转Map

1
Map<String,String> oMapper = new ObjectMapper().convertValue(clerk, HashMap.class);

常量的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static final String MONDAY = "test";
public static final Map map = new HashMap();
static {
map.put("key1", "value1");
map.put("key2", "value2");
}
//或者
public static final Map<String, String> map = new HashMap<>() {
{
put("key1", "value1");
put("key2", "value2");
}
};
//List常量定义方式
public static final List<String> list = Arrays.asList("88","99","100");

SpringBoot使用RequestBodyAdvice进行统一参数处理

请求处理====在实际项目中 , 往往需要对请求参数做一些统一的操作 , 例如参数的过滤 , 字符的编码 , 第三方的解密等等 , Spring提供了RequestBodyAdvice一个全局的解决方案 , 免去了我们在Controller处理的繁琐 .RequestBodyAdvice仅对使用了@RqestBody注解的生效 , 因为它原理上还是AOP , 所以GET方法是不会操作的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.xbz.common.web;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

/**
* @title 全局请求参数处理类
* @author Xingbz
* @createDate 2019-8-2
*/
@Component
//@ControllerAdvice(basePackages = "com.xbz.controller")//此处设置需要当前Advice执行的域 , 省略默认全局生效
@ControllerAdvice(assignableTypes = {TestController.class})//也可以用这种方式 让单个controller生效
public class GlobalRequestBodyAdvice implements RequestBodyAdvice {
private final Logger log = LoggerFactory.getLogger(getClass());

/** 此处如果返回false , 则不执行当前Advice的业务 */
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// return methodParameter.getMethod().isAnnotationPresent(XXApiReq.class);
return true;
}

/**
* @title 读取参数前执行
* @description 在此做些编码 / 解密 / 封装参数为对象的操作
*
* */
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
//return new XHttpInputMessage(inputMessage, "UTF-8");
return inputMessage;
}

/**
* @title 读取参数后执行 解密等的操作区域
* @author Xingbz
*/
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
log.info("第三方请求加密数据:"+body)
//解密 todo
String aaa = body;
return aaa;
}

/**
* @title 无请求时的处理
*/
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
}

响应处理=====实现ResponseBodyAdvice接口,其实是对加了@RestController(也就是@Controller+@ResponseBody)注解的处理器将要返回的值进行增强处理。
其实也就是采用了AOP的思想,对返回值进行一次修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//此接口说明对添加了@Controller的类织入一个通知(增强功能)
@ControllerAdvice(assignableTypes = {TestController.class})
@Component
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
private final Logger log = LoggerFactory.getLogger(getClass());

@Override
public boolean supports(MethodParameter returnType, Class converterType) {
// 开关处于开启状态 是get请求
//使用MethodParameter参数判断注解信息
//符合此Get请求才进行织入通知
//return enable && returnType.hasMethodAnnotation(GetMapping.class)
return true;
}

/**
*@param body:原controller要返回的内容
加密操作等
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
log.info("响应第三方数据加密处理开始:"+body)
//加密处理 todo
String sendXml = body;
return sendXml;
}
}

vue中将后台返回的数字转换成对应的文字

7.1、对于列表循环el-table-column采用如下方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  第一种方案
<el-table-column prop="status" :show-overflow-tooltip="true" label="状态" width="60" :formatter="statusFormatter">
</el-table-column>ji

methods:{
statusFormatter(row, column){
}
}
第二种方案
<el-table-column prop="type" label="类型" align="center">
<template v-slot="{ row }">
<span v-show="row.type == 1">普通用户</span>
<span v-show="row.type == 2">管理员</span>
<span v-show="row.type == 3">项目经理</span>
</template>
</el-table-column>

7.2、对于详情或者修改页面采用如下方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
在Vue中,created和mounted的区别是created用来初始化属性值,mounted用来操作属性值。下面小编举例讲解Vue中created和mounted的区别是什么。
created是用来初始化页面的值的
mounted是修改页面的值的
created() {
console.log(this.dataMsg);
console.log(this.propMsg);
},
mounted() {
console.log(this.dataMsg);
this.submit()
console.log(this.propMsg);
}
上面方法mounted有可能只触发一次,就是说详情页面关闭后再打开mounted中的方法不会再次执行。
完美方案v-model绑定计算属性:
<el-form-item>
<span slot="label">
<i class="el-icon-edit"></i>
活动名称
</span>
<el-input v-model="formatValue"></el-input>
</el-form-item>
计算属性定义
computed: {
formatValue() {
if(this.inputvalue === '0'){
return "中国";
}
}
},

实体类属性copy

1
BeanUtils.copyProperties(source,target);

el-table-column宽度自适应

1
2
3
4
5
6
7
8
如下这种方式会把每列宽度设置为10px
<el-table-column
prop="sort"
label="Sort"
width="10%">
如下是百分比的写法
<el-table-column prop="sort" label="Sort" min-width="50%">
<el-table-column prop="sort" label="Sort" min-width="50%">

el-table表格中单元格内容过多显示省略号

1
2
3
4
5
6
7
8
9
10
el-table表格中内容超出单元格的宽度会自动换行,会使整个表格看起来显得不太美观,
此时可以使用el-table-column 自带的 show-overflow-tooltip="true" 属性来设置,可以使超出单元格宽度的内容变成省略号,
而且鼠标放上去会提示单元格中原本有的全部的内容
<el-table-column
prop="address"
label="地址"
show-overflow-tooltip="true"
min-width="100">

</el-table-column>

mybatis-plus的LambdaQueryWrapper自定义sql用法

1
2
3
4
5
6
7
8
9
10
11
# 如果排序的字段需要先转换类型呢
# 那么就需要 sql自由拼接方法 (wrapper.apply)
@Override
public List<SysRoleEntity> selectListByTypeCode(String typeCode) {
LambdaQueryWrapper<SysRoleEntity> wrapper = new LambdaQueryWrapper<SysRoleEntity>()
.eq(SysRoleEntity::getTypeCode,typeCode)

.apply("ORDER BY TO_NUMBER(SEQUENCEVALUE) ASC");
}
或者
apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2020-10-08")

mybatis-plus的@Select注解用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//没有查询条件
@Select("select BLOCK_ID,BLOCK_NAME,PARENT_BLOCK_ID,BLOCK_LEVEL,ORDER_NUM from XY_DIC_BLOCK_T where block_level=1 " )
public List<Block> sqlMany();
//有查询条件
@Select("select BLOCK_ID,BLOCK_NAME,PARENT_BLOCK_ID,BLOCK_LEVEL,ORDER_NUM from XY_DIC_BLOCK_T where block_level=#{level}" )
public List<Block> sqlManyParm(String level);
//sql中有条件判断
@Select("<script> select BLOCK_ID,BLOCK_NAME,PARENT_BLOCK_ID,BLOCK_LEVEL,ORDER_NUM from XY_DIC_BLOCK_T where 1=1 " +
"<if test='level != null'>" + " and block_level=#{level} " + "</if>" +
"</script>")
public List<Block> sqlManyParmNull(String level);
//sql中有条件判断并且传递参数时个对象的情况
@Select("<script> select BLOCK_ID,BLOCK_NAME,PARENT_BLOCK_ID,BLOCK_LEVEL,ORDER_NUM from XY_DIC_BLOCK_T where 1=1 " +
"<if test='item.blockLevel != null'>" + " and block_level=#{item.blockLevel} " + "</if>" +
"</script>")
public List<Block> sqlManyObject(@Param("item") Block block);

vue调用子组件作为弹窗时只执行一次created问题

解决办法:用v-if将子组件包裹起来,因为v-if=false时可以将子组件销毁掉,再次调用时重新渲染
补充知识:vue如何每次打开子组件弹窗都进行初始化 :visible.sync 与 v-if同时使用即可

Java字符串前后补零的几种方法

数字类型前补 0

1
String.format("%08d", 123);    // 00000123

字符串类型前补 0

1
2
String.format("%8s", "abc").replace(" ", "0");
// 00000abc

也可以先在前面补 8 位的 0,再截取:

1
2
3
String str = "00000000" + "abc";
str.substring(str.length() - 8);
// 00000abc

后补 0
对于后补 0,都可以使用一种方式来做,就是在后面加上 00000…,之后截取:

1
2
(123 + "00000000").substring(0, 8);
// 12300000

这种方式通用任何类型

springboot集成https

1
2
3
生成证书
keytool -genkey -alias tomcat -dname "CN=Andy,OU=kfit,O=kfit,L=HaiDian,ST=BeiJing,C=CN" -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 365
说明:"CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码"

输入后会提示输入密码,这个密码在下面配置文件有用到。
生成后,在家目录找到证书文件,复制到SpringBoot应用的src/main/resources下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
application.yml配置如下信息
server:
ssl:
# 证书路径
key-store: classpath:keystore.p12
# 与申请时输入一致
key-alias: tomcat
enabled: true
key-store-type: PKCS12
#与申请时输入一致
key-store-password: 123456
# 浏览器默认端口 和 80 类似,https默认的端口号为443
port: 443
说明:端口443可以改成任意值

此时启动SpringBoot应用,发现可以通过https访问了====快去打开浏览器访问试试😉。
想要http同时访问就看下面,否则跳过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在yml配置文件中,添加http端口号定义
server
http:
port: 8888
创建配置类
@Value("${server.http.port}")
private Integer httpPort;

@Bean
public ServletWebServerFactory servletContainer(){
final Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setPort(httpPort);
final TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();

tomcat.addAdditionalTomcatConnectors(connector);
return tomcat;
}
启动项目时,我们会发现如下日志,Tomcat绑定了两个端口号,其中https绑定在8080,http绑定在8888
恭喜你通关

项目防刷控制(自定义注解)

创建一个自定义注解

1
2
3
4
5
6
7
8
9
10
11
package com.example.annotation;
import java.lang.annotation.*;

@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
int seconds() default 1;
int maxCount() default 1;
}

2.创建一个拦截器 (用于拦截请求,更新当前用户访问的次数,如果访问受限,则返回超时的状态码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.example.interceptor;

import com.example.annotation.AccessLimit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@Component
public class FangshuaInterceptor extends HandlerInterceptorAdapter {
@Resource
RedisTemplate<String, Object> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handler1 = (HandlerMethod) handler;
// 3. 获取方法中的注解,看是否有该注解
AccessLimit accessLimit = handler1.getMethodAnnotation(AccessLimit.class);
if (accessLimit != null) {
// 3.2 : 判断请求是否受限制
if (isLimit(request, accessLimit)) {
render(response, "{\"code\":\"30001\",\"message\":\"请求过快\"}");
return false;
}
}
}
return true;
}

//判断请求是否受限
public boolean isLimit(HttpServletRequest request, AccessLimit accessLimit) {
// 受限的redis 缓存key ,因为这里用浏览器做测试,我就用sessionid 来做唯一key,如果是app ,可以使用 用户ID 之类的唯一标识。
String limitKey = request.getServletPath() + request.getSession().getId();
// 从缓存中获取,当前这个请求访问了几次
Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
if (redisCount == null) {
//初始 次数
redisTemplate.opsForValue().set(limitKey, 1, accessLimit.seconds(), TimeUnit.SECONDS);
System.out.println("写入redis --");
} else {
System.out.println("intValue-->" + redisCount.intValue());
if (redisCount.intValue() >= accessLimit.maxCount()) {
return true;
}
// 次数自增
redisTemplate.opsForValue().increment(limitKey);
}
return false;
}

private void render(HttpServletResponse response, String cm) throws Exception {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
out.write(cm.getBytes("UTF-8"));
out.flush();
out.close();
}
}

3.注册拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.example.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.io.Serializable;

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private FangshuaInterceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
//如果redisTemplate.opsForValue().increment(limitKey);的increment方法报错添加这个方法
@Bean
public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
//创建 redisTemplate 模版
RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
//设置 value 的转化格式和 key 的转化格式
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//关联 redisConnectionFactory
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}

4.OK , 下面我们就可以在需要进行现在访问次数的controller中的方法使用该注解了

1
2
3
4
5
6
7
8
9
10
@RestController
@RequestMapping("/test")
public class Test {
@GetMapping("/test1")
// 指定此接口同一个用户在20秒内只能访问2次
@AccessLimit(seconds = 20, maxCount = 2)
public String test1() {
return "我是test1";
}
}

string转json

1
2
JSONObject  json=new  JSONObject(“”);
String key = json.getString(key);

SpringBoot项目业务操作日志记录

1、依赖配置

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、表结构设计

1
2
3
4
5
6
7
8
9
10
create table if not exists bus_log(
id bigint auto_increment comment '自增id'
primary key,
bus_name varchar(100) null comment '业务名称',
bus_descrip varchar(255) null comment '业务操作描述',
oper_person varchar(100) null comment '操作人',
oper_time datetime null comment '操作时间',
ip_from varchar(50) null comment '操作来源ip',
param_file varchar(255) null comment '操作参数报文文件'
)comment '业务操作日志' default charset ='utf8';

3、定义业务日志注解@BusLog,可以作用在控制器或其他业务类上,用于描述当前类的功能;也可以用于方法上,用于描述当前方法的作用

1
2
3
4
5
6
7
8
9
/** * 业务日志注解 * 可以作用在控制器或其他业务类上,用于描述当前类的功能; * 也可以用于方法上,用于描述当前方法的作用; */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface BusLog {
/** * 功能名称 * @return */
String name() default "";
/** * 功能描述 * @return */
String descrip() default "";
}

4、把业务操作日志注解BusLog标记在PersonController类和方法上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
@Slf4j
@BusLog(name = "人员管理")
@RequestMapping("/person")
public class PersonController {
@Autowired
private IPersonService personService;
private Integer maxCount=100;

@PostMapping
@NeedEncrypt
@BusLog(descrip = "添加单条人员信息")
public Person add(@RequestBody Person person) {
Person result = this.personService.registe(person);
log.info("//增加person执行完成");
return result;
}
}

5、编写切面类BusLogAop,并使用@BusLog定义切入点,在环绕通知内执行过目标方法后,获取目标类、目标方法上的业务日志注解上的功能名称和功能描述, 把方法的参数报文写入到文件中,最后保存业务操作日志信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@Component
@Aspect
@Slf4j
public class BusLogAop implements Ordered {
@Autowired
private BusLogDao busLogDao;

/** * 定义BusLogAop的切入点为标记@BusLog注解的方法 */
@Pointcut(value = "@annotation(com.fanfu.anno.BusLog)")
public void pointcut() {
}

/** * 业务操作环绕通知 * * @param proceedingJoinPoint * @retur */
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----BusAop 环绕通知 start");
//执行目标方法
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
//目标方法执行完成后,获取目标类、目标方法上的业务日志注解上的功能名称和功能描述
Object target = proceedingJoinPoint.getTarget();
Object[] args = proceedingJoinPoint.getArgs();
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
BusLog anno1 = target.getClass().getAnnotation(BusLog.class);
BusLog anno2 = signature.getMethod().getAnnotation(BusLog.class);
BusLogBean busLogBean = new BusLogBean();
String logName = anno1.name();
String logDescrip = anno2.descrip();
busLogBean.setBusName(logName);
busLogBean.setBusDescrip(logDescrip);
busLogBean.setOperPerson("fanfu");
busLogBean.setOperTime(new Date());
JsonMapper jsonMapper = new JsonMapper();
String json = null;
try {
json = jsonMapper.writeValueAsString(args);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//把参数报文写入到文件中
OutputStream outputStream = null;
try {
String paramFilePath = System.getProperty("user.dir") + File.separator + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN) + ".log";
outputStream = new FileOutputStream(paramFilePath);
outputStream.write(json.getBytes(StandardCharsets.UTF_8));
busLogBean.setParamFile(paramFilePath);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//保存业务操作日志信息
this.busLogDao.insert(busLogBean);
log.info("----BusAop 环绕通知 end");
return result;
}

@Override
public int getOrder() {
return 1;
}
}

jackson返回前端的字符串中引号被自动加上反斜杆

  • 对象中有字符串是返回的对象中有反斜杆
    1
    {\"networkNumber\": 1}
    解决方法:
    使用JsonNode
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class DtoNew {
    JsonNode data;
    }
    ObjectMapper mapper = new ObjectMapper();
    try {
    dtoNew.data = mapper.readTree(dto.data ));
    } catch (IOException e) {
    e.printStackTrace();
    }
    实际使用方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @JsonIgnore
    private String preserveList;

    private JsonNode preserve_list;

    public JsonNode getPreserve_list(){
    try {
    return new ObjectMapper().readTree(this getPreserveList());
    }catch (JsonProcessingException e) {
    e.printStackTrace();
    return null;
    }
    }
  • 对象中字段转换json输出名字
    1
    2
    @JsonProperty("door_no")
    private String doorNo;
  • 忽略字段的输出
    1
    2
    @JsonIgnore
    private String doorNo;

结尾。