从零搭建自己的SpringBoot后台框架(十五)

释放双眼,带上耳机,听听看~!

一:添加AOP依赖


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

二:创建自定义注解和切面

创建core→aop文件夹

在aop文件夹下中创建注解,其中remark为记录的备注


1
2
3
4
5
6
7
8
9
10
11
12
1package com.example.demo.core.aop;
2
3import java.lang.annotation.*;
4
5@Target(ElementType.METHOD)
6@Retention(RetentionPolicy.RUNTIME)
7@Documented
8public @interface AnnotationLog {
9    String remark() default "";
10
11}
12

创建切面

注:关于SystemLog类,之前集成generator自动生成model,xml,dao功能这篇文章提到过的,忘记的可以再去看一下


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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
1package com.example.demo.core.aop;
2
3import com.alibaba.fastjson.JSON;
4import com.example.demo.core.systemlog.SystemLogQueue;
5import com.example.demo.core.ret.ServiceException;
6import com.example.demo.core.utils.ApplicationUtils;
7import com.example.demo.model.SystemLog;
8import com.example.demo.model.UserInfo;
9import com.example.demo.service.SystemLogService;
10import org.apache.ibatis.javassist.*;
11import org.apache.ibatis.javassist.bytecode.CodeAttribute;
12import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
13import org.apache.ibatis.javassist.bytecode.MethodInfo;
14import org.apache.shiro.SecurityUtils;
15import org.aspectj.lang.JoinPoint;
16import org.aspectj.lang.annotation.*;
17import org.slf4j.Logger;
18import org.slf4j.LoggerFactory;
19import org.springframework.stereotype.Component;
20import org.springframework.web.context.request.RequestContextHolder;
21import org.springframework.web.context.request.ServletRequestAttributes;
22
23import javax.annotation.Resource;
24import javax.servlet.http.HttpServletRequest;
25import javax.servlet.http.HttpServletResponse;
26import java.lang.reflect.Method;
27import java.util.Date;
28import java.util.HashMap;
29import java.util.Map;
30
31/**
32 * @Description: aop记录操作日志
33 * @author phubing
34 * @date 2019年5月28日 18:43:28
35 */
36@Aspect
37@Component
38public class AspectLog {
39
40    private static final Logger logger = LoggerFactory.getLogger(AspectLog.class);
41    
42    @Resource
43    private SystemLogService systemLogService;
44
45    /**
46     * 定义切点
47     */
48    @Pointcut("@annotation(com.example.demo.core.aop.AnnotationLog)")
49    public void methodCachePointcut() {
50    }
51
52    @Before("methodCachePointcut()")
53    public void doBefore(JoinPoint p) throws Exception{
54        SystemLog systemLog = getSystemLogInit(p);
55        systemLog.setLogType(SystemLog.LOGINFO);
56        systemLogService.insert(systemLog);
57    }
58
59    /**
60     * 调用后的异常处理
61     * @param p
62     * @param e
63     */
64    @AfterThrowing(pointcut = "methodCachePointcut()", throwing = "e")
65    public void doAfterThrowing(JoinPoint p, Throwable e) throws Throwable {
66        //业务异常不用记录
67        if(!(e instanceof ServiceException)) {
68            try {
69                SystemLog systemLog =getSystemLogInit(p);
70                systemLog.setLogType(SystemLog.LOGERROR);
71                systemLog.setExceptionCode(e.getClass().getName());
72                systemLog.setExceptionDetail(e.getMessage());
73                systemLogService.insert(systemLog);
74            } catch (Exception ex) {
75                logger.error("==异常通知异常==");
76                logger.error("异常信息:{}", ex.getMessage());
77            }
78        }
79    }
80
81    private SystemLog getSystemLogInit(JoinPoint p){
82        SystemLog systemLog = new SystemLog();
83        try{
84            //类名
85            String targetClass = p.getTarget().getClass().toString();
86            //请求的方法名
87            String tartgetMethod = p.getSignature().getName();
88            //获取类名  UserController
89            String classType = p.getTarget().getClass().getName();
90            Class<?> clazz = Class.forName(classType);
91            String clazzName = clazz.getName();
92            //请求参数名+参数值的map
93            Map<String, Object> nameAndArgs = getFieldsName(this.getClass(), clazzName, tartgetMethod, p.getArgs());
94            systemLog.setId(ApplicationUtils.getUUID());
95            systemLog.setDescription(getMthodRemark(p));
96            systemLog.setMethod(targetClass+"."+tartgetMethod);
97            //大家可自行百度获取ip的方法
98            systemLog.setRequestIp("192.168.1.104");
99            systemLog.setParams(JSON.toJSONString(nameAndArgs));
100            systemLog.setUserId(getUserId());
101            systemLog.setCreateTime(new Date());
102        }catch (Exception ex){
103            logger.error("==异常通知异常==");
104            logger.error("异常信息:{}", ex.getMessage());
105        }
106        return systemLog;
107    }
108
109    /**
110     * 通过反射机制 获取被切参数名以及参数值
111     *
112     * @param cls
113     * @param clazzName
114     * @param methodName
115     * @param args
116     * @return
117     * @throws NotFoundException
118     */
119    private Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
120        Map<String, Object> map = new HashMap<>();
121        ClassPool pool = ClassPool.getDefault();
122        ClassClassPath classPath = new ClassClassPath(cls);
123        pool.insertClassPath(classPath);
124        CtClass cc = pool.get(clazzName);
125        CtMethod cm = cc.getDeclaredMethod(methodName);
126        MethodInfo methodInfo = cm.getMethodInfo();
127        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
128        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
129        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
130        for (int i = 0; i < cm.getParameterTypes().length; i++) {
131            //HttpServletRequest 和HttpServletResponse 不做处理
132            if(!(args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse)){
133                //paramNames即参数名
134                map.put(attr.variableName(i + pos), JSON.toJSONString(args[i]));
135            }
136        }
137        return map;
138    }
139
140    /**
141     * 获取方法的中文备注____用于记录用户的操作日志描述
142     * @param joinPoint
143     * @return
144     * @throws Exception
145     */
146    private static String getMthodRemark(JoinPoint joinPoint) throws Exception {
147        String targetName = joinPoint.getTarget().getClass().getName();
148        String methodName = joinPoint.getSignature().getName();
149        Object[] arguments = joinPoint.getArgs();
150        Class targetClass = Class.forName(targetName);
151        Method[] method = targetClass.getMethods();
152        String methode = "";
153        for (Method m : method) {
154            if (m.getName().equals(methodName)) {
155                Class[] tmpCs = m.getParameterTypes();
156                if (tmpCs.length == arguments.length) {
157                    AnnotationLog methodCache = m.getAnnotation(AnnotationLog.class);
158                    if (methodCache != null) {
159                        methode = methodCache.remark();
160                    }
161                    break;
162                }
163            }
164        }
165        return methode;
166    }
167
168    private static String getUserId() {
169        String userId = "";
170        UserInfo userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();
171        if(userInfo != null){
172            userId = userInfo.getId();
173        }
174        return userId;
175    }
176
177
178
179}
180

三:用法

在UserInfoController的selectById上添加我们刚创建的注解


1
2
3
4
5
6
7
1@PostMapping("/selectById")
2@AnnotationLog(remark = "查询")
3public RetResult<UserInfo> selectById(@RequestParam String id) {
4    UserInfo userInfo = userInfoService.selectById(id);
5    return RetResponse.makeOKRsp(userInfo);
6}
7

四:测试

输入localhost:8080/userInfo/selectById

拿到结果

查看数据库System_log

可以看到我们的日志

五:优化

我们知道,日志系统主要是方便我们分析程序,定位异常的。但是对于用户来说,用户一点都不在乎,所以我们不能因为需要记录日志,而延长用户的等待时间,所以,这里我们创建队列来异步执行记录日志操作

1:创建批量添加方法

SystemLogMapper.xml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1<insert id="insertByBatch" parameterType="java.util.List" >
2  insert into system_log (
3    id, description, method, log_type, request_ip, exception_code,
4    exception_detail, params, user_id, create_time
5  )
6  values
7  <foreach collection="list" item="item" index= "index" separator =",">
8    (
9      #{item.id,jdbcType=VARCHAR},
10      #{item.description,jdbcType=VARCHAR},
11      #{item.method,jdbcType=VARCHAR},
12      #{item.logType,jdbcType=VARCHAR},
13      #{item.requestIp,jdbcType=VARCHAR},
14      #{item.exceptionCode,jdbcType=VARCHAR},
15      #{item.exceptionDetail,jdbcType=VARCHAR},
16      #{item.params,jdbcType=VARCHAR},
17      #{item.userId,jdbcType=VARCHAR},
18      #{item.createTime,jdbcType=TIMESTAMP}
19    )
20  </foreach>
21

SystemLogMapper.java


1
2
1Integer insertByBatch(List<SystemLog> list);
2

Service层可参考码云地址,这里就不做示例

2:创建日志的存放队列

创建core→systemlog文件夹

在该文件夹下创建SystemLogQueue


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1package com.example.demo.core.systemlog;
2
3import com.example.demo.model.SystemLog;
4import org.springframework.stereotype.Component;
5
6import java.util.concurrent.BlockingQueue;
7import java.util.concurrent.LinkedBlockingQueue;
8import java.util.concurrent.TimeUnit;
9
10@Component
11public class SystemLogQueue {
12
13    private BlockingQueue<SystemLog> blockingQueue = new LinkedBlockingQueue<>();
14
15    public void add(SystemLog systemLog) {
16        blockingQueue.add(systemLog);
17    }
18
19    public SystemLog poll() throws InterruptedException {
20        return blockingQueue.poll(1, TimeUnit.SECONDS);
21    }
22}
23
24

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
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
1package com.example.demo.core.systemlog;
2
3import com.example.demo.model.SystemLog;
4import com.example.demo.service.SystemLogService;
5import org.slf4j.Logger;
6import org.slf4j.LoggerFactory;
7import org.springframework.stereotype.Component;
8
9import javax.annotation.PostConstruct;
10import javax.annotation.PreDestroy;
11import javax.annotation.Resource;
12import java.util.ArrayList;
13import java.util.List;
14
15@Component
16public class SystemLogConsumer implements Runnable{
17
18    private static Logger logger = LoggerFactory.getLogger(SystemLogConsumer.class);
19    
20    public static final int DEFAULT_BATCH_SIZE = 64;
21    
22    private SystemLogQueue auditLogQueue;
23    
24    private SystemLogService systemLogService;
25    
26    private int batchSize = DEFAULT_BATCH_SIZE;
27    
28    private boolean active = true;
29    
30    private Thread thread;
31
32    @PostConstruct
33    public void init() {
34        thread = new Thread(this);
35        thread.start();
36    }
37
38    @PreDestroy
39    public void close() {
40        active = false;
41    }
42
43    @Override
44    public void run() {
45        while (active) {
46            execute();
47        }
48    }
49
50    public void execute() {
51        List<SystemLog> systemLogs = new ArrayList<>();
52        try {
53            int size = 0;
54            while (size < batchSize) {
55                SystemLog systemLog = auditLogQueue.poll();
56                if (systemLog == null) {
57                    break;
58                }
59                systemLogs.add(systemLog);
60                size++;
61            }
62        } catch (Exception ex) {
63            logger.info(ex.getMessage(), ex);
64        }
65        if (!systemLogs.isEmpty()) {
66            try {
67                //休眠10秒来模拟业务复杂,正在计算,测试之后大家别忘记删除这句话
68                Thread.sleep(10000);
69                systemLogService.insertByBatch(systemLogs);
70            }catch (Exception e){
71                logger.error("异常信息:{}", e.getMessage());
72            }
73
74        }
75    }
76
77    @Resource
78    public void setAuditLogQueue(SystemLogQueue auditLogQueue) {
79        this.auditLogQueue = auditLogQueue;
80    }
81    @Resource
82    public void setAuditLogService(SystemLogService systemLogService) {
83        this.systemLogService = systemLogService;
84    }
85
86    public void setBatchSize(int batchSize) {
87        this.batchSize = batchSize;
88    }
89
90
91
92}
93
94

4:修改切面AspectLog

将:


1
2
3
1@Resourc
2    private SystemLogService systemLogService;
3

修改为:


1
2
3
1@Resource
2private SystemLogQueue systemLogQueue;
3

将:


1
2
1systemLogQueue.insert(systemLog);
2

修改为:


1
2
1systemLogQueue.add(systemLog);
2

5:测试

输入localhost:8080/userInfo/selectById

注意:这里是立刻返回结果,并没有等待10秒,10秒之后打开数据库,得到我们刚操作的日志

给TA打赏
共{{data.count}}人
人已打赏
安全技术

C++ 高性能服务器网络框架设计细节

2022-1-11 12:36:11

安全经验

黑帽和Defcon安全大会回顾:谷歌恶意链接最多

2010-8-2 11:12:22

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索