@SpringBootConfiguration publicclassApp{ publicstaticvoidmain(String[] args){ ConfigurableApplicationContext applicationContext = SpringApplication.run(App.class, args); A a = applicationContext.getBean(A.class); B b = a.getB(); System.out.println(b); } }
类 A
1 2 3 4 5 6 7 8
@Component publicclassA{ @Resource private B b; public B getB(){ return b; } }
类 B
1 2 3
@Service publicclassB{ }
现象是一运行 A 对象内的 B 属性注入不进来,通过 applicationContext.gerBean 是可以正常获取 B 类的 bean,这 TM 就奇了怪了。然后就去 debug 源码。
最初是在 Spring 扫描包路径获得 BeanDefinition 的地方找是不是有什么问题,是不是这个时候获得的 BeanDefinition 不对,遗漏了 A 类的属性。找了半天,发现 @Resource 注释的属性是在 CommonAnnotationBeanPostProcessor 类内部扫描获得的:
/** * Populate the bean instance in the given BeanWrapper with the property values * from the bean definition. * @param beanName the name of the bean * @param mbd the bean definition for the bean * @param bw BeanWrapper with bean instance */ protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {}
publicabstractclassAbstractBeanFactoryextendsFactoryBeanRegistrySupportimplementsConfigurableBeanFactory{ /** BeanPostProcessors to apply in createBean */ privatefinal List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>(); }
@Override publicvoidrefresh()throws BeansException, IllegalStateException { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }
看起来应该是一个默认一定会有的 bean,然后 Shift + Command + F 搜这个类,全局只存在一定地方有这个完整的字符:
1 2 3 4 5 6 7
publicclassAnnotationConfigUtils{ /** * The bean name of the internally managed JSR-250 annotation processor. */ publicstaticfinal String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.context.annotation.internalCommonAnnotationProcessor"; }
privatestaticfinalboolean jsr250Present = ClassUtils.isPresent("javax.annotation.Resource", AnnotationConfigUtils.class.getClassLoader()); /** * Register all relevant annotation post processors in the given registry. * @param registry the registry to operate on * @param source the configuration source element (already extracted) * that this registration was triggered from. May be {@code null}. * @return a Set of BeanDefinitionHolders, containing all bean definitions * that have actually been registered by this call */ publicstatic Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, Object source){ // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } }
这个 if 前后省略了一些逻辑。到这里就很明显了,Spring 启动时,会用去加载 javax.annotation.Resource 类,如果加载不到,那么也就不会去注册 internalCommonAnnotationProcessor 了。
java.lang.ClassNotFoundException: javax.annotation.Resource at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496) at org.springframework.util.ClassUtils.forName(ClassUtils.java:250) at org.springframework.util.ClassUtils.isPresent(ClassUtils.java:327) at com.footmanff.utsample.App.main(App.java:46)