Spring源码之bean的解析obtainFreshBeanFactory方法解读
目录
- 前言
- 何时解析
- 测试类
- `ClassPathXmlApplicationContext` 的构造器
- `refresh()` 方法
- `obtainFreshBeanFactory()` 方法
- `refreshBeanFactory()` 方法
- `destroyBeans()` 方法(重点)
- `loadBeanDefinitions()` 方法(重点)
- 进入到 `loadBeanDefinitions()` 方法中
- `reader.loadBeanDefinitions(configLocation)` 方法
- `loadBeanDefinitions(resources)` 方法
- `loadBeanDefinitions(resource)` 方法
- `doLoadBeanDefinitions()` 方法
- `registerBeanDefinitions()` 方法(注册 `bean` 信息)
- `doRegisterBeanDefinitions()` 方法
- `parseBeanDefinitions()` 方法
- `obtainFreshBeanFactory()` 方法总结
- `obtainFreshBeanFactory()` 方法流程总结
前言
一直以来,只是知道 Spring
会解析所有的配置文件(通常我们会放在 resources
目录下),然后将从配置文件中解析到的 bean
定义封装成 BeanDefinition
,加载到 IOC
容器中。而不知道其内部是如何解析,执行的,本篇文章将予以揭秘
何时解析
要知道 spring
何时去解析配置文件,我们可以断定是在 IOC
容器启动时,那么我们可以根据 IOC
容器启动流程进行找寻
测试类
public class TransactionTest_2 {public static void main(String[] args) {/* 初始化启动 spring ioc 容器 */ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-spring.xml");/* 从 spring ioc 容器中获取一个 bean */ProductInfoService productInfoService = (ProductInfoService) applicationContext.getBean("productInfoServiceImpl");System.out.println("拿到的Bean为:" + productInfoService);}
}
在第五行代码处打上断点,DEBUG
模式去调试
ClassPathXmlApplicationContext
的构造器
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {// 调用如下this(new String[] {configLocation}, true, null);
}public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();}
}
它的核心方法肯定就在 refresh()
方法里面,实际上它调用的是抽象类AbstractApplicationContext
的 refresh()
refresh()
方法
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {/*** 刷新上下文环境,初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验*/prepareRefresh();// 该方法会解析所有 spring 配置文件(通常我们会放在 resources 目录下)// 将配置文件中的 bean 定义封装成 BeanDefinition,加载到 BeanFactory 中ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();/*** 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired @Qualifier等* 设置SPEL表达式#{key}的解析器* 设置资源编辑注册器,如PerpertyEditorSupper的支持* 添加ApplicationContextAwareProcessor处理器* 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等* 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去*/prepareBeanFactory(beanFactory);try {/*** 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess* 在当前上下文使用的Bean容器BeanFactory的标准初始化完成后对其做一些修改。此时* 所有的Bean definition都已经加载但是还没有 Bean 被创建*/postProcessBeanFactory(beanFactory);/*** 实例化和调用所有 BeanFactoryPostProcessor,包括其子类 BeanDefinitionRegistryPostProcessor* 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor* 执行对应的postProcessBeanDefinitionRegistry方法 和 postProcessBeanFactory方法*/invokeBeanFactoryPostProcessors(beanFactory);/*** 注册所有的 BeanPostProcessor,将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中* 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别* 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法*/ registerBeanPostProcessors(beanFactory);// 初始化上下文中的资源文件,如国际化文件的处理等initMessageSource();// 初始化上下文事件广播器,并放入applicatioEventMulticaster,如ApplicationEventPublisherinitApplicationEventMulticaster();// 给子类扩展初始化其他BeanonRefresh();// 注册监听器registerListeners();/*** 设置转换器* 注册一个默认的属性值解析器* 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理* 初始化剩余的非惰性的bean,即初始化非延迟加载的bean*/finishBeanFactoryInitialization(beanFactory);/*** 初始化生命周期处理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring启动的时候调用start方法开始生命周期,* spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,启动有一直运行,如一直轮询kafka* 启动所有实现了Lifecycle接口的类* 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理,即对那种在spring启动后需要处理的一些类,这些类实现了* ApplicationListener<ContextRefreshedEvent> ,这里就是要触发这些类的执行(执行onApplicationEvent方法)另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent* 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人*/finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}destroyBeans();cancelRefresh(ex);throw ex;}finally {resetCommonCaches();}}
}
obtainFreshBeanFactory()
方法会解析所有 spring
配置文件。在此也证明了我们的推断:在 IOC
容器启动时,会去解析所有的 spring
配置文件
obtainFreshBeanFactory()
方法
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 1.刷新 BeanFactory,并执行加载和解析配置文件的操作refreshBeanFactory();// 2.拿到刷新后的 BeanFactoryConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);}return beanFactory;
}
refreshBeanFactory()
:刷新BeanFactory
,并执行加载和解析配置文件的操作getBeanFactory()
:拿到刷新后的BeanFactory
refreshBeanFactory()
方法
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {...@Overrideprotected final void refreshBeanFactory() throws BeansException {// 判断是否存在beanFactoryif (hasBeanFactory()) {// 注销所有的单例 beandestroyBeans();// 重置beanFactorycloseBeanFactory();}try {// 创建一个新的BeanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();// 指定序列化id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象beanFactory.setSerializationId(getId());// 定制BeanFactorycustomizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}...
}
- 如果之前已经有
beanFactory
,那么就销毁单例bean
以及关闭其beanFactory
createBeanFactory()
:创建一个新的BeanFactory
,并指定序列化其Id
customizeBeanFactory()
:定制自定义的BeanFactory
loadBeanDefinitions()
:加载bean
定义
destroyBeans()
方法(重点)
注意 destroyBeans()
方法:它只能销毁单例 bean
,原因可以查看文章: spring 源码之 bean 的销毁
protected void destroyBeans() {getBeanFactory().destroySingletons();
}
继续看 destroySingletons()
方法
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {...@Overridepublic void destroySingletons() {// 调用父类的销毁方法销毁单例beansuper.destroySingletons();// 清空手工注册的beanName的缓存this.manualSingletonNames.clear();// 清空类型-->beanName的映射缓存clearByTypeCache();}
}
这里不再赘述 destroySingletons()
方法,详情可以查看:spring 源码之 bean 的销毁中的 destroySingletons() 方法
loadBeanDefinitions()
方法(重点)
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {...@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// 为指定beanFactory创建XmlBeanDefinitionReader(加载解析bean)XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// 对beanDefinitionReader进行环境变量的设置beanDefinitionReader.setEnvironment(getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// 对beanDefinitionReader进行设置,空方法initBeanDefinitionReader(beanDefinitionReader);// 设置loadBeanDefinitions的配置文件loadBeanDefinitions(beanDefinitionReader);}...
}
进入到 loadBeanDefinitions()
方法中
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {// 1.获取配置文件路径String[] configLocations = getConfigLocations();if (configLocations != null) {for (String configLocation : configLocations) {// 2.根据配置文件路径加载 bean 定义reader.loadBeanDefinitions(configLocation);}}
}// AbstractRefreshableWebApplicationContext.java
@Override
public String[] getConfigLocations() {return super.getConfigLocations();
}// AbstractRefreshableConfigApplicationContext.java
protected String[] getConfigLocations() {return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}// XmlWebApplicationContext.java
@Override
protected String[] getDefaultConfigLocations() {if (getNamespace() != null) {return new String[]{DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};} else {return new String[]{DEFAULT_CONFIG_LOCATION};}
}
经过调试 configLocations = [“classpath:applicationContext-spring.xml”]
如下
其中
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
以上代码分析可知:如果不在 web.xml
中配置 contextConfigLocation
属性的话,spring
会读取 contextConfigLocation
的默认值 "/WEB-INF/applicationContext.xml"
<!-- web.xml 中配置 contextConfigLocation属性 -->
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext-spring.xml</param-value>
</context-param>
reader.loadBeanDefinitions(configLocation)
方法
抽象类 AbstractBeanDefinitionReader
的 loadBeanDefinitions()
方法
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {// 获取 resourceLoader,这边为 XmlWebApplicationContextResourceLoader resourceLoader = getResourceLoader();if (resourceLoader == null) {throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");}// 判断 resourceLoader 是否为 ResourcePatternResolver 的实例if (resourceLoader instanceof ResourcePatternResolver) {try {// 根据路径拿到该路径下所有符合的配置文件,并封装成ResourceResource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);// 根据Resource,加载Bean的定义int loadCount = loadBeanDefinitions(resources);if (actualResources != null) {for (Resource resource : resources) {actualResources.add(resource);}}if (logger.isDebugEnabled()) {logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");}return loadCount;}catch (IOException ex) {throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);}}else {// 只能通过绝对URL加载单个资源Resource resource = resourceLoader.getResource(location);// 根据Resource,加载Bean的定义int loadCount = loadBeanDefinitions(resource);if (actualResources != null) {actualResources.add(resource);}if (logger.isDebugEnabled()) {logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");}return loadCount;}
}
resources
调试如下
loadBeanDefinitions(resources)
方法
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {Assert.notNull(resources, "Resource array must not be null");int counter = 0;// 1.遍历所有的Resourcefor (Resource resource : resources) {// 2.根据Resource加载bean的定义,XmlBeanDefinitionReader实现counter += loadBeanDefinitions(resource);}return counter;
}
loadBeanDefinitions(resource)
方法
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {// 加载 bean 定义return loadBeanDefinitions(new EncodedResource(resource));
}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());}// 1.当前正在加载的EncodedResourceSet<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}// 2.将当前encodedResource添加到currentResourcesif (!currentResources.add(encodedResource)) {// 如果添加失败,代表当前的encodedResource已经存在,则表示出现了循环加载throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {// 3.拿到Resource的inputStreamInputStream inputStream = encodedResource.getResource().getInputStream();try {// 4.将inputStream封装成org.xml.sax.InputSourceInputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}// 5.加载 bean 定义(方法以do开头,真正处理的方法)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()
方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {// 1.根据inputSource和resource加载XML文件,并封装成DocumentDocument doc = doLoadDocument(inputSource, resource);// 2.根据返回的Document注册Bean信息(对配置文件的解析,核心逻辑)return registerBeanDefinitions(doc, resource);} catch (BeanDefinitionStoreException ex) {throw ex;} catch (SAXParseException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);} catch (SAXException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"XML document from " + resource + " is invalid", ex);} catch (ParserConfigurationException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from " + resource, ex);} catch (IOException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from " + resource, ex);} catch (Throwable ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from " + resource, ex);}
}
registerBeanDefinitions()
方法(注册 bean
信息)
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {// 1.使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReaderBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();// 2.记录统计前BeanDefinition的加载个数int countBefore = getRegistry().getBeanDefinitionCount();// 3.createReaderContext:根据resource创建一个XmlReaderContext// 4.registerBeanDefinitions:加载及注册Bean定义documentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 5.返回本次加载的BeanDefinition个数return getRegistry().getBeanDefinitionCount() - countBefore;
}
进入到 documentReader.registerBeanDefinitions(doc, createReaderContext(resource))
中
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");// 1.拿到文档的子节点,对于Spring的配置文件来说,理论上应该都是<beans>Element root = doc.getDocumentElement();// 2.通过拿到的节点,注册 Bean 定义doRegisterBeanDefinitions(root);
}
doRegisterBeanDefinitions()
方法
将从 spring
配置文件拿到的节点,注册 Bean
定义
protected void doRegisterBeanDefinitions(Element root) {BeanDefinitionParserDelegate parent = this.delegate;// 构建BeanDefinitionParserDelegatethis.delegate = createDelegate(getReaderContext(), root, parent);// 1.校验root节点的命名空间是否为默认的命名空间(默认命名空间http://www.springframework.org/schema/beans)if (this.delegate.isDefaultNamespace(root)) {// 2.处理profile属性String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);// 校验当前节点的 profile 是否符合当前环境定义的, 如果不是则直接跳过, 不解析该节点下的内容if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isInfoEnabled()) {logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource());}return;}}}// 3.解析前处理, 留给子类实现preProcessXml(root);// 4.解析并注册bean定义parseBeanDefinitions(root, this.delegate);// 5.解析后处理, 留给子类实现postProcessXml(root);this.delegate = parent;
}
parseBeanDefinitions()
方法
解析并注册 bean
定义,此方法就是解析 xml
配置文件的方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// 1.默认命名空间的处理if (delegate.isDefaultNamespace(root)) {NodeList nl = root.getChildNodes();// 遍历root的子节点列表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)) {// 1.1 默认命名空间节点的处理,例如: <bean id="test" class="" />parseDefaultElement(ele, delegate);}else {// 1.2 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>delegate.parseCustomElement(ele);}}}} else {// 2.自定义命名空间的处理delegate.parseCustomElement(root);}
}
- 来到了解析
bean
定义的核心部分,这边会遍历root
节点(正常为<beans>
节点)下的所有子节点,对子节点进行解析处理 - 如果节点的命名空间是
spring
默认的命名空间,则走parseDefaultElement(ele, delegate)
方法进行解析,例如最常见的:<bean>
- 如果节点的命名空间不是
spring
默认的命名空间,也就是自定义命名空间,则走delegate.parseCustomElement(ele)
方法进行解析,例如常见的:<context:component-scan/>、<aop:aspectj-autoproxy/>
- 默认的命名空间为:
http://www.springframework.org/schema/beans
,其他都是自定义命名空间,例如下图aop
的命名空间为:http://www.springframework.org/schema/aop
parseDefaultElement(ele, delegate)
源码分析:https://blog.csdn.net/v123411739/article/details/86669952
parseCustomElement(ele)
源码分析:https://blog.csdn.net/v123411739/article/details/87447290
obtainFreshBeanFactory()
方法总结
- 该方法会解析所有
spring
配置文件(通常我们会放在resources
目录下),将所有 配置文件中的bean
定义封装成BeanDefinition
,加载到BeanFactory
中 - 常见的,如果解析到
<context:component-scan base-package="" />
注解时,会扫描base-package
指定的目录,将该目录下使用指定注解(@Controller、@Service、@Component、@Repository
)的bean
定义也同样封装成BeanDefinition
,加载到BeanFactory
中
而所谓的加载到 BeanFactory
中的内容主要指的是添加到以下 3
个缓存
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
}public class SimpleAliasRegistry implements AliasRegistry {private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
}
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;int ROLE_APPLICATION = 0;int ROLE_SUPPORT = 1;int ROLE_INFRASTRUCTURE = 2;void setParentName(@Nullable String parentName);@NullableString getParentName();void setBeanClassName(@Nullable String beanClassName);@NullableString getBeanClassName();void setScope(@Nullable String scope);@NullableString getScope();void setLazyInit(boolean lazyInit);boolean isLazyInit();void setDependsOn(@Nullable String... dependsOn);@NullableString[] getDependsOn();void setAutowireCandidate(boolean autowireCandidate);boolean isAutowireCandidate();void setPrimary(boolean primary);boolean isPrimary();void setFactoryBeanName(@Nullable String factoryBeanName);@NullableString getFactoryBeanName();void setFactoryMethodName(@Nullable String factoryMethodName);@NullableString getFactoryMethodName();ConstructorArgumentValues getConstructorArgumentValues();default boolean hasConstructorArgumentValues() {return !getConstructorArgumentValues().isEmpty();}MutablePropertyValues getPropertyValues();default boolean hasPropertyValues() {return !getPropertyValues().isEmpty();}boolean isSingleton();boolean isPrototype();boolean isAbstract();int getRole();@NullableString getDescription();@NullableString getResourceDescription();@NullableBeanDefinition getOriginatingBeanDefinition();
}
obtainFreshBeanFactory()
方法流程总结
- 如果已经有
beanFactory
,那么关闭其beanFactory
;然后创建一个新的BeanFactory:DefaultListableBeanFactory
- 根据
web.xml
或spring
默认的配置文件contextConfigLocation
配置的路径,读取spring
配置文件,验证配置文件的内容,并封装成Resource
- 根据
Resource
加载XML
配置文件,并解析成Document
对象 - 拿到
Document
中的根节点,遍历根节点和所有子节点
参考:https://blog.csdn.net/v123411739/article/details/86563620
Spring源码之bean的解析obtainFreshBeanFactory方法解读相关推荐
- Spring源码深度解析,Spring源码以及Bean的生命周期(五)(附代码示例:)
五)Bean 的生命周期,创建---初始化---销毁的过程 目录 五)Bean 的生命周期,创建---初始化---销毁的过程 一 , 指定初始化方法 init-method 方法 二 ,指定销毁 ...
- Spring源码剖析——Bean的配置与启动
IOC介绍 相信大多数人在学习Spring时 IOC 和 Bean 算得上是最常听到的两个名词,IOC在学习Spring当中出现频率如此之高必然有其原因.如果我们做一个比喻的话,把Bean说成Sp ...
- Spring源码之Bean的注册(使用XML配置的方式)
本文分析的Spring源码是5.2.2版本,使用Gradle进行管理. 一.Bean的注册,先来看通过XML配置Bean的方式 1.配置applicationContext.xml: <?xml ...
- 一、如何阅读Spring源码(全网最简单的方法)
学习Java最好最有效的方法是学习Spring,但是最笨最没效的方法也是学习Spring. 为什么这么说呢?道理其实很简单 A.Spring很庞大,很完善,也非常的底层,如果我们学会的Spring,那 ...
- Spring源码分析——Bean的生命周期
文章目录 说明 测试代码 说明 本文从源码的角度分析Spring中Bean的加载过程,本文使用的Spring版本为4.3.25.RELEASE 测试代码 测试代码如下,根据这段简单的测试代码,一步步跟 ...
- 深入浅出Spring源码:IOC原理解析(一)
IOC(Inversion of Control),即控制反转,意思是将对象的创建和依赖关系交给第三方容器处理,我们要用的时候告诉容器我们需要什么然后直接去拿就行了.举个例子,我们有一个工厂,它生产各 ...
- Spring源码之Bean的注册(注解方式)
1.创建AnnotationConfigApplicationContext AnnotationConfigApplicationContext context = new AnnotationCo ...
- Spring源码学习--Bean的生命周期
Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...
- spring源码之bean加载(bean解析下篇)
bean的加载步骤: MyTestBean bean = (MyTestBean) bf.getBean("myTestBean"); 步骤: (1) 转换对应的beanName ...
- Spring源码之启动过程(四)—— Bean的实例化详解
前面,我们把Bean的生命周期做了一个概述和梳理,为的是更深刻的理解容器启动及Bean的生命周期,最主要的是Bean的实例化过程,没有看过的,可以进去先看一下(文章链接:Spring源码之Bean的生 ...
最新文章
- 中南财经政法大学计算机考研难吗,中南财经政法大学考研难吗(考研难度分析)...
- thymeleaf rs 查询结果_第十一章 JDBC与MySQL数据库(10)——通用查询
- 传统BI为什么注定会失败?
- webpack 配置详解
- LeetCode 67. 二进制求和
- 一层循环时间复杂度_算法的时间与空间复杂度(一看就懂)
- JavaScript中的高级特性及特别对象、属性和方法
- perl 判断不包含某字符串
- 对讲机怎么用_对讲机防水透气解决方案是怎么做的?
- 【渝粤教育】广东开放大学 知识产权法 形成性考核 (44)
- Niushop开源微信商城+小程序商城源码
- phpbb简体中文语言包的安装
- 如何生成一个APP_ID
- 游戏开发:目前主流游戏引擎的分析报告
- Navicat 的使用
- 2017ACM ICPC Asia Regional-Daejeon H-Rock Paper Scissors[ FFT]
- ifft java_在Matlab中的fft / ifft反卷积
- Qt编写高仿苹果MAC电脑输入法(支持触摸滑动选词)
- 番茄花园系统剖析 下
- HaaS100 云端钉一体智能语音播放器设计
热门文章
- 容器技术Docker K8s 46 Serverless Kubernetes(ASK)详解-场景应用
- 翻译:如何理解K-means的缺点
- 易筋SpringBoot 2.1 | 第十三篇:SpringBoot综合应用多个DataSoure
- 万兆网口和千兆网口区别_万兆网卡的安装以及注意事项
- 重载前自增运算符和后自增运算符
- python游戏程序中游戏对象是什么_Python游戏编程入门
- 来电通java版_Java 程序员常用的 11 款免费 IDE 编辑器
- MongoDB 在windows shell环境下的基本操作和命令的使用示例(二)
- 【CF 706】(C.Hard problem) + (D.Vasiliy's Multiset) + (E.Working routine)【最短路、01字典树、十字链表模拟】
- 实数域上的压缩映射不动点原理