spring是如何加载bean的?
来源:互联网 发布:妍霓丝黄金水知乎 编辑:程序博客网 时间:2024/05/19 16:05
我debug的是3.1.1版本的spring,因为刚好一个项目中用的是这个版本的源码,所以就直接拿这个版本的debug一下,了解一下spring加载bean的过程。
debug用的测试代码
测试类
public class Hello { public void sayHello(String name){ System.out.println("Hello " + name); }}
public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring.xml"); Hello h = (Hello) ac.getBean("hello"); h.sayHello("Mr zou"); }}
配置文件
<?xml version="1.0" encoding="UTF-8" ?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id=" hello" class="com.zs.Hello"/></beans>
debug源码
spring中加载配置文件的代码主要过程在AbstractApplicationContext.refresh()这个方法中,在这个方法中通过obtainFreshBeanFactory()方法
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 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);try {// 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();}catch (BeansException ex) {// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}}}
这个方法创建了beanfactory,并加载xml配置文件,并解析xml配置文件,最后把相应的解析完成后把生成的beandefinition放入DefaultListableBeanFactory的beanDefinitionMap中,在这个beandefinition保存着配置文件中bean的相关信息。
下面我们来看看是如何完成这个过程的。
加载配置文件
在debug进上面的方法后,继续debug,在XmlBeanDefinitionReader .loadBeanDefinitions()中,spring生成了一个配置文件的inputstream
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isInfoEnabled()) {logger.info("Loading XML bean definitions from " + encodedResource.getResource());}Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {InputStream inputStream = encodedResource.getResource().getInputStream();try {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {inputStream.close();}}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}}
然后我们发现实际的加载这个配置文件的方法在doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法中。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {int validationMode = getValidationModeForResource(resource);Document doc = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());return registerBeanDefinitions(doc, resource);}......
在这个方法中,第一行getValidationModeForResource()这个方法是用来获取xml配置文件的验证方式,了解xml验证的同学都知道xml文件有两种验证方式DTD和XSD,一些xml文件如果格式错误就会报错,比如web.xml,如果你不按一定的格式去配置xml文件,编译器就会报相应的错误。在返回去看最开始的spirng配置文件,可以发现采用的是xsd的方式。
下面开始获取这个配置文件的domcument对象,在获取配置文件的domcument对象的方法中,有一个参数是getEntityResolver(),用来获取EntityResolver的方法,那这个EntityResolver是用来干什么的呢?在上面我们已经获取到验证xml文件的方式,在构建这个dom对象的时候,会去网络上去加载这个xsd或dtd文件去验证xml,如果网络出现问题加载不到xsd或dtd文件,就可以用EntityResolver去加载本地的xsd或dtd文件去验证xml文件。最后是通过sax方式去解析的xml文件构建的这个dom对象。
解析并注册beandefinitions
继续往下debug,在XmlBeanDefinitionReader.registerBeanDefinitions()的方法中完成了注册beandefinitions。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();//设置环境变量documentReader.setEnvironment(this.getEnvironment());//获得注册前的BeanDefinition数量int countBefore = getRegistry().getBeanDefinitionCount();//注册BeanDefinitionsdocumentReader.registerBeanDefinitions(doc, createReaderContext(resource));return getRegistry().getBeanDefinitionCount() - countBefore;}
下面在看看到底是如何注册BeanDefinitions,往下debug,发现注册的实现在DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()方法中,在这个方法中进行下面相关主要操作
// //解析前相关操作,留给子类去做 preProcessXml(root);parseBeanDefinitions(root, this.delegate);//解析后相关操作,留给子类去做postProcessXml(root);
除了解析前后的相关操作外,最后解析操作由parseBeanDefinitions这个方法来实现,在spring中有两种解析的处理操作。一种操作方法是处理配置文件中默认声明bean的,另一种操作方法是处理自定义bean声明
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)) { //默认bean声明方式解析操作parseDefaultElement(ele, delegate);}else { //自定义配置解析方式delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}
我们这里是用默认的bean配置,所以在debug时走的是上面的那一条线。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //处理import标签if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}//处理alias标签else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}//处理bean标签else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}//处理beans标签else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}}
这里我们的配置的bean是bean标签
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// Send registration event.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}
在processBeanDefinition方法中,第一行是通过这个配置文件构建一个BeanDefinitionHolder对象,这个时候bdHolder这个对象中已经有bean的id、name、class和alias等信息,在这个方法中spring解析了bean里面可能出现的一切的配置信息,具体的解析操作,感兴趣可以debug进行看看是如何解析各个元素的。这个bdHolder的包含内容如下图片:
第二个操作decorateBeanDefinitionIfRequired()方法是用来解析自定义标签。这里我们没有用自定义标签。 最后一步注册BeanDefinition,debug发现最终在DefaultListableBeanFactory.registerBeanDefinition()完成注册,其实注册就是把生成的beandefinition put到我们前面说的DefaultListableBeanFactory的beanDefinitionMap中。 最后调用fireComponentRegistered()方法完成事件的通知。
生成那些非延迟加载的bean
回到最初的refresh()方法,在
finishBeanFactoryInitialization(beanFactory);
这行代码中实现了非延迟加载bean的实例化。下面就是生成bean的最终实现
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {final String beanName = transformedBeanName(name);Object bean;// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.debug("Returning cached instance of singleton bean '" + beanName + "'");}}bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dependsOnBean : dependsOn) {getBean(dependsOnBean);registerDependentBean(dependsOnBean, beanName);}}// Create bean instance.if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {public Object getObject() throws BeansException {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {public Object getObject() throws BeansException {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; " +"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}// Check if required type matches the type of the actual bean instance.if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {try {return getTypeConverter().convertIfNecessary(bean, requiredType);}catch (TypeMismatchException ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to convert bean '" + name + "' to required type [" +ClassUtils.getQualifiedName(requiredType) + "]", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}
在doGetBean()这个方法中,1、首先根据传入的name获取真实的beanname;2、尝试从缓存中获取bean对象;3、获取最开始构造的hbd对象;4、生成bean对象。生成bean的代码主要在下面这个doCreateBean()中
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, new ObjectFactory() {public Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});}// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {exposedObject = initializeBean(beanName, exposedObject, mbd);}}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
最终生成bean的方法就在这个doCreateBean()中,在这个方法中:1、首先创建一个bean的包装类beanwrapper,在生成这个包装类的时候,如果工厂方法不为空,则会使用工厂方法初始化bean,若已经解析过,ze则使用相应的构造方法去构造,否则则使用默认构造函数去构造,在使用默认构造方法构造的时候,如果这个类中没有override方法,则直接使用java反射,否则使用cglib字节码增强,生成一个含有override的代理类;2、在生成完bean后,然后开始注入属性,把配置文件中的属性都注入进去;3、最后初始化bean。
总结
到此,spring加载xml配置文件,并生成bean的大概过程就完成了。总的来说,首先开始加载xml配置文件,然后开始使用sax解析并校验xml配置文件,在解析xml文件,生成beandefinitions缓存起来,当我们去加载生成bean的时候,然后去加载这个缓存起来的beandefinitions,在根据不同的配置情况去生成bean,最后把bean加入缓存中缓存起来。
- spring是如何加载bean的?
- spring bean的加载
- spring的bean加载
- Spring是如何管理Bean
- Spring是如何管理Bean
- Spring是如何管理Bean
- Spring是如何加载Xml文件的
- Spring如何利用XmlBeanFactory类加载bean的配置文件?
- spring的bean加载2
- Spring bean的延迟加载
- spring中bean的加载
- spring bean的加载过程
- Spring Bean 的加载顺序
- Spring bean的加载过程
- spring加载bean的过程
- Spring是如何缓存单例bean
- 求助:Spring的bean加载底层是怎样实现的呀?
- Spring是如何加载XML文件中的标签的
- 定义局部变量与全局变量未初始的结果
- 剑指offer-25.复杂链表的复制
- CoIDE+GNU工具链构建STM32F103xx工程
- redis主从复制
- [LeetCode OJ]Target Sum
- spring是如何加载bean的?
- 《深入分析JavaWeb技术内幕》读书笔记四.I/O调优
- js初始化时间的方法
- 四月英语总结——《坚不可摧》
- 强制 pkill redis 后,flushdb 命令报错的解决办法
- 4.29 SSM项目实战(二)--秒杀系统api之Service层
- 数据结构与算法(七)
- android studio 菜单中的app运行按钮上有个叉号,原因与解决办法
- 【FastDFS】——基本概念