package com.yourpackage;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.SmartLifecycle;
import java.util.HashSet;
import java.util.Set;
public class SpringVisualizer implements ApplicationContextAware, SmartLifecycle {
private ConfigurableApplicationContext startContext;
private boolean isRunning = false;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.startContext = (ConfigurableApplicationContext) applicationContext;
}
@Override
public void start() {
System.out.println("\n=== GENERATING CLEAN PROPERTY GRAPH ===");
generateCleanHtmlGraph();
this.isRunning = true;
}
private void generateCleanHtmlGraph() {
StringBuilder dot = new StringBuilder("digraph G {\n");
dot.append(" rankdir=LR; node [shape=plain, fontname=\"Arial\", fontsize=10];\n\n");
Set<String> processed = new HashSet<>();
ApplicationContext current = this.startContext;
while (current != null) {
ConfigurableListableBeanFactory factory = ((ConfigurableApplicationContext) current).getBeanFactory();
for (String name : factory.getBeanDefinitionNames()) {
if (processed.add(name)) {
BeanDefinition def = factory.getBeanDefinition(name);
String color = "#E1F5FE";
if (name.toLowerCase().contains("comet")) color = "#FFF9C4";
if (name.toLowerCase().contains("cpls")) color = "#C8E6C9";
dot.append(String.format(" \"%s\" [label=<<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\" BGCOLOR=\"%s\">\n", name, color));
dot.append(String.format(" <TR><TD COLSPAN=\"2\"><B>%s</B></TD></TR>\n", xmlEscape(name)));
PropertyValue[] pvs = def.getPropertyValues().getPropertyValues();
for (PropertyValue pv : pvs) {
String cleanVal = extractValue(pv.getValue());
if (cleanVal.length() > 60) cleanVal = cleanVal.substring(0, 57) + "...";
dot.append(" <TR>");
dot.append("<TD ALIGN=\"LEFT\">").append(xmlEscape(pv.getName())).append("</TD>");
dot.append("<TD ALIGN=\"LEFT\">").append(xmlEscape(cleanVal)).append("</TD>");
dot.append("</TR>\n");
}
dot.append(" </TABLE>>];\n");
for (String dep : factory.getDependenciesForBean(name)) {
dot.append(String.format(" \"%s\" -> \"%s\";\n", name, dep));
}
}
}
current = current.getParent();
}
dot.append("}\n");
System.out.println("----- COPY START -----\n" + dot.toString() + "\n----- COPY END -----");
}
/**
* Logic to strip out the Spring metadata and return just the value or reference
*/
private String extractValue(Object value) {
if (value == null) return "null";
// If it's a pointer to another bean, just show the name
if (value instanceof BeanReference) {
return "@" + ((BeanReference) value).getBeanName();
}
// If it's the TypedStringValue you're seeing in the screenshot, pull out the raw string
if (value instanceof TypedStringValue) {
String val = ((TypedStringValue) value).getValue();
return val != null ? val : "null";
}
// Otherwise (int, double, float, String), just return string representation
return value.toString();
}
private String xmlEscape(String input) {
if (input == null) return "null";
return input.replace("&", "&").replace("<", "<").replace(">", ">")
.replace("\"", """).replace("'", "'").replace("\n", " ");
}
@Override public int getPhase() { return Integer.MAX_VALUE; }
@Override public boolean isAutoStartup() { return true; }
@Override public void stop() { this.isRunning = false; }
@Override public boolean isRunning() { return this.isRunning; }
@Override public void stop(Runnable callback) { stop(); callback.run(); }
}
For further actions, you may consider blocking this person and/or reporting abuse
Top comments (0)