前两篇简单介绍了XmlBeanFactory如何加载xml以及如何创建bean,这都是完全基于xml配置的,那么注解又是如何处理的呢?以@Component和@Resource为例简单说明。
1 XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring/spring-test.xml")); 2 //BeanFactory不会自动添加BeanPostProcessor,ApplicationContext会 3 CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor = new CommonAnnotationBeanPostProcessor(); 4 commonAnnotationBeanPostProcessor.setBeanFactory(factory); 5 factory.addBeanPostProcessor(commonAnnotationBeanPostProcessor); 6 MyBean bean = (MyBean) factory.getBean("myBean"); 7 System.out.println(bean.name());//my bean
1 @Component("myBean") 2 public class MyBean { 3 @Resource 4 private MyBean myBean; 5 private String name = "my bean"; 6 public String name() { 7 return myBean.name; 8 } 9 }
1 <beans 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://www.springframework.org/schema/beans" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context.xsd"> 9 <context:component-scan base-package="com.zyong.spring.beanfactory" /> 10 </beans>
之前有提到过,将xml解析成Document后就会注册bean definitions,注册bean definition的过程就是解析Document的过程,之前分析了parseDefaultElement是处理bean标签的主要方法,现在来看delegate.parseCustomElement方法,它会处理一些复杂标签,比如component-scan。
1 //DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法,有删减 2 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 3 NodeList nl = root.getChildNodes(); 4 for (int i = 0; i < nl.getLength(); i++) { 5 Node node = nl.item(i); 6 if (node instanceof Element) { 7 Element ele = (Element) node; 8 if (delegate.isDefaultNamespace(ele)) { 9 parseDefaultElement(ele, delegate); 10 } 11 else { 12 delegate.parseCustomElement(ele); 13 } 14 } 15 } 16 }
1 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { 2 //得到namespace,这里对于context:component-scan就是http://www.springframework.org/schema/context 3 String namespaceUri = getNamespaceURI(ele); 4 //spring会预注册一些NamespaceHandler,此处就会得到ContextNamespaceHandler 5 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 6 if (handler == null) { 7 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); 8 return null; 9 } 10 //使用NamespaceHandler解析该DOM节点 11 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 12 }
1 public class ContextNamespaceHandler extends NamespaceHandlerSupport { 2 //init方法会在NamespaceHandler构造完成后回调 3 @Override 4 public void init() { 5 registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); 6 registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); 7 registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); 8 //NamespaceHandlerSupport中持有BeanDefinitionParser, 9 //handler.parse会调用findParserForElement(element, parserContext).parse(element, parserContext), 10 //即将parse任务委托给具体的BeanDefinitionParser,这里就是ComponentScanBeanDefinitionParser 11 registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); 12 registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); 13 registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); 14 registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); 15 registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); 16 } 17 }
1 //ComponentScanBeanDefinitionParser的parse方法,该方法与解析bean标签类似,最终都会产生bean definition并注册。 2 public BeanDefinition parse(Element element, ParserContext parserContext) { 3 //得到package信息 4 String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); 5 //进一步分析package路径 6 basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); 7 String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, 8 ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); 9 10 // Actually scan for bean definitions and register them. 11 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); 12 //doScan会遍历package下的类,通过filters检查是否应该将类注册为bean,如果检查通过则注册该bean definition。 13 //filters就会包含AnnotationTypeFilter,其annotationType为org.springframework.stereotype.Component,如果被检查类有@Compenent注解则将其视为bean 14 Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); 15 16 registerComponents(parserContext.getReaderContext(), beanDefinitions, element); 17 18 return null; 19 }
至此就将使用注解声明的bean注册进beanFactory了(还未实例化),由于此时在bean中使用了注解@Resource进行注入,故与xml注入有所差异。
由下表可知,beanFactory不会自动注册BeanPostProcessor,而通过文档发现注解的注入其实就是通过BeanPostProcessor完成的,所以测试代码中才会显示添加CommonAnnotationBeanPostProcessor。
Feature | BeanFactory |
ApplicationContext |
---|---|---|
Bean instantiation/wiring |
Yes |
Yes |
Automatic BeanPostProcessor registration
|
No |
Yes |
Automatic BeanFactoryPostProcessor registration
|
No |
Yes |
Convenient MessageSource access (for i18n)
|
No |
Yes |
ApplicationEvent publication
|
No |
Yes |
CommonAnnotationBeanPostProcessor是一个InstantiationAwareBeanPostProcessor,这里我们只关注其postProcessPropertyValues方法。
1 //CommonAnnotationBeanPostProcessor的postProcessPropertyValues方法 2 public PropertyValues postProcessPropertyValues( 3 PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { 4 5 InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs); 6 try { 7 //注入,注入逻辑无非就是遍历可注入属性(字段、方法),进行反射调用。 8 metadata.inject(bean, beanName, pvs); 9 } 10 catch (Throwable ex) { 11 throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex); 12 } 13 return pvs; 14 }
附:
时间: 2024-10-04 08:12:46