DEV Community

Query Filter
Query Filter

Posted on

Find spring bean

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)