一个java程序/项目检测工具.方法是否被调用以及频率

做javaer2.5年了,,做过手机,玩过网站,看过一些代码,也写过不少代码(好吧确实也不多),总感觉有些代码或者方法在项目并没有什么卵用,,在一个八百年不会用一次的方法上纠结性能/运行速度,有这个必要吗?


也行是觉得写开源框架的人都很屌,然后也想提升一下B格,就这么开始了,,,


嘿,你,对,没错,就是你,现在正在看屏幕的那个你,,,虽然我觉得你肯定是手滑点进来的,但是假如你能看完的话.....也许可以给点建议或者什么的


方案一:
每个方法调用一个公共方法,记录当前堆栈信息,保存方法调用路径
优点:实时,准确,可以记录被调用次数,添加相关说明
缺点: 在现有代码上添加工作量大(除非脑袋抽筋不然绝对不会使用)
方案二:使用jstack 命令抽取当前时刻堆栈信息
优点:与当前已有工程/代码没有关联,只需要知道pid即可
缺点:需要长时间运行,抽样处理,概率处理,不能发现那些方法从来都没有调用过,对于某些不常用的方法就只有抱歉了"sorry,我来的时候没看到你"


个人比较倾向于方案二...别问我为什么


数据结构
采用图论中的无向图
从堆栈信息中可以获取到当前方法的调用路线


A>B>C>D>E

可以这样记录

ABCDE
A1
B1
C1
D1
E

A>B>D>C>E

可以这样记录

ABCDE
A1
B1
C1
D1
E

最终

ABCDE
A2
B11
C11
D11
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);
	}
}


结果查看

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值