Spring 学习日记 (1)配置文件的装载
这次就不啰嗦 直接上 (以下资料都是查互联网之后加上自己的理解整理出来的 纯粹作为笔记自己学习下 没有其他什么意思 )
Spring最重要的就是IOC和AOP 对于IOC来说在配置文件中你配置好了对象和对象之间的关系 但是你如何来读取这个配置文件呢
首先来看读取配置文件的XmlBeanFactory 在这里面需要指定Resource对象也就是xml文件
XmlBeanFactory继承自DefaultListableBeanFactory,扩展了从xml文档中读取bean definition的能力。
从本质上讲,XmlBeanFactory等同于
DefaultListableBeanFactory+XmlBeanDefinitionReader ,如果有更好的需求,可以考虑使用DefaultListableBeanFactory+XmlBeanDefinitionReader方案,因为该方案可以从多个xml文件读取资源,并且在解析xml上具有更灵活的可配置性。
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException
{ super(parentBeanFactory); reader = new XmlBeanDefinitionReader(this); reader.loadBeanDefinitions(resource);
}
于是这样可以得到XmlBeanFactory 引用资源的方法
Resource resource = new ClassPathResource("appcontext.xml");
BeanFactory factory = new XmlBeanFactory(resource);
其实在有的代码中也见过这么写的 不过这种方法要说到另外的东西 在后面的日记中会重新说明的
ClassPathResource res = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
同时在上述XmlBeanFactory源码中 reader是XmlBeanDefinitionReader的实例,XmlBeanDefinitionReader类继承自AbstractBeanDefinitionReader类
瞧一眼XmlBeanDefinitionReader类的源码
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader
同样的把AbstractBeanDefinitionReader类的源码818
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader
是不是感觉很像传销呢 = 。 =
public interface BeanDefinitionReader
到头了 那么来看下接口的方法 重要的就是loadBeanDefinitions方法
public abstract int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; public abstract int loadBeanDefinitions(Resource aresource[]) throws BeanDefinitionStoreException; public abstract int loadBeanDefinitions(String s) throws BeanDefinitionStoreException; public abstract int loadBeanDefinitions(String as[]) throws BeanDefinitionStoreException;
所以可以简单的总结一下 在XmlBeanFactory 实例化XmlBeanDefinitionReader 之后,同时调用一系列七大姑八大姨的关系在XmlBeanDefinitionReader 中实现的loadBeanDefinitions 方法 来加载 bean 配置并把 bean 配置注册到 XmlBeanFactory 中
当然读取到XmlBeanFactory中并不代表结束了 在Spring框架中有的时候会遇到个这么一个错误
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions........
看在XmlBeanDefinitionReader似乎还有一个这么东西 这个从哪来的呢 这个不急 先把那个do去掉不就是LoadBeanDefinitions方法了吗 那么把LoadBeanDefinitions方法拆开会怎么样呢?
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //这里是调用的入口。进入下面的方法 return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (this.logger.isInfoEnabled()) { this.logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } Set currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); try { //这里得到XML文件,并得到IO的InputSource准备进行读取。 InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //在这里发现其实在LoadBeanDefinitions方法中调用了doLoadBeanDefinitions方法int i = doLoadBeanDefinitions(inputSource, encodedResource.getResource()); inputStream.close(); currentResources.remove(encodedResource); if (currentResources.isEmpty()) this.resourcesCurrentlyBeingLoaded.remove(); return i; } finally { InputStream inputStream; 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(); } throw localObject2; }
那么接下来去doLoadBeanDefinitions方法中查一下水表吧
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); //这里取得XML文件的Document对象,这个解析过程是由 documentLoader完成的,这个documentLoader是DefaultDocumentLoader,在定义documentLoader的地方创建 Document doc = this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); //这里启动的是对BeanDefinition解析的详细过程,这个解析会使用到Spring的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); } //调用这个方法 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
如此是否一目了然了呢 当然饭要一口一口吃 不过因为本喵自己学的也是懵懵然 所以大概捋一下顺序吧
Document doc = documentLoader.loadDocument(inputSource, getEntityResolver(), errorHandler, validationMode, isNamespaceAware());
可以看出通过一个叫documentLoader的东西的loadDocument方法来加载配置文件形成DOM, 来看看documentLoader
...
private DocumentLoader documentLoader
...
documentLoader = new DefaultDocumentLoader();
...
那么接着来看DefaultDocumentLoader
public class DefaultDocumentLoader implements DocumentLoader
... public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if(logger.isDebugEnabled()) logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); }
于是至此 已完成了从配置文件读取到Domcument. 接着要开始解析Dom了
自然是通过sax解析得到Dom的 至于怎么解析 这个太高深了 稍微的818吧 其实我也不是很懂 T.T 以下是为大神的文章 copy过来大家一起膜拜下 其实这些对于初学者来说可以有空的研究下 如果持续纠结在这里面有时觉得得不偿失 毕竟理解还是要跟经验走的
关于具体的Spring BeanDefinition的解析 是在BeanDefinitionParserDelegate中完成的 这个类里包含了各种Spring Bean定义规则的处理
我们举一个例子来分析这个处理过程 比如我们最熟悉的对Bean元素的处理怎样完成的,也就是我们在XML定义文件中出现的<bean></bean>
这个最常见的元素信息是怎样被处理的。在这里,我们会看到那些熟悉的BeanDefinition定义的处理,比如id、name、aliase等属性元素。把这些元素的值从XML文件相应的元素的属性中读取出来以后,会被设置到生成的BeanDefinitionHolder中去。这些属性的解析还是比较简单的。对于其他元素配置的解析,比如各种Bean的属性配置,通过一个较为复杂的解析过程,这个过程是由parseBeanDefinitionElement来完成的。解析完成以后,会把解析结果放到BeanDefinition对象中并设置到BeanDefinitionHolder中去。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { String id = ele.getAttribute("id"); String nameAttr = ele.getAttribute("name"); List aliases = new ArrayList(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, ",; "); aliases.addAll((Collection)Arrays.asList(nameArr)); } String beanName = id; if ((!StringUtils.hasText(beanName)) && (!aliases.isEmpty())) { beanName = (String)aliases.remove(0); if (this.logger.isDebugEnabled()) { this.logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } //这个方法会引发对bean元素的详细解析 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); String beanClassName = beanDefinition.getBeanClassName(); if ((beanClassName != null) && (beanName.startsWith(beanClassName)) && (beanName.length() > beanClassName.length()) && (!this.readerContext.getRegistry().isBeanNameInUse(beanClassName))) { aliases.add(beanClassName); } } if (this.logger.isDebugEnabled()) this.logger.debug("Neither XML 'id' nor 'name' specified - using generated bean name [" + beanName + "]"); } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
在具体生成BeanDefinition以后。我们举一个对property进行解析的例子来完成对整个BeanDefinition载入过程的分析,还是在类BeanDefinitionParserDelegate的代码中,它对BeanDefinition中的定义一层一层地进行解析,比如从属性元素集合到具体的每一个属性元素,然后才是对具体的属性值的处理。根据解析结果,对这些属性值的处理会封装成PropertyValue对象并设置到BeanDefinition对象中去,如以下代码清单所示。
public void parsePropertyElements(Element beanEle, BeanDefinition bd) { //遍历所有bean元素下定义的property元素 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if ((isCandidateElement(node)) && (nodeNameEquals(node, "property"))) //在判断是property元素后进入parsePropertyElement解析 parsePropertyElement((Element)node, bd); } }
进入parsePropertyElement解析
public void parsePropertyElement(Element ele, BeanDefinition bd) { //这里取得property的名字 String propertyName = ele.getAttribute("name"); if (!StringUtils.hasLength(propertyName)) { error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { //如果同一个bean中已经有同名的存在,则不进行解析,直接返回。也就是说,如果在同一个bean中有同名的property设置,那么起作用的只是第一个。 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } //这里是解析property值的地方,返回的对象对应对Bean定义的property属性设置的解析结果,这个解析结果会封装到PropertyValue对象中,然后设置到BeanDefinitionHolder中去 Object val = parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } this.parseState.pop(); }
parsePropertyValue方法具体代码
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = propertyName != null ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"; NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if ((!(node instanceof Element)) || (nodeNameEquals(node, "description")) || (nodeNameEquals(node, "meta"))) continue; if (subElement != null) { error(elementName + " must not contain more than one sub-element", ele); } else { subElement = (Element)node; } } boolean hasRefAttribute = ele.hasAttribute("ref"); boolean hasValueAttribute = ele.hasAttribute("value"); //这里判断property的属性,是ref还是value,不允许同时是ref和value。 if (((hasRefAttribute) && (hasValueAttribute)) || ( ((hasRefAttribute) || (hasValueAttribute)) && (subElement != null))) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } //如果是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref的信息。 if (hasRefAttribute) { String refName = ele.getAttribute("ref"); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } //如果是value,创建一个value的数据对象TypedStringValue ,这个对象封装了value的信息。 if (hasValueAttribute) { TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute("value")); valueHolder.setSource(extractSource(ele)); return valueHolder; } //如果还有子元素,触发对子元素的解析 if (subElement != null) { return parsePropertySubElement(subElement, bd); } error(elementName + " must specify a ref or value", ele); return null; }
经过这样一层一层的解析,我们在XML文件中定义的BeanDefinition就被整个给载入到了IoC容器中,并在容器中建立了数据映射。在IoC容器中建立了对应的数据结构,或者说可以看成是POJO对象在IoC容器中的映像,这些数据结构可以以AbstractBeanDefinition为入口,让IoC容器执行索引、查询和操作。
写的有些跑题了 那么回归正题 继续来818 doLoadBeanDefinitions这个方法
有的时候在部署spring时还会遇到这个问题
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions
其实就是在doLoadBeanDefinitions()方法中的调用的registerBeanDefinitions 这个方法
如下
return registerBeanDefinitions(doc, resource);
来看看registerBeanDefinitions的实现
...
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
...
于是找到一个BeanDefinitionDocumentReader接口 实际上Spring对它有一个默认的实现类叫DefaultBeanDefinitionDocumentReader 我们来算一下这个软妹子(不要问我为什么!捂脸) 的生辰八字
public class DefaultBeanDefinitionDocumentReader Implement BeanDefinitionDocumentReader
public interface BeanDefinitionDocumentReader
在这个接口中我们找到了唯一的一个方法
public abstract void registerBeanDefinitions(Document document, XmlReaderContext xmlreadercontext) throws BeanDefinitionStoreException;
对于上述方法所带的两个参数 一个是Document模型 这个应该是我们读取配置文件获取到的 另一个是XmlReaderContext对象 我们在上面方法中看到是通过createReaderContext(resource)得到的
那么接着瞧一瞧怎么实现的
protected XmlReaderContext createReaderContext(Resource resource)
{ if(namespaceHandlerResolver == null) namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); return new XmlReaderContext(resource, problemReporter, eventListener, sourceExtractor, this, namespaceHandlerResolver);
}
于是知道是通过构造函数new出来的 并且自带一个参数resource 再继续来看DefaultBeanDefinitionDocumentReader对BeanDefinitionDocumentReader的registerBeanDefinitions方法实现
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { ...
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
... parseBeanDefinitions(root, delegate); ... }
于是开始解析了 主要是通过parseBeanDefinitions方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
{ if(delegate.isDefaultNamespace(root.getNamespaceURI())) { NodeList nl = root.getChildNodes(); for(int i = 0; i < nl.getLength(); i++) { org.w3c.dom.Node node = nl.item(i); if(node instanceof Element) { Element ele = (Element)node; String namespaceUri = ele.getNamespaceURI(); if(delegate.isDefaultNamespace(namespaceUri)) parseDefaultElement(ele, delegate); else delegate.parseCustomElement(ele); } } } else { delegate.parseCustomElement(root); }
}
循环解析Domcument节点
parseDefaultElement方法和delegate的parseCustomElement方法
先来看parseDefaultElement方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
{ if(DomUtils.nodeNameEquals(ele, "import")) importBeanDefinitionResource(ele); else if(DomUtils.nodeNameEquals(ele, "alias")) processAliasRegistration(ele); else if(DomUtils.nodeNameEquals(ele, "bean")) processBeanDefinition(ele, delegate);
}
这就很清楚了, 就是根据节点的名称作不同解析, 如我们Spring配置文件中常有以下几种配置
<import resource="classpath:xxx" />
<bean id="xxx" class="xxx.xxx.xxx" />
<alias name="xxxx" alias="yyyyy"/>
对节点, 调用importBeanDefinitionResource方法解析, 此方法中, 又回到第一步读取配置文件并解析. 如此递归循环.
...
Resource relativeResource = getReaderContext().getResource().createRelative(location);
int importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
...
对<alias>
节点, 调用processAliasRegistration进行别名解析
我们主要看对节点调用processBeanDefinition进行解析
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
{ BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if(bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch(BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); }
}
嘿嘿, 又用到delegate对象了, 且调用它的BeanDefinitionHolder方法, 返回一个BeanDefinitionHolder, 进去看它的parseBeanDefinitionElement方法
public class BeanDefinitionParserDelegate
{ private final Set usedNames = new HashSet(); ... public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { ... 解析id, name等属性, 并验证name是否唯一, 并将name保存在usedNames中 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); ... return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } ...
}
可以看到, 在BeanDefinitionHolder中保存了BeanDefinition的定义
OK, 重头戏开始, 最经典的部分出现了, 请看parseBeanDefinitionElement方法
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)
{ ... 代码太长, 请参考具体代码 AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(parent, className, readerContext.getBeanClassLoader()); ... return abstractbeandefinition; ...
}
在这个方法中, 解析了bean的所有属性, 有最常用的class, scope, lazy-init等等. 并返回一个AbstractBeanDefinition实例. 至于具体怎么解析, 就只能进一步跟踪了, 不过既然到了这一步, 已经明白了它的基本原理, 具体实现就不作介绍
这一步将节点解析成BeanDefinitionHolder对象, 再看看如何注册, 回到DefaultBeanDefinitionDocumentReader的processBeanDefinition方法
看到对解析到的bdHolder对象又做了decorateBeanDefinitionIfRequired操作 接着调用了BeanDefinitionReaderUtils的registerBeanDefinition方法注册bdHolder, 来看看如何实现的
public class BeanDefinitionReaderUtils
{ public static void registerBeanDefinition(BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory) throws BeanDefinitionStoreException { String beanName = bdHolder.getBeanName(); beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition()); String aliases[] = bdHolder.getAliases(); if(aliases != null) { for(int i = 0; i < aliases.length; i++) beanFactory.registerAlias(beanName, aliases[i]); } }
}
看吧, 又调用了BeanDefinitionRegistry的registerBeanDefinition方法, 跟踪之 (这个要看DefaultListableBeanFactory的实现)
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry
{ private final Map beanDefinitionMap; private final List beanDefinitionNames; ... public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { ... Object oldBeanDefinition = beanDefinitionMap.get(beanName); ... beanDefinitionMap.put(beanName, beanDefinition); ... }
}
这里, 看到了一个最最重要的对象就是beanDefinitionMap, 这个map存放了所有的bean对象, 和我们通常讲的容器概念最为接近, getBean时实际是也是从这里辚取对象, 相同的还有一个beanDefinitionNames, 但这个只保存bean的名称
完成上面之后, 还有一步操作beanFactory.registerAlias(beanName, aliases[i]);
这个实现实际是上AbstractBeanFactory抽象类所定义的
于是从定义 -> 定位 -> 装载 -> 注册 就完成只是在Spring3.1中已经将XmlBeanFactory 设置成不赞成使用了 推荐是DefaultListableBeanFactory和XmlBeanDefinitionReader来进行替换 不过今天就写到这吧 我要回家玩刺客信条呢 明天来公司加班再说其他的吧!
Spring 学习日记 (1)配置文件的装载相关推荐
- spring学习笔记之配置文件applicationContext.xml
1:spring中,用配置文件时 <bean>的<scope>属性是singleton时在创建容器时创建对象,创建一个容器在,对象在: <bean>的<sco ...
- Spring 学习日记 (四) Spring 整合Mybaits 和 struts2 框架的配置文件
其实也是挺无奈的 这东西永远这一棒子那一锤子的 太不系统了 哎 没办法 跟着项目走吧 首先准备的JAR包 需要配置的几个配置文件 配置spring applicationContext.xml ...
- SpringMVC学习日记 1.Spring框架
SpringMVC学习日记 1.Spring框架 Spring简介 Spring框架是一个开源框架,由Rod Johnson组织和开发,生产目的在于简化企业级应用的开发. 主要特性 非侵入(no-in ...
- spring学习笔记03-spring-DI-依赖注入详解(通过xml配置文件来配置依赖注入)
spring学习笔记03-spring-DI-依赖注入详解 1.概念 2.构造函数注入 3.set方法注入 4.集合的注入 需要被注入的实体对象 package com.itheima.service ...
- 【Spring Boot学习笔记】——配置文件
两种类型的配置文件 properties和yml 作为全局配置文件,配置文件名是固定的: application.properties application.yml 配置文件的作用:修改Spring ...
- Spring学习总结一
Spring框架IoC与DI思想及应用 Spring学习总结一 1.Spring是什么 2.Spring的优点 2.1.关键概念 2.2.Spring的优点 3.Spring的架构图 3.1.核心容器 ...
- Spring学习(五)bean装配详解之 【XML方式配置】
本文借鉴:Spring学习(特此感谢!) 一.配置Bean的方式及选择 配置方式 在 XML 文件中显式配置 在 Java 的接口和类中实现配置 隐式 Bean 的发现机制和自动装配原则 方式选择的原 ...
- Spring 学习总结笔记【七、AOP面向切面编程】
往期文章: Spring 学习总结笔记[一.快速入门] Spring 学习总结笔记[二.IoC-控制反转] Spring 学习总结笔记[三.注解开发] Spring 学习总结笔记[四.整合Junit] ...
- spring学习12 -Spring 框架模块以及面试常见问题注解等
以下为spring常见面试问题: 1.Spring 框架中都用到了哪些设计模式? Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的: 代理模式-在AOP和remoting中被用的比较 ...
最新文章
- mysql删除数据后不释放空间问题
- TensorFlow Wide And Deep 模型详解与应用 TensorFlow Wide-And-Deep 阅读344 作者简介:汪剑,现在在出门问问负责推荐与个性化。曾在微软雅虎工作,
- 10000 字讲清楚 Spring Boot 注解原理
- 基本权限管理框架,开通淘宝支付
- LeetCode 1552. 两球之间的磁力(极小极大化 二分查找)
- 设置中文linux输入ubuntu,Linux_ubuntu怎么设置成中文?ubuntu中文设置图文方法, 很多朋友安装ubuntu后,发 - phpStudy...
- image 微信小程序flex_微信小程序进阶-flex布局
- Java基础0308
- 还原二叉树--根据后序中序输出先序
- LeetCode刷题-四因数
- 关于打卡值班制度---一个excel开发工具小函数
- 老男孩教育 | 从小白进军IT,他仅用了四个月的时间!
- 2015.2,对任意正整数n,求xn,要求运算时间复杂度为o(logn).例如x30=x15*x15.
- 由113号元素鉨114号元素夫115号元素镆元素汞银金等元素构成的超导体
- 音视频开发系列1:音视频开发基本概念
- [内核内存] [arm64] 内存回收2---快速内存回收和直接内存回收
- gunicorn、uwsgi、uvicorn认识
- 【ssd】M.2的SATA,PCI-x2(Socket 2 ),PCI-x4(Socket 3)了解一下,老程序员都快被新硬件搞蒙圈了
- Python 之 列表推导式
- 辅流式沉淀池固体负荷计算方法_辐流式沉淀池设计计算
热门文章
- 提高Tomcat并发量的几种方法
- 一周看点 | Docker创始人再创业;谷歌Fuchsia OS负责人离职;淘宝小范围内测微信支付;蒋凡卸任淘宝董事长...
- 可以跟踪军事和情报人员的啤酒点评应用程序
- 使用Spring AOP自定义注解方式实现用户操作日志记录
- GYP(Generate Your Project)介绍
- python中的import指的是什么?
- SQL的交集并集差集
- cmd安装mysql时出现:计算机中丢失MSVCP120.dll 的解决办法
- 关于Segmentation fault(段错误)探究
- 神经网络算法可以解决什么问题