skills/spring-context/SKILL.md
原则:
getBean() 触发 Bean 初始化产生副作用。vmtool -l 控制实例数量;避免无条件输出完整 getBeanDefinitionNames()。优先尝试获取常见的 Spring Boot Context(通常是 AbstractApplicationContext 子类):
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 5
如果拿不到结果,可以尝试获取: org.springframework.context.ApplicationContext
如果获取到多个对象,可以从对象的 classloader 的 Class<?> name 来判断。
LaunchedURLClassLoader只看值(示例:server.port):
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express 'instances[0].getEnvironment().getProperty("server.port")'
获取“来源”
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express '#env=instances[0].getEnvironment(), #ps=#env.getPropertySources().get("configurationProperties"), #ps.findConfigurationProperty("server.port")'
如果应用有集成 spring-boot-starter-actuator ,可以尝试
vmtool --action getInstances \
--className org.springframework.boot.actuate.env.EnvironmentEndpoint \
--express 'instances[0].environmentEntry("server.port")'
假设你要查的 beanName 为 fooService:
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express 'instances[0].containsBean("fooService")'
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express 'instances[0].containsLocalBean("fooService")'
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express 'instances[0].containsBeanDefinition("fooService")'
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express 'instances[0].getAliases("fooService")'
判读:
containsBean=true 但 containsLocalBean=false:Bean 可能来自父 Context。containsBean=false 且你确定应该存在:优先检查是否选错 Context、@Profile/@Conditional、配置项/环境变量是否生效。当你只有一个关键词(比如 order / datasource)而不知道精确 beanName 时:
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express '#ctx=instances[0], #[email protected]@asList(#ctx.getBeanDefinitionNames()), #m=#names.{? #this.toLowerCase().contains("order")}, #m.subList(0, @java.lang.Math@min(#m.size(), 50))'
说明:
当你知道目标类型(接口/父类)全限定名时(示例:com.foo.OrderService):
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express 'instances[0].getBeanNamesForType(@com.foo.OrderService@class)'
若返回多个候选(NoUniqueBeanDefinitionException 常见根因),只看候选名称即可:
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express 'instances[0].getBeansOfType(@com.foo.OrderService@class).keySet()'
提示:
@com.foo.OrderService@class 报 ClassNotFound,通常是类加载器不对:先用 classloader(stats/instances/tree)找到应用的 classLoaderHash,再在 vmtool/ognl 上加 --classLoader <hash> 重新执行。也可以找到 classloader的 Class Name,再使用 --classLoaderClass 参数。当你需要确认 Bean 是怎么注册进来的(@Bean 工厂方法?XML?自动扫描?)时,可尝试拿 BeanFactory 看 BeanDefinition(前提:Context 是 AbstractApplicationContext)。
vmtool --action getInstances --className org.springframework.context.support.AbstractApplicationContext -l 1 --express '#ctx=instances[0], #bf=#ctx.getBeanFactory(), #bd=#bf.getBeanDefinition("fooService")'