Spring Bean注册方式小结
2015-03-02 23:54
375 查看
IOC容器
Spring的核心是一个IOC容器,管理着我们向容器注册的所有bean。下面我们来看下两种向容器注册bean的方式,通过BeanDefinitionReader读取Spring Bean的配置文件,解析然后注册;
通过ClassPathBeanDefinitionScanner直接扫描带有Spring Bean注解的Java类并注册;
Reader
BeanDefinitionReader的使用方式如下,[code]import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Main { public static void main(String[] args) throws Exception { loadBeans(); } private static void loadBeans() { BeanDefinitionRegistry registry = new DefaultListableBeanFactory(); BeanDefinitionReader reader = new XmlBeanDefinitionReader(registry); Resource resource = new ClassPathResource("spring-beans.xml"); reader.loadBeanDefinitions(resource); System.out.println(((DefaultListableBeanFactory)registry).getBean("foo")); } }
spring-beans.xml文件如下,
[code]<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans default-autowire="byName"> <bean id="foo" class="me.kisimple.just4fun.Foo"/> </beans>
上面我们以XmlBeanDefinitionReader为栗。需要将BeanDefinitionRegistry传给XmlBeanDefinitionReader,并且需要将配置文件构造成Resource,然后就可以调用
BeanDefinitionReader#loadBeanDefinitions对bean配置文件进行加载,解析,最后向容器,也就是BeanDefinitionRegistry,注册bean了。loadBeanDefinitions方法最终是会来到registerBeanDefinitions方法,
[code] /** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. * <p>Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * @param doc the DOM document * @param resource the resource descriptor (for context information) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of parsing errors * @see #loadBeanDefinitions * @see #setDocumentReaderClass * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ @SuppressWarnings("deprecation") public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
可以看到真正的工作是在DefaultBeanDefinitionDocumentReader#registerBeanDefinitions里面完成的,
[code] /** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). * <p>Opens a DOM Document; then initializes the default settings * specified at the {@code <beans/>} level; then parses the contained bean definitions. */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }
[code] /** * Register each bean definition within the given root {@code <beans/>} element. */ protected void doRegisterBeanDefinitions(Element root) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(this.readerContext, root, parent); preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
[code] protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
最终是由BeanDefinitionParserDelegate来解析bean配置文件。
解析完成进行注册,因为两种方式实际上使用的注册方法都是同一个,因此我们放在后面再说,先来看下另一种方式。
Scanner
ClassPathBeanDefinitionScanner使用方式如下,[code]import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; public class Main { public static void main(String[] args) throws Exception { scanBeans(); } private static void scanBeans() { BeanDefinitionRegistry registry = new DefaultListableBeanFactory(); ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry); scanner.scan("me.kisimple.just4fun"); System.out.println(((DefaultListableBeanFactory)registry).getBean("FooComponent")); } }
比使用reader要简单一些,只需要指定要扫描的package就可以。而想要注册成Spring Bean的Java类则需要使用Spring提供的注解,
[code]package me.kisimple.just4fun; import org.springframework.stereotype.Component; @Component(value = "FooComponent") // 默认value为foo public class Foo { public void bar() { System.out.println("hello Foo."); } }
支持的注解不止是Component,看ClassPathBeanDefinitionScanner的注释,
[code]/** * A bean definition scanner that detects bean candidates on the classpath, * registering corresponding bean definitions with a given registry ({@code BeanFactory} * or {@code ApplicationContext}). * * <p>Candidate classes are detected through configurable type filters. The * default filters include classes that are annotated with Spring's * {@link org.springframework.stereotype.Component @Component}, * {@link org.springframework.stereotype.Repository @Repository}, * {@link org.springframework.stereotype.Service @Service}, or * {@link org.springframework.stereotype.Controller @Controller} stereotype. * * <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and * JSR-330's {@link javax.inject.Named} annotations, if available. * * @author Mark Fisher * @author Juergen Hoeller * @author Chris Beams * @since 2.5 * @see AnnotationConfigApplicationContext#scan * @see org.springframework.stereotype.Component * @see org.springframework.stereotype.Repository * @see org.springframework.stereotype.Service * @see org.springframework.stereotype.Controller */
看下scan方法,
[code] public int scan(String... basePackages) { int beanCountAtScanStart = this.registry.getBeanDefinitionCount(); doScan(basePackages); // Register annotation config processors, if necessary. if (this.includeAnnotationConfig) { AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart); }
与
XmlBeanDefinitionReader#registerBeanDefinitions方法结构相似。真正扫描的动作由doScan方法完成,
[code] protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
最后调用registerBeanDefinition方法进行注册,
[code] protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) { BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry); }
上面我们已经说过了,两种方式其实最后都是调用
BeanDefinitionReaderUtils#registerBeanDefinition来完成注册的。
Registry
来看下BeanDefinitionReaderUtils#registerBeanDefinition方法,[code] /** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with * @throws BeanDefinitionStoreException if registration failed */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }
妥妥的,最后还是要使用BeanDefinitionRegistry,
[code] /** * Register a new bean definition with this registry. * Must support RootBeanDefinition and ChildBeanDefinition. * @param beanName the name of the bean instance to register * @param beanDefinition definition of the bean instance to register * @throws BeanDefinitionStoreException if the BeanDefinition is invalid * or if there is already a BeanDefinition for the specified bean name * (and we are not allowed to override it) * @see RootBeanDefinition * @see ChildBeanDefinition */ void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
看了下,这个方法只有一个实现,就是DefaultListableBeanFactory#registerBeanDefinition,具体就暂时不作分析了。
最后再说一点,IOC容器的核心只是一个map(可以思考下,除了用map是否还有其他实现方式?),而这个map便是
DefaultListableBeanFactory#beanDefinitionMap,
[code] /** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
相关文章推荐
- Spring概念简介、bean扫描与注册实现方式
- spring概念简介、bean扫描与注册实现方式
- 通过注解的方式在spring中注册bean
- Spring中Bean的注册与注入——注解方式
- Spring注解方式使用与注册bean
- spring概念简介、bean扫描与注册实现方式
- Spring 5:以函数式方式注册 Bean
- Spring整理系列(01)——spring概念简介、bean扫描与注册实现方式
- Spring中Bean的注册与注入——XML方式
- Spring-程序中获取注册bean的方式
- Spring2.5 IoC之bean的四种注入方式(实践篇)
- 传智播客 Spring中bean的实例化方式与IOC容器总结
- spring中读取xml配置文件、获取bean的几种方式
- spring实例化bean的方式
- 几种获得spring里注册Bean的方法
- spring的配置文件中bean的注入方式
- Spring Bean Xml映射方式
- Spring的三种实例化Bean的方式
- 编程方式实现SpringBean LazyInit
- 传智播客Spring2.5视频教程_Spring的三种实例化Bean的方式 1