我有一个带有大型弹簧上下文的应用程序,它加载了很多开发人员编写的bean
一些bean可能会对其初始化代码进行一些重要的处理,这可能需要很长时间
我正在寻找一种简单的方法来获得每个bean的加载时间
由于软件在大量客户的机器上运行,我需要一种方法在日志中轻松找到瓶颈bean。
如果我可以注册诸如“在加载bean之前”之类的事件,之后它就会很棒
因此,如果我能够有问题地得到这些数据,我可以编写如下内容:
if (beanLoadingTime > 2 seconds)
print bean details and loading time to log file
这就是为什么启用日志记录或profilng是不够的。
答案 0 :(得分:2)
不知道我的解决方案是否能帮助你,但这就是我所做的,因为我需要类似的东西。
首先,我们需要记录两件事,实例化时间和初始化时间。首先,我只为包“org.springframework.beans.factory”启用日志记录,使用%d {mm:ss,SSS}%m%n作为模式(仅限时间和消息)。 Spring记录消息,如:创建bean的实例...和完成创建bean的实例...第二件事我按照this answer中的建议创建了一个LoggerBeanPostProcessor。代码是:
public class LoggerBeanPostProcessor implements BeanPostProcessor, Ordered {
protected Log logger = LogFactory.getLog("org.springframework.beans.factory.LoggerBeanPostProcessor");
private Map<String, Long> start;
private Map<String, Long> end;
public LoggerBeanPostProcessor() {
start = new HashMap<>();
end = new HashMap<>();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
start.put(beanName, System.currentTimeMillis());
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
end.put(beanName, System.currentTimeMillis());
logger.debug("Init time for " + beanName + ": " + initializationTime(beanName));
return bean;
}
@Override
public int getOrder() {
return Integer.MAX_VALUE;
}
// this method returns initialization time of the bean.
public long initializationTime(String beanName) {
return end.get(beanName) - start.get(beanName);
}
}
我在log4j配置中使用了文件appender。然后我写了一个简单的代码来解析这些信息并得到每个东西的毫秒并总结它们:
public static void main(String[] argumentos) throws Exception{
File file = new File("C:\\app\\daily.log");
List<String> lines = FileUtils.readLines(file);
Map<String,Long> start = new HashMap();
Map<String,Long> end = new HashMap();
Map<String,Long> init = new HashMap();
List<String> beans = new ArrayList();
int max = 0;
for(String line : lines) {
String time = StringUtils.substring(line, 0, 9);
String msg = StringUtils.substring(line, 10);
if(msg.startsWith("Creating instance")) {
int fi = StringUtils.indexOf(msg, '\'') + 1;
int li = StringUtils.lastIndexOf(msg, '\'');
String bean = StringUtils.substring(msg, fi, li);
if(start.containsKey(bean)) {
continue;
}
start.put(bean, parseTime(time));
beans.add(bean);
max = Math.max(max, bean.length());
} else if(msg.startsWith("Finished creating")) {
int fi = StringUtils.indexOf(msg, '\'') + 1;
int li = StringUtils.lastIndexOf(msg, '\'');
String bean = StringUtils.substring(msg, fi, li);
if(end.containsKey(bean)) {
continue;
}
end.put(bean, parseTime(time));
} else if(msg.startsWith("Init time for")) {
int li = StringUtils.lastIndexOf(msg, ':');
String bean = StringUtils.substring(msg, 14, li);
if(init.containsKey(bean)) {
continue;
}
init.put(bean, Long.parseLong(StringUtils.substring(msg, li+2)));
}
}
for(String bean : beans) {
long s = start.get(bean);
long e = end.get(bean);
long i = init.containsKey(bean) ? init.get(bean) : -1;
System.out.println(StringUtils.leftPad(bean, max) + ": " + StringUtils.leftPad(Long.toString((e-s)+i), 6, ' '));
}
}
导致:
splashScreen: 172
org.springframework.aop.config.internalAutoProxyCreator: 31
loggerBeanPostProcessor: 1137
appContext: 1122
希望这对你有所帮助。
答案 1 :(得分:1)
要查找Java代码中的性能瓶颈,请使用分析器。
分析器将测量每个被分析方法所花费的时间,包括方法本身,以及方法的总和加上它所做的每次调用。通常,在类或包级别启用它的分析,例如如果您的代码位于com.example
包或子包中,则指定该代码,并且探查器将监视您的代码,而不会浪费时间监视Spring代码和Java运行时库。
取决于您的IDE,可能已经内置,或者可以作为扩展/插件使用。
<强>更新强>
要挂钩Spring容器bean实例化过程,BeanPostProcessor可能是解决方案。引用的描述包括以下内容:
[...]对于容器创建的每个bean实例,后处理器从容器初始化方法之前的中获取回调容器(例如InitializingBean的
afterPropertiesSet()
任何bean初始化回调之后都会调用和任何声明的init方法)。后处理器可以对bean实例执行任何操作,包括完全忽略回调。