import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
/**
- Scans the JVM object graph to find a Spring ApplicationContext that contains a given bean name.
-
No external libraries used.
*/
public class SpringContextScanner {private static final int MAX_OBJECTS_TO_VISIT = 10000; // safety cap
private static final int MAX_DEPTH = 8;/**
- Attempts to find a Spring bean with the given name by scanning reachable objects in memory.
-
Returns the bean instance if found, or null otherwise.
*/
public static Object findBeanByScanningAll(String beanName) {
try {
final Queue queue = new ArrayDeque<>();
final Set rootsSeen = Collections.newSetFromMap(new IdentityHashMap<>());// Step 1: Gather root objects from static fields of discovered classes Set<Class<?>> discoveredClasses = new HashSet<>(); // From thread stack traces Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces(); for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) { for (StackTraceElement element : entry.getValue()) { try { Class<?> cls = Class.forName(element.getClassName(), false, Thread.currentThread().getContextClassLoader()); discoveredClasses.add(cls); } catch (Throwable ignored) {} } } // (Optional) Add known root classes manually, if applicable // discoveredClasses.add(MyMainAppClass.class); // Add static field values to queue for (Class<?> cls : discoveredClasses) { try { for (Field field : cls.getDeclaredFields()) { if (!Modifier.isStatic(field.getModifiers())) continue; try { field.setAccessible(true); Object value = field.get(null); if (value != null && rootsSeen.add(value)) { queue.add(value); } } catch (Throwable ignored) {} } } catch (Throwable ignored) {} } // Step 2: Also inspect non-static fields of all Threads (can hold ApplicationContexts) try { for (Thread t : traces.keySet()) { try { for (Field f : t.getClass().getDeclaredFields()) { if (Modifier.isStatic(f.getModifiers())) continue; f.setAccessible(true); Object val = null; try { val = f.get(t); } catch (Throwable ignored) {} if (val != null && rootsSeen.add(val)) { queue.add(val); } } } catch (Throwable ignored) {} } } catch (Throwable ignored) {} // Step 3: BFS object graph traversal final Set<Object> visited = Collections.newSetFromMap(new IdentityHashMap<>()); int visitedCount = 0; Map<Object, Integer> objDepth = new IdentityHashMap<>(); while (!queue.isEmpty() && visitedCount < MAX_OBJECTS_TO_VISIT) { Object cur = queue.poll(); if (cur == null || !visited.add(cur)) continue; visitedCount++; int curDepth = objDepth.getOrDefault(cur, 0); // Check if it's an ApplicationContext and contains the bean if (isApplicationContext(cur)) { try { Method contains = cur.getClass().getMethod("containsBean", String.class); Object containsRes = contains.invoke(cur, beanName); if (Boolean.TRUE.equals(containsRes)) { Method getBean = cur.getClass().getMethod("getBean", String.class); Object bean = getBean.invoke(cur, beanName); System.out.println("Found bean in ApplicationContext instance: " + cur); return bean; } } catch (NoSuchMethodException nsme) { try { Method containsMethod = findMethodByName(cur.getClass(), "containsBean"); if (containsMethod != null) { Object containsRes = containsMethod.invoke(cur, beanName); if (Boolean.TRUE.equals(containsRes)) { Method getBeanMethod = findMethodByName(cur.getClass(), "getBean"); if (getBeanMethod != null) { Object bean = getBeanMethod.invoke(cur, beanName); System.out.println("Found bean (fallback) in: " + cur); return bean; } } } } catch (Throwable t) { /* ignore */ } } catch (Throwable ex) { // ignore } } // Limit graph traversal depth if (curDepth >= MAX_DEPTH) continue; // Explore instance fields Class<?> clazz = cur.getClass(); while (clazz != null) { for (Field field : clazz.getDeclaredFields()) { try { if (Modifier.isStatic(field.getModifiers())) continue; field.setAccessible(true); Object val = null; try { val = field.get(cur); } catch (Throwable ignored) {} if (val == null) continue; if (isIgnoredType(val.getClass())) continue; if (!visited.contains(val) && rootsSeen.add(val)) { queue.add(val); objDepth.put(val, curDepth + 1); } } catch (Throwable ignored) {} } clazz = clazz.getSuperclass(); } } return null; // not found
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
// Ignore standard library classes / primitives / wrappers / strings / arrays
private static boolean isIgnoredType(Class<?> cls) {
if (cls.isPrimitive()) return true;
if (cls.isArray()) {
Class<?> comp = cls.getComponentType();
return comp.isPrimitive() || comp == String.class;
}String name = cls.getName(); return name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("sun.") || name.startsWith("jdk.");
}
// Try to find a method by name, even if not public
private static Method findMethodByName(Class<?> cls, String name) {
while (cls != null) {
for (Method m : cls.getDeclaredMethods()) {
if (m.getName().equals(name)) {
m.setAccessible(true);
return m;
}
}
cls = cls.getSuperclass();
}
return null;
}// Check whether object implements org.springframework.context.ApplicationContext (by name)
private static boolean isApplicationContext(Object o) {
if (o == null) return false;
Class<?> cls = o.getClass();while (cls != null) { for (Class<?> iface : cls.getInterfaces()) { if (iface.getName().equals("org.springframework.context.ApplicationContext")) return true; } cls = cls.getSuperclass(); } return false;
}
// Demo runner
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Usage: SpringContextScanner ");
System.exit(2);
}String beanName = args[0]; System.out.println("Scanning JVM for ApplicationContext instances that contain bean: " + beanName); Object bean = findBeanByScanningAll(beanName); if (bean != null) { System.out.println("✅ SUCCESS: Found bean instance: " + bean + " (class=" + bean.getClass().getName() + ")"); } else { System.out.println("❌ Not found."); }
}
}
Top comments (0)