From 218e4908c8dcd716ade9a2544ca8fbca63b6dca6 Mon Sep 17 00:00:00 2001
From: huoyo <1729913829@qq.com>
Date: Mon, 16 Aug 2021 23:03:44 +0800
Subject: [PATCH] add a stack to optimize the code about how to get parent
method
---
pom.xml | 2 +-
.../langpy/kotime/config/DefaultConfig.java | 110 +++++++++++-------
.../cn/langpy/kotime/config/LoadConfig.java | 75 ++++++++++++
.../kotime/controller/KoTimeController.java | 15 ++-
.../cn/langpy/kotime/data/MemoryBase.java | 7 +-
.../langpy/kotime/handler/RunTimeHandler.java | 9 +-
.../langpy/kotime/service/InvokeService.java | 27 +++--
.../java/cn/langpy/kotime/util/Common.java | 36 ------
.../java/cn/langpy/kotime/util/Context.java | 12 +-
.../cn/langpy/kotime/util/MethodStack.java | 39 +++++++
10 files changed, 221 insertions(+), 111 deletions(-)
create mode 100644 src/main/java/cn/langpy/kotime/util/MethodStack.java
diff --git a/pom.xml b/pom.xml
index 3146cde..4a66692 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
cn.langpy
ko-time
- 2.0.0
+ 2.0.1-SNAPSHOT
koTime
koTime
diff --git a/src/main/java/cn/langpy/kotime/config/DefaultConfig.java b/src/main/java/cn/langpy/kotime/config/DefaultConfig.java
index 7265647..59de3e2 100644
--- a/src/main/java/cn/langpy/kotime/config/DefaultConfig.java
+++ b/src/main/java/cn/langpy/kotime/config/DefaultConfig.java
@@ -1,64 +1,30 @@
package cn.langpy.kotime.config;
-import cn.langpy.kotime.handler.RunTimeHandler;
-import cn.langpy.kotime.model.KoTimeConfig;
-import cn.langpy.kotime.util.Context;
-import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import javax.annotation.PostConstruct;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
-@Configuration
+@Component
+@ConfigurationProperties(prefix = "ko-time")
public class DefaultConfig {
- @Value("${koTime.enable:true}")
- private Boolean kotimeEnable;
- @Value("${koTime.log.language:chinese}")
+ private Boolean enable;
private String logLanguage;
- @Value("${koTime.log.enable:false}")
private Boolean logEnable;
- @Value("${koTime.time.threshold:800.0}")
- private Double timeThreshold;
- @Value("${koTime.pointcut:execution(* cn.langpy.kotime.controller.KoTimeController.*(..))}")
+ private Double threshold;
private String pointcut;
- @Value("${koTime.exception.enable:false}")
private Boolean exceptionEnable;
- @Value("${koTime.save.saver:memory}")
private String saveSaver;
- @Value("${koTime.save.async:false}")
private Boolean saveAsync;
- @Value("${koTime.save.thread-num:4}")
private Integer threadNum;
+ private String uiTemplate;
-
- @PostConstruct
- public void function() {
- KoTimeConfig config = new KoTimeConfig();
- config.setLogEnable(logEnable);
- config.setLogLanguage(logLanguage);
- config.setTimeThreshold(timeThreshold);
- config.setExceptionEnable(exceptionEnable);
- config.setDataSaver(saveSaver);
- config.setKotimeEnable(kotimeEnable);
- Context.setConfig(config);
+ public Boolean getEnable() {
+ return enable;
}
- @Bean
- public AspectJExpressionPointcutAdvisor configurabledvisor() {
- AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
- advisor.setExpression(pointcut);
- advisor.setAdvice(new RunTimeHandler());
- return advisor;
- }
-
- public Double getTimeThreshold() {
- return timeThreshold;
- }
-
- public void setTimeThreshold(Double timeThreshold) {
- this.timeThreshold = timeThreshold;
+ public void setEnable(Boolean enable) {
+ this.enable = enable;
}
public String getLogLanguage() {
@@ -77,5 +43,59 @@ public class DefaultConfig {
this.logEnable = logEnable;
}
+ public Double getThreshold() {
+ return threshold;
+ }
+ public void setThreshold(Double threshold) {
+ this.threshold = threshold;
+ }
+
+ public String getPointcut() {
+ return pointcut;
+ }
+
+ public void setPointcut(String pointcut) {
+ this.pointcut = pointcut;
+ }
+
+ public Boolean getExceptionEnable() {
+ return exceptionEnable;
+ }
+
+ public void setExceptionEnable(Boolean exceptionEnable) {
+ this.exceptionEnable = exceptionEnable;
+ }
+
+ public String getSaveSaver() {
+ return saveSaver;
+ }
+
+ public void setSaveSaver(String saveSaver) {
+ this.saveSaver = saveSaver;
+ }
+
+ public Boolean getSaveAsync() {
+ return saveAsync;
+ }
+
+ public void setSaveAsync(Boolean saveAsync) {
+ this.saveAsync = saveAsync;
+ }
+
+ public Integer getThreadNum() {
+ return threadNum;
+ }
+
+ public void setThreadNum(Integer threadNum) {
+ this.threadNum = threadNum;
+ }
+
+ public String getUiTemplate() {
+ return uiTemplate;
+ }
+
+ public void setUiTemplate(String uiTemplate) {
+ this.uiTemplate = uiTemplate;
+ }
}
diff --git a/src/main/java/cn/langpy/kotime/config/LoadConfig.java b/src/main/java/cn/langpy/kotime/config/LoadConfig.java
index 1e25554..231a1f7 100644
--- a/src/main/java/cn/langpy/kotime/config/LoadConfig.java
+++ b/src/main/java/cn/langpy/kotime/config/LoadConfig.java
@@ -1,7 +1,82 @@
package cn.langpy.kotime.config;
+import cn.langpy.kotime.handler.RunTimeHandler;
+import cn.langpy.kotime.util.Common;
+import cn.langpy.kotime.util.Context;
+import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.logging.Logger;
@ComponentScan("cn.langpy.kotime")
+@Configuration
public class LoadConfig {
+ public static Logger log = Logger.getLogger(Common.class.toString());
+ @Value("${koTime.enable:true}")
+ private Boolean kotimeEnable;
+ @Value("${koTime.log.language:chinese}")
+ private String logLanguage;
+ @Value("${koTime.log.enable:false}")
+ private Boolean logEnable;
+ @Value("${koTime.time.threshold:800.0}")
+ private Double timeThreshold;
+ @Value("${koTime.pointcut:execution(* cn.langpy.kotime.controller.KoTimeController.*(..))}")
+ private String pointcut;
+ @Value("${koTime.exception.enable:false}")
+ private Boolean exceptionEnable;
+ @Value("${koTime.ui.template}")
+ private String uiTemplate;
+ @Value("${koTime.save.saver:memory}")
+ private String saveSaver;
+ @Value("${koTime.save.async:false}")
+ private Boolean saveAsync;
+ @Value("${koTime.save.thread-num:4}")
+ private Integer threadNum;
+
+ @Resource
+ private DefaultConfig defaultConfig;
+
+
+ @PostConstruct
+ public void function() {
+ getUiTemplate();
+ DefaultConfig config = new DefaultConfig();
+ config.setLogEnable(defaultConfig.getLogEnable()==null?logEnable:defaultConfig.getLogEnable());
+ config.setLogLanguage(defaultConfig.getLogLanguage()==null?logLanguage:defaultConfig.getLogLanguage());
+ config.setThreshold(defaultConfig.getThreshold()==null?timeThreshold:defaultConfig.getThreshold());
+ config.setExceptionEnable(defaultConfig.getExceptionEnable()==null?exceptionEnable:defaultConfig.getExceptionEnable());
+ config.setSaveSaver(defaultConfig.getSaveSaver()==null?saveSaver:defaultConfig.getSaveSaver());
+ config.setEnable(defaultConfig.getEnable()==null?kotimeEnable:defaultConfig.getEnable());
+ config.setUiTemplate(defaultConfig.getUiTemplate()==null?(uiTemplate==null?getUiTemplate():uiTemplate):defaultConfig.getUiTemplate());
+ Context.setConfig(config);
+ }
+
+ public String getUiTemplate() {
+ try {
+ LoadConfig.class.getClassLoader().loadClass("freemarker.template.Configuration");
+ log.info("loaded freemarker");
+ return "freemarker";
+ } catch (ClassNotFoundException e) {
+ }
+ try {
+ LoadConfig.class.getClassLoader().loadClass("org.thymeleaf.Thymeleaf");
+ log.info("loaded thymeleaf");
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("cannot find any ui-template,please add spring-boot-starter-freemarker(or thymeleaf) to pom.xml ");
+ }
+ return "thymeleaf";
+ }
+
+ @Bean
+ public AspectJExpressionPointcutAdvisor configurabledvisor() {
+ AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
+ advisor.setExpression(defaultConfig.getPointcut()==null?pointcut:defaultConfig.getPointcut());
+ advisor.setAdvice(new RunTimeHandler());
+ return advisor;
+ }
}
diff --git a/src/main/java/cn/langpy/kotime/controller/KoTimeController.java b/src/main/java/cn/langpy/kotime/controller/KoTimeController.java
index d87b369..20b8f24 100644
--- a/src/main/java/cn/langpy/kotime/controller/KoTimeController.java
+++ b/src/main/java/cn/langpy/kotime/controller/KoTimeController.java
@@ -1,9 +1,9 @@
package cn.langpy.kotime.controller;
+import cn.langpy.kotime.config.DefaultConfig;
import cn.langpy.kotime.model.*;
import cn.langpy.kotime.service.GraphService;
import cn.langpy.kotime.util.Context;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@@ -14,8 +14,7 @@ import java.util.List;
@Controller
@RequestMapping("/koTime")
public class KoTimeController {
- @Value("${koTime.ui.template:freemarker}")
- private String showTemplate;
+
@GetMapping
public String index(Model model) {
@@ -29,7 +28,7 @@ public class KoTimeController {
model.addAttribute("system", system);
model.addAttribute("config", Context.getConfig());
String template = "index-freemarker";
- if ("thymeleaf".equals(showTemplate)) {
+ if ("thymeleaf".equals(Context.getConfig().getUiTemplate())) {
template = "index-thymeleaf";
}
return template;
@@ -37,7 +36,7 @@ public class KoTimeController {
@GetMapping("/getConfig")
@ResponseBody
- public KoTimeConfig getConfig() {
+ public DefaultConfig getConfig() {
return Context.getConfig();
}
@@ -84,9 +83,9 @@ public class KoTimeController {
@PostMapping("/updateConfig")
@ResponseBody
public boolean updateConfig(@RequestBody KoTimeConfig config) {
- KoTimeConfig koTimeConfig = Context.getConfig();
+ DefaultConfig koTimeConfig = Context.getConfig();
if (config.getKotimeEnable()!=null) {
- koTimeConfig.setKotimeEnable(config.getKotimeEnable());
+ koTimeConfig.setEnable(config.getKotimeEnable());
}
if (config.getExceptionEnable()!=null) {
koTimeConfig.setExceptionEnable(config.getExceptionEnable());
@@ -95,7 +94,7 @@ public class KoTimeController {
koTimeConfig.setLogEnable(config.getLogEnable());
}
if (config.getTimeThreshold()!=null) {
- koTimeConfig.setTimeThreshold(config.getTimeThreshold());
+ koTimeConfig.setThreshold(config.getTimeThreshold());
}
return true;
}
diff --git a/src/main/java/cn/langpy/kotime/data/MemoryBase.java b/src/main/java/cn/langpy/kotime/data/MemoryBase.java
index 78862d9..de28925 100644
--- a/src/main/java/cn/langpy/kotime/data/MemoryBase.java
+++ b/src/main/java/cn/langpy/kotime/data/MemoryBase.java
@@ -31,6 +31,9 @@ public class MemoryBase implements GraphService {
@Override
public void addMethodNode(MethodNode methodNode) {
+ if (null==methodNode) {
+ return;
+ }
if (!methodNodes.containsKey(methodNode.getId())) {
methodNodes.put(methodNode.getId(), methodNode);
}
@@ -200,9 +203,9 @@ public class MemoryBase implements GraphService {
if (null == controllerApis || controllerApis.size() == 0) {
return systemStatistic;
}
- int delayNum = (int) controllerApis.stream().filter(controllerApi -> controllerApi.getAvgRunTime() >= Context.getConfig().getTimeThreshold()).count();
+ int delayNum = (int) controllerApis.stream().filter(controllerApi -> controllerApi.getAvgRunTime() >= Context.getConfig().getThreshold()).count();
systemStatistic.setDelayNum(delayNum);
- int normalNum = (int) controllerApis.stream().filter(controllerApi -> controllerApi.getAvgRunTime() < Context.getConfig().getTimeThreshold()).count();
+ int normalNum = (int) controllerApis.stream().filter(controllerApi -> controllerApi.getAvgRunTime() < Context.getConfig().getThreshold()).count();
systemStatistic.setNormalNum(normalNum);
int totalNum = (int) controllerApis.stream().count();
systemStatistic.setTotalNum(totalNum);
diff --git a/src/main/java/cn/langpy/kotime/handler/RunTimeHandler.java b/src/main/java/cn/langpy/kotime/handler/RunTimeHandler.java
index 2ce8127..98e755f 100644
--- a/src/main/java/cn/langpy/kotime/handler/RunTimeHandler.java
+++ b/src/main/java/cn/langpy/kotime/handler/RunTimeHandler.java
@@ -5,6 +5,7 @@ import cn.langpy.kotime.service.GraphService;
import cn.langpy.kotime.model.MethodNode;
import cn.langpy.kotime.service.InvokeService;
import cn.langpy.kotime.util.Context;
+import cn.langpy.kotime.util.MethodStack;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@@ -12,13 +13,15 @@ public class RunTimeHandler implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
- boolean kotimeEnable = Context.getConfig().getKotimeEnable();
+ boolean kotimeEnable = Context.getConfig().getEnable();
if (!kotimeEnable) {
return invocation.proceed();
}
boolean exceptionEnable = Context.getConfig().getExceptionEnable();
long begin = System.nanoTime();
Object obj = null;
+ MethodNode parent = InvokeService.getParentMethodNode();
+ MethodStack.record(invocation);
if (exceptionEnable) {
try {
obj = invocation.proceed();
@@ -36,19 +39,19 @@ public class RunTimeHandler implements MethodInterceptor {
graphService.addExceptionNode(exception);
graphService.addExceptionRelation(current, exception);
}
+ MethodStack.clear();
throw e;
}
} else {
obj = invocation.proceed();
}
long end = System.nanoTime();
- String packName = invocation.getMethod().getDeclaringClass().getPackage().getName();
- MethodNode parent = InvokeService.getParentMethodNode(packName);
MethodNode current = InvokeService.getCurrentMethodNode(invocation, ((end - begin) / 1000000.0));
GraphService graphService = GraphService.getInstance();
graphService.addMethodNode(parent);
graphService.addMethodNode(current);
graphService.addMethodRelation(parent, current);
+ MethodStack.clear();
return obj;
}
}
diff --git a/src/main/java/cn/langpy/kotime/service/InvokeService.java b/src/main/java/cn/langpy/kotime/service/InvokeService.java
index 6f49f9b..91a004d 100644
--- a/src/main/java/cn/langpy/kotime/service/InvokeService.java
+++ b/src/main/java/cn/langpy/kotime/service/InvokeService.java
@@ -1,27 +1,34 @@
package cn.langpy.kotime.service;
-
import cn.langpy.kotime.model.MethodNode;
import cn.langpy.kotime.util.Common;
+import cn.langpy.kotime.util.MethodStack;
+import cn.langpy.kotime.util.MethodType;
import org.aopalliance.intercept.MethodInvocation;
import java.math.BigDecimal;
+import java.util.Stack;
import java.util.logging.Logger;
public class InvokeService {
public static Logger log = Logger.getLogger(InvokeService.class.toString());
-
- public static MethodNode getParentMethodNode(String packName) {
- String parentClassName = "";
- String parentMothodName = "";
- StackTraceElement[] stacks = Thread.currentThread().getStackTrace();
- StackTraceElement stack = Common.filter(stacks, packName);
- if (stack != null) {
- parentClassName = stack.getClassName();
- parentMothodName = stack.getMethodName();
+ public static MethodNode getParentMethodNode() {
+ Stack stack = MethodStack.get();
+ if (null==stack) {
+ MethodNode parent = new MethodNode();
+ parent.setId("com.langpy.kotime.Cotroller.dispatch");
+ parent.setClassName("Cotroller");
+ parent.setMethodName("dispatch");
+ parent.setName("Cotroller.dispatch");
+ parent.setMethodType(MethodType.Others);
+ return parent;
}
+ String classMethod = stack.peek();
+ String[] classMethodSplit = classMethod.split("#");
+ String parentClassName = classMethodSplit[0];
+ String parentMothodName = classMethodSplit[1];
MethodNode parent = new MethodNode();
parent.setId(parentClassName + "." + parentMothodName);
parent.setClassName(parentClassName);
diff --git a/src/main/java/cn/langpy/kotime/util/Common.java b/src/main/java/cn/langpy/kotime/util/Common.java
index 08a0b63..02c73d2 100644
--- a/src/main/java/cn/langpy/kotime/util/Common.java
+++ b/src/main/java/cn/langpy/kotime/util/Common.java
@@ -2,7 +2,6 @@ package cn.langpy.kotime.util;
import cn.langpy.kotime.model.MethodRelation;
import org.aopalliance.intercept.MethodInvocation;
-import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
@@ -13,19 +12,6 @@ import java.util.logging.Logger;
public class Common {
public static Logger log = Logger.getLogger(Common.class.toString());
- public static StackTraceElement filter(StackTraceElement[] stacks, String packName) {
- String[] packNameSplit = packName.split("\\.");
- String filter = packNameSplit.length > 1 ? packNameSplit[0] + "." + packNameSplit[1] : packNameSplit[0];
- int stacksLength = stacks.length;
- for (int i = 0; i < stacksLength; i++) {
- StackTraceElement stack = stacks[i];
- if (stack.getClassName().startsWith(filter) && !stack.getClassName().contains("$")) {
- return stack;
- }
- }
- return null;
- }
-
public static MethodType getMethodType(MethodInvocation pjp) {
Class> targetClass = pjp.getThis().getClass();
@@ -47,28 +33,6 @@ public class Common {
return MethodType.Others;
}
}
-
- public static MethodType getMethodType(ProceedingJoinPoint pjp) {
- Class> targetClass = pjp.getTarget().getClass();
- if (targetClass.getAnnotation(Controller.class) != null || targetClass.getAnnotation(RestController.class) != null) {
- return MethodType.Controller;
- } else if (targetClass.getAnnotation(Service.class) != null) {
- return MethodType.Service;
- } else if (targetClass.getAnnotation(Repository.class) != null) {
- return MethodType.Dao;
- }
- String className = pjp.getTarget().getClass().getName().toLowerCase();
- if (className.contains("controller")) {
- return MethodType.Controller;
- } else if (className.contains("service")) {
- return MethodType.Service;
- } else if (className.contains("dao") || className.contains("mapper") || className.contains("com.sun.proxy.$Proxy")) {
- return MethodType.Dao;
- } else {
- return MethodType.Others;
- }
- }
-
public static MethodType getMethodType(String className) {
className = className.toLowerCase();
if (className.contains("controller")) {
diff --git a/src/main/java/cn/langpy/kotime/util/Context.java b/src/main/java/cn/langpy/kotime/util/Context.java
index 0db325f..c396a04 100644
--- a/src/main/java/cn/langpy/kotime/util/Context.java
+++ b/src/main/java/cn/langpy/kotime/util/Context.java
@@ -1,23 +1,23 @@
package cn.langpy.kotime.util;
-import cn.langpy.kotime.model.KoTimeConfig;
+import cn.langpy.kotime.config.DefaultConfig;
public class Context {
- private static KoTimeConfig config;
+ private static DefaultConfig config;
static {
- config = new KoTimeConfig();
+ config = new DefaultConfig();
config.setLogEnable(false);
- config.setKotimeEnable(true);
+ config.setEnable(true);
config.setLogLanguage("chinese");
}
- public static void setConfig(KoTimeConfig koTimeConfig) {
+ public static void setConfig(DefaultConfig koTimeConfig) {
config = koTimeConfig;
}
- public static KoTimeConfig getConfig() {
+ public static DefaultConfig getConfig() {
return config;
}
diff --git a/src/main/java/cn/langpy/kotime/util/MethodStack.java b/src/main/java/cn/langpy/kotime/util/MethodStack.java
new file mode 100644
index 0000000..105499e
--- /dev/null
+++ b/src/main/java/cn/langpy/kotime/util/MethodStack.java
@@ -0,0 +1,39 @@
+package cn.langpy.kotime.util;
+
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringStack;
+import org.aopalliance.intercept.MethodInvocation;
+
+import java.util.Stack;
+
+public class MethodStack {
+
+ private static ThreadLocal threadMethods = new ThreadLocal<>();
+
+ public static void record(MethodInvocation pjp) {
+ String className = pjp.getMethod().getDeclaringClass().getName();
+ String methodName = pjp.getMethod().getName();
+ Stack queue = null;
+ if (null==threadMethods.get()) {
+ queue = new StringStack();
+ }else {
+ queue = threadMethods.get();
+ }
+ queue.add(className+"#"+methodName);
+ threadMethods.set(queue);
+ }
+
+ public static Stack get() {
+ return threadMethods.get();
+ }
+
+ public static void clear() {
+ Stack queue = threadMethods.get();
+ if (queue==null) {
+ return;
+ }
+ queue.pop();
+ if (queue.isEmpty()) {
+ threadMethods.remove();
+ }
+ }
+}