做javaer2.5年了,,做过手机,玩过网站,看过一些代码,也写过不少代码(好吧确实也不多),总感觉有些代码或者方法在项目并没有什么卵用,,在一个八百年不会用一次的方法上纠结性能/运行速度,有这个必要吗?
也行是觉得写开源框架的人都很屌,然后也想提升一下B格,就这么开始了,,,
嘿,你,对,没错,就是你,现在正在看屏幕的那个你,,,虽然我觉得你肯定是手滑点进来的,但是假如你能看完的话.....也许可以给点建议或者什么的
方案一:
每个方法调用一个公共方法,记录当前堆栈信息,保存方法调用路径
优点:实时,准确,可以记录被调用次数,添加相关说明
缺点: 在现有代码上添加工作量大(除非脑袋抽筋不然绝对不会使用)
方案二:使用jstack 命令抽取当前时刻堆栈信息
优点:与当前已有工程/代码没有关联,只需要知道pid即可
缺点:需要长时间运行,抽样处理,概率处理,不能发现那些方法从来都没有调用过,对于某些不常用的方法就只有抱歉了"sorry,我来的时候没看到你"
个人比较倾向于方案二...别问我为什么
数据结构
采用图论中的无向图
从堆栈信息中可以获取到当前方法的调用路线
A>B>C>D>E
可以这样记录
A | B | C | D | E | |
A | 1 | ||||
B | 1 | ||||
C | 1 | ||||
D | 1 | ||||
E |
A>B>D>C>E
可以这样记录
A | B | C | D | E | |
A | 1 | ||||
B | 1 | ||||
C | 1 | ||||
D | 1 | ||||
E |
最终
A | B | C | D | E | |
A | 2 | ||||
B | 1 | 1 | |||
C | 1 | 1 | |||
D | 1 | 1 | |||
E |
简单说明一下记录的时候 D>E (E,D),C>D (D,C),B>C (C,B) ,A>B (B,A)
以最终图读取,
A>B 2次
B>C,B>D
C>D,C>E
D>C,D>E
看不懂没关系,反正是技术贴
这就完了?没有.当然没有,这鬼东西谁会看!
所以必须有一个很直观的展现,最好是图文形式,可以点点点.比如3Dmax建模应该可以很好的实现,可惜我不会,然后想到了Python,据说可以实现,当我刚好装完环境写完"Holle wold"时"这里发现了一堆bug,快来解决一下",胎死至今....
----------------------
假如你看到这里,并且也有兴趣实现的话,请联系我一起将它完成....或者什么时候当以后的我有这个技术的时候还能有这份....激情?或者什么莫名其妙的东西能把它完成
说实话我都不知道是不是有人已经实现过了
愿喜欢技术的你一直都在
----to me
-------------------------------2022-08-12--填坑------------------------
结果还是采用了方案一,当初年轻没想到切面
核心点:
1aop,和业务逻辑不相干,随意插拔,
2,图数据库,数据存储
3redis,作为队列分离操作,减少对原业务的处理阻塞时间
代码
<dependency> <groupId>com.baidu.hugegraph</groupId> <artifactId>hugegraph-client</artifactId> <version>2.0.1</version> <exclusions> <exclusion> <groupId> org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> </exclusion> </exclusions> </dependency>
package org.springblade;
//public class AfterRunner {
import com.baidu.hugegraph.driver.GraphManager;
import com.baidu.hugegraph.driver.HugeClient;
import com.baidu.hugegraph.driver.SchemaManager;
import com.baidu.hugegraph.structure.graph.Edge;
import com.baidu.hugegraph.structure.graph.Vertex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@Component
@Order(value = 1)
public class AfterRunner implements ApplicationRunner {
String QUERY_NAME = "query_name";
@Autowired
RedisUtilx redisUtil;
@Override
public void run(ApplicationArguments args) {
new Thread(new Runnable() {
@Override
public void run() {
HugeClient hugeClient = HugeClient.builder("http://192.168.0.3:18181",
"hugegraph")
.build();
init(hugeClient);
while (true) {
try {
// 1.从任务队列"queue-producter"中获取一个任务,并将该任务放入暂存队列"tmp-queue"
Bean obj = (Bean) redisUtil.brpop(QUERY_NAME, 0, TimeUnit.SECONDS);
// 2.处理任务----纯属业务逻辑,模拟一下:睡觉
System.out.println(obj.toString());
Vertex src = getVertex(obj.getSrc());
Vertex des = getVertex(obj.getDes());
List<Vertex> vertexs = new ArrayList<>();
vertexs.add(src);
vertexs.add(des);
GraphManager graph = hugeClient.graph();
graph.addVertices(vertexs);
ClazzBean srcbean = getClazzBean(obj.getSrc());
ClazzBean dscbean = getClazzBean(obj.getDes());
Edge edge = getEdge(graph, srcbean, dscbean);
if (edge == null) {
edge = new Edge("relationship").source(src).target(des)
.property("time", 1);
} else {
Integer time = (Integer) edge.property("time");
time++;
edge = edge.property("time", time);
}
List<Edge> edges = new ArrayList<>();
edges.add(edge);
graph.addEdges(edges);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
private Edge getEdge(GraphManager graph, ClazzBean srcbean, ClazzBean dscbean) {
try {
return graph.getEdge("S1:" + srcbean.getKey() + ">1>>S1:" + dscbean.getKey());
}catch (Exception e){
}
return null;
}
private Vertex getVertex(String str) {
// String str = obj.getSrc();
ClazzBean clazzBean = getClazzBean(str);
Vertex src = new Vertex("method")
.property("clazz", clazzBean.getClazz())
.property("returnType", clazzBean.getReturnType())
.property("method", clazzBean.getMethod())
.property("parameter", clazzBean.getParameter())
.property("packageName", clazzBean.getPackageName())
.property("key", clazzBean.getKey());
System.out.println(clazzBean.getKey());
return src;
}
private ClazzBean getClazzBean(String str) {
ClazzBean clazzBean = new ClazzBean();
String[] strs = str.split(" ");
clazzBean.setReturnType(strs[0]);
String other = strs[1];
strs = other.split("\\(");
clazzBean.setParameter(strs[1].replace(")", ""));
other = strs[0];
clazzBean.setMethod(other.substring(other.lastIndexOf(".") + 1));
other = other.substring(0, other.lastIndexOf("."));
clazzBean.setClazz(other.substring(other.lastIndexOf(".") + 1));
clazzBean.setPackageName(other.substring(0, other.lastIndexOf(".")));
return clazzBean;
}
private void init(HugeClient hugeClient) {
List<ClazzBean> list = getClassName();
SchemaManager schema = hugeClient.schema();
schema.propertyKey("key").asText().ifNotExist().create();
schema.propertyKey("clazz").asText().ifNotExist().create();
schema.propertyKey("returnType").asText().ifNotExist().create();
schema.propertyKey("method").asText().ifNotExist().create();
schema.propertyKey("parameter").asText().ifNotExist().create();
schema.propertyKey("packageName").asText().ifNotExist().create();
schema.propertyKey("time").asLong().ifNotExist().create();
schema.vertexLabel("method")
.properties("clazz")
.properties("returnType")
.properties("method")
.properties("parameter")
.properties("packageName")
.properties("key")
.primaryKeys("key")
.ifNotExist()
.create();
// schema.indexLabel("methodByCity")
// .onV("method")
// .by("name")
// .secondary()
// .ifNotExist()
// .create();
schema.edgeLabel("relationship")
.sourceLabel("method")
.targetLabel("method")
.properties("time")
.ifNotExist()
.create();
GraphManager graph = hugeClient.graph();
List<Vertex> vertexList = new ArrayList<>();
for (ClazzBean name : list) {
vertexList.add(new Vertex("method")
.property("clazz", name.getClazz())
.property("returnType", name.getReturnType())
.property("method", name.getMethod())
.property("parameter", name.getParameter())
.property("packageName", name.getPackageName())
.property("key", name.getKey())
);
if (vertexList.size() == 100) {
graph.addVertices(vertexList);
vertexList.clear();
}
}
if (vertexList.size() > 0) {
graph.addVertices(vertexList);
}
}
public List<ClazzBean> getClassName() {
List<ClazzBean> list = new ArrayList<>();
try {
// 获取包名下所有类
Set<Class<?>> classes = getClasses("org.springblade.modules.zyhy");
for (Class c : classes) {
Method[] methods = c.getDeclaredMethods();
;
;
for (Method method : methods) {
String returnType = method.getReturnType().getName();
returnType = returnType.substring(returnType.lastIndexOf(".") == -1 ? 0 : returnType.lastIndexOf(".") + 1);
String naem = "";
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
String parameterName = parameter.getType().getTypeName();
parameterName = parameterName.substring(parameterName.lastIndexOf(".") == -1 ? 0 : parameterName.lastIndexOf(".") + 1);
naem += parameterName + ",";
}
if (!"".equals(naem)) {
naem = naem.substring(0, naem.length() - 1);
}
ClazzBean clazzBean = new ClazzBean();
clazzBean.setParameter(naem);
clazzBean.setClazz(c.getSimpleName());
clazzBean.setReturnType(returnType);
clazzBean.setMethod(method.getName());
clazzBean.setPackageName(c.getPackage().getName());
list.add(clazzBean);
System.out.println(clazzBean.getKey());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
/**
* 根据包名获取包下面所有的类名
*
* @param pack
* @return
* @throws Exception
*/
public static Set<Class<?>> getClasses(String pack) throws Exception {
// 第一个class类的集合
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
//System.out.println("jar类型的扫描");
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes);
} catch (IOException e) {
// log.error("在扫描用户定义视图时从jar包获取文件出错");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用户定义包名 " + packageName + " 下没有任何文件");
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
@Override
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
// classes.add(Class.forName(packageName + '.' +
// className));
// 经过回复同学的提醒,这里用forName有一些不好,会触发static方法,没有使用classLoader的load干净
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
/**
* 以jar的形式来获取包下的所有Class
*
* @param packageName
* @param entries
* @param packageDirName
* @param recursive
* @param classes
*/
private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// .error("添加用户自定义视图类错误 找不到此类的.class文件");
e.printStackTrace();
}
}
}
}
}
}
}
package org.springblade;
import lombok.Data;
import java.io.Serializable;
@Data
public class Bean implements Serializable {
private static final long serialVersionUID = 1L;
String src;
String des;
Bean(String src, String des) {
this.src = src;
this.des = des;
}
@Override
public String toString() {
return "Bean{" +
"src='" + src + '\'' +
", des='" + des + '\'' +
'}';
}
}
package org.springblade;
import lombok.Data;
import java.lang.reflect.Parameter;
@Data
public class ClazzBean {
String clazz;
String returnType;
String method;
String parameter;
String packageName;
@Override
public String toString() {
return returnType+" "+packageName+"."+clazz+"."+method+"("+parameter+")";
}
public String getKey() {
String[] strs= packageName.split("\\.");
String packagestr="";
for (String str : strs) {
if(str.length()>1){
packagestr += str.substring(0,1) + ".";
}else {
packagestr += str + ".";
}
}
packagestr = packagestr.substring(0, packagestr.length() - 1);
strs= parameter.split(",");
String parameterstr="";
for (String str : strs) {
if(str.length()>1){
parameterstr += str.substring(0,1) + ",";
}else {
parameterstr += str + ",";
}
}
if(!"".equals(packagestr)){
parameterstr = parameterstr.substring(0, parameterstr.length() - 1);
}
return returnType.substring(0,1)+" "+packagestr+"."+clazz+method+"("+parameterstr+")_"+toString().hashCode();
}
}
package org.springblade;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
@Aspect
@Component
public class ClazzPortrait {
String QUERY_NAME = "query_name";
@Autowired
RedisUtilx redisUtil;
@Before("execution(* org.springblade.modules..*.*(..))")
// @Before("execution(* org.springblade.modules.zyhy..*.*(..))")
public void Before(JoinPoint point) {
// //System.out.println("Thread.currentThread().getId():-----------------------" + Thread.currentThread().getId());
// //System.out.println( "doAfter>>>>>" + point.getSignature()+"__"+System.nanoTime());
String name = point.getSignature().toString();
String threadId = Thread.currentThread().getId() + "";
//System.out.println("Thread.currentThread().getId():-----------------------" + Thread.currentThread().getId());
//System.out.println("Before >>>>>" + point.getSignature() + "__" + System.nanoTime());
if (map.get(threadId) == null) {
map.put(threadId, new ArrayList<>());
}
List<String> list = map.get(threadId);
list.add(name);
}
@After("execution(* org.springblade.modules..*.*(..))")
// @After("execution(* org.springblade.modules.zyhy..*.*(..))")
// @Before("execution(* org.springblade.modules.zyhy..*.*(..))")
public void After(JoinPoint point) {
//System.out.println("Thread.currentThread().getId():-----------------------" + Thread.currentThread().getId());
//System.out.println( "Before >>>>>" + point.getSignature()+"__"+System.nanoTime());
String name = point.getSignature().toString();
String threadId = Thread.currentThread().getId() + "";
List<String> list = map.get(threadId);
String src = "";
if (list.size() > 1) {
src = list.get(list.size() - 2);
}
if (StringUtils.isNotBlank(src)) {
//System.out.println("src >>>>>" + src + "_des_" +name);
Bean bean = new Bean(src, name);
redisUtil.lpush(QUERY_NAME, bean);
}
list.remove(list.size() - 1);
if (list.isEmpty()) {
map.remove(threadId);
}
}
static Map<String, List<String>> map = new HashMap<>();
}
package org.springblade;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtilx {
@Autowired
private RedisTemplate redisTemplate;
public final static String QUEUE_NAME = "redis_delay_queue";
//将 {value} 添加到 {key} 处的排序集合,或者如果它已经存在则更新其 {score}
public <V>void opsForZsetAdd(String key, V value, double score){
redisTemplate.opsForZSet().add(key, value, score);
}
public Set<JSONObject> opsForZSetrangeByScore(String key, double min, double max){
return redisTemplate.opsForZSet().rangeByScore(key, min, max);
}
// 获取zset中指定key的score
public Double opsForZSetScore(String key, Object score) {
return redisTemplate.opsForZSet().score(key, score);
}
public void sSetAndTime(String key, long time, Object... values) {
try {
this.redisTemplate.opsForValue().set(key, values);
if (time > 0L) {
this.expire(key, time);
}
} catch (Exception var6) {
var6.printStackTrace();
}
}
public boolean expire(String key, long time) {
try {
if (time > 0L) {
this.redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception var5) {
var5.printStackTrace();
return false;
}
}
/**
* 取值 - <brpop:阻塞式> - 推荐使用
* @param key 键
* @param timeout 超时时间
* @param timeUnit 给定单元粒度的时间段
* TimeUnit.DAYS //天
* TimeUnit.HOURS //小时
* TimeUnit.MINUTES //分钟
* TimeUnit.SECONDS //秒
* TimeUnit.MILLISECONDS //毫秒
* @return
*/
public Object brpop(String key, long timeout, TimeUnit timeUnit) {
try {
return redisTemplate.opsForList().rightPop(key, timeout, timeUnit);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/** ---------------------------------- redis消息队列 ---------------------------------- */
/**
* 存值
* @param key 键
* @param value 值
* @return
*/
public boolean lpush(String key, Object value) {
try {
redisTemplate.opsForList().leftPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
// zset删除元素
public void opsForZSetRemove(String key, Object value) {
redisTemplate.opsForZSet().remove(key, value);
}
}
结果查看