如何把接口配置信息存在本地并从re文件管理器中读取配置信息

Java中spring读取配置文件的几种方法示例
转载 &更新时间:日 13:59:50 & 作者:stack_over_flow
本篇文章中主要介绍了Java中spring读取配置文件的几种方法示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
Spring读取配置XML文件分三步:
一.新建一个Java Bean:
public class HelloBean {
private String helloW
public String getHelloWorld() {
return helloW
public void setHelloWorld(String helloWorld) {
this.helloWorld = helloW
二.构建一个配置文件bean_config.xml:
&?xml version="1.0" encoding="UTF-8"?&
&!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd" &
  &bean id="helloBean" class="springdemo.HelloBean"&
    &property name="helloWorld"&
      &value&Hello!chb!&/value&
    &/property&
  &/bean&
三.读取配置文件:
1.利用ClassPathXmlApplicationContext:
ApplicationContext context = new ClassPathXmlApplicationContext("bean_config.xml");
//这种用法不够灵活,不建议使用。
HelloBean helloBean = (HelloBean)context.getBean("helloBean");
System.out.println(helloBean.getHelloWorld());
ClassPathXmlApplicationContext实现了接口ApplicationContext,ApplicationContext实现了BeanFactory。其通过jdom进行XML配置文件的读取,并构建实例化Bean,放入容器内。
public interface BeanFactory {
public Object getBean(String id);
//实现类ClassPathXmlApplicationContext
import java.lang.reflect.M
import java.util.HashM
import java.util.L
import java.util.M
import org.jdom.D
import org.jdom.E
import org.jdom.input.SAXB
public class ClassPathXmlApplicationContext implements BeanFactory {
private Map&String , Object& beans = new HashMap&String, Object&();
//(IOC:Inverse of Control/DI:Dependency Injection)
public ClassPathXmlApplicationContext() throws Exception {
SAXBuilder sb=new SAXBuilder();
Document doc=sb.build(this.getClass().getClassLoader().getResourceAsStream("beans.xml")); //构造文档对象
Element root=doc.getRootElement(); //获取根元素HD
List list=root.getChildren("bean");//取名字为disk的所有元素
for(int i=0;i&list.size();i++){
Element element=(Element)list.get(i);
String id=element.getAttributeValue("id");
String clazz=element.getAttributeValue("class");
Object o = Class.forName(clazz).newInstance();
System.out.println(id);
System.out.println(clazz);
beans.put(id, o);
for(Element propertyElement : (List&Element&)element.getChildren("property")) {
String name = propertyElement.getAttributeValue("name"); //userDAO
String bean = propertyElement.getAttributeValue("bean"); //u
Object beanObject = beans.get(bean);//UserDAOImpl instance
String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
System.out.println("method name = " + methodName);
Method m = o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);
m.invoke(o, beanObject);
public Object getBean(String id) {
return beans.get(id);
BeanFactory是一个很根的接口,ApplicationContext和ClassPathXmlApplicationContext都实现了接口BeanFactory,所以也可以这么写:
ApplicationContext context = new ClassPathXmlApplicationContext("bean_config.xml");
HelloBean helloBean = (HelloBean)context.getBean("helloBean");
BeanFactory factory= new ClassPathXmlApplicationContext("bean_config.xml");
HelloBean helloBean = (HelloBean)factory.getBean("helloBean");
ClassPathXmlApplicationContext层级关系如下:
2.利用FileSystemResource读取
Resource rs = new FileSystemResource("D:/software/tomcat/webapps/springWebDemo/WEB-INF/classes/bean_config.xml");
BeanFactory factory = new XmlBeanFactory(rs);
HelloBean helloBean = (HelloBean)factory.getBean("helloBean");
System.out.println(helloBean.getHelloWorld());
注意:利用FileSystemResource,则配置文件必须放在project直接目录下,或者写明绝对路径,否则就会抛出找不到文件的异常。
&Spring读取properties配置文件
介绍两种技术:利用spring读取properties 文件和利用java.util.Properties读取:
一.利用spring读取properties 文件
还利用上面的HelloBean.java文件,构造如下bean_config.properties文件:
helloBean.class=springdemo.HelloBean
helloBean.helloWorld=Hello!HelloWorld!
属性文件中的"helloBean"名称即是Bean的别名设定,.class用于指定类来源。
然后利用org.springframework.beans.factory.support.PropertiesBeanDefinitionReader来读取属性文件。
BeanDefinitionRegistry reg = new DefaultListableBeanFactory();
PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(reg);
reader.loadBeanDefinitions(new ClassPathResource("bean_config.properties"));
BeanFactory factory = (BeanFactory)
HelloBean helloBean = (HelloBean)factory.getBean("helloBean");
System.out.println(helloBean.getHelloWorld());
二.利用java.util.Properties读取属性文件
比如,我们构造一个ip_config.properties来保存服务器ip地址和端口,如:
ip=192.168.0.1
我们可以用如下程序来获得服务器配置信息:
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("ip_config.properties");
Properties p = new Properties();
p.load(inputStream);
} catch (IOException e1) {
e1.printStackTrace();
System.out.println("ip:"+p.getProperty("ip")+",port:"+p.getProperty("port"));
三.用接口类WebApplicationContext来取。
private WebApplicationC
wac =WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
wac = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
JdbcTemplate jdbcTemplate = (JdbcTemplate)ctx.getBean("jdbcTemplate");
其中,jdbcTemplate为spring配置文件中的一个bean的id值。
这种用法比较灵活,spring配置文件在web中配置启动后,该类会自动去找对应的bean,而不用再去指定配置文件的具体位置。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具curl命令_Linux curl 命令用法详解:利用URL规则在命令行下工作的文件传输工具当前位置: &
& curlcurl命令
curl命令是一个利用URL规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称curl为下载工具。作为一款强力工具,curl支持包括HTTP、HTTPS、等众多协议,还支持POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。做网页处理流程和数据检索自动化,curl可以祝一臂之力。 语法
curl(选项)(参数) 选项 -a/--append 上传文件时,附加到目标文件 -A/--user-agent &string& 设置用户代理发送给服务器 -anyauth 可以使用&任何&身份验证方法 -b/--cookie &name=string/& cookie字符串或文件读取位置 & & &--basic 使用HTTP基本验证 -B/--use-ascii 使用ASCII /文本传输 -c/--cookie-jar &file& 操作结束后把cookie写入到这个文件中 -C/--continue- &offset& 断点续转 -d/--data &data& HTTP POST方式传送数据 & & &--data-ascii &data& 以ascii的方式post数据 & & &--data-binary &data& 以二进制的方式post数据 & & &--negotiate 使用HTTP身份验证 & & &--digest 使用数字身份验证 & & &--disable-eprt 禁止使用EPRT或LPRT & & &--disable-epsv 禁止使用EPSV -D/---header &file& 把header信息写入到该文件中 & & &--egd-file &file& 为随机数据(SSL)设置EGD socket路径 & & &--tcp-nodelay 使用TCP_NODELAY选项 -e/--referer 来源网址 -E/--cert &cert[:]& 客户端证书文件和密码 (SSL) & & &--cert- &type& 证书文件类型 (DER/PEM/ENG) (SSL) & & &--key &key& 私钥文件名 (SSL) & & &--key-type &type& 私钥文件类型 (DER/PEM/ENG) (SSL) & & &--pass &pass& 私钥密码 (SSL) & & &--engine &eng& 加密引擎使用 (SSL). &--engine list& for list & & &--cacert &file& CA证书 (SSL) & & &--capath &directory& CA目录 (made using c_rehash) to verify peer against (SSL) & & &--ciphers &list& SSL密码 & & &--compressed 要求返回是压缩的形势 (using deflate or ) & & &--connect-timeout &seconds& 设置最大请求时间 & & &--create- 建立本地目录的目录层次结构 & & &--crlf 上传是把LF转变成CRLF -f/--fail 连接失败时不显示http错误 & & &--ftp-create-dirs 如果远程目录不存在,创建远程目录 & & &--ftp-method [multicwd/nocwd/singlecwd] 控制CWD的使用 & & &--ftp-pasv 使用 PASV/EPSV 代替端口 & & &--ftp-skip-pasv- 使用PASV的时候,忽略该IP地址 & & &--ftp-ssl 尝试用 SSL/TLS 来进行ftp数据传输 & & &--ftp-ssl-reqd 要求用 SSL/TLS 来进行ftp数据传输 -F/--form &name=content& 模拟http表单提交数据 & & &--form-string &name=string& 模拟http表单提交数据 -g/--globoff 禁用网址序列和范围使用{}和[] -G/--get 以get的方式来发送数据 -H/--header &line& 自定义头信息传递给服务器 & & &--ignore-content-length 忽略的HTTP头信息的长度 -i/--include 输出时包括protocol头信息 -I/-- 只显示请求头信息 -j/--junk-session-cookies 读取文件进忽略session cookie & & &--interface &interface& 使用指定网络接口/地址 & & &--krb4 &level& 使用指定安全级别的krb4 -k/--insecure 允许不使用证书到SSL站点 -K/--config 指定的配置文件读取 -l/--list-only 列出ftp目录下的文件名称 & & &--limit-rate &rate& 设置传输速度 & & &--local-port&NUM& 强制使用本地端口号 -m/--max- &seconds& 设置最大传输时间 & & &--max-redirs &num& 设置最大读取的目录数 & & &--max-filesize &bytes& 设置最大下载的文件总量 -M/--manual 显示全手动 -n/--netrc 从netrc文件中读取用户名和密码 & & &--netrc-optional 使用 .netrc 或者 URL来覆盖-n & & &--ntlm 使用 HTTP NTLM 身份验证 -N/--no-buffer 禁用缓冲输出 -o/--output 把输出写到该文件中 -O/--remote-name 把输出写到该文件中,保留远程文件的文件名 -p/--proxytunnel 使用HTTP代理 & & &--proxy-anyauth 选择任一代理身份验证方法 & & &--proxy-basic 在代理上使用基本身份验证 & & &--proxy-digest 在代理上使用数字身份验证 & & &--proxy-ntlm 在代理上使用ntlm身份验证 -P/--ftp-port &address& 使用端口地址,而不是使用PASV -q 作为第一个参数,关闭 .curlrc -Q/--quote &cmd& 文件传输前,发送命令到服务器 -r/--range &range& 检索来自HTTP/1.1或FTP服务器字节范围 --range-file 读取(SSL)的随机文件 -R/--remote-time 在本地生成文件时,保留远程文件时间 & & &--retry &num& 传输出现问题时,重试的次数 & & &--retry-delay &seconds& 传输出现问题时,设置重试间隔时间 & & &--retry-max-time &seconds& 传输出现问题时,设置最大重试时间 -s/--silent 静默模式。不输出任何东西 -S/--show-error 显示错误 & & &--socks4 &[:port]& 用socks4代理给定主机和端口 & & &--socks5 &host[:port]& 用socks5代理给定主机和端口 & & &--stderr &file& & -t/---option &OPT=val& Telnet选项设置 & & &--trace &file& 对指定文件进行debug & & &--trace-ascii &file& Like --跟踪但没有hex输出 & & &--trace-time 跟踪/详细输出时,添加时间戳 -T/--upload-file &file& 上传文件 & & &--url &URL& Spet URL to work with -u/--user &user[:password]& 设置服务器的用户和密码 -U/--proxy-user &user[:password]& 设置代理用户名和密码 -/---out [format] 什么输出完成后 -x/--proxy &host[:port]& 在给定的端口上使用HTTP代理 -X/--request && 指定什么命令 -y/--speed-time 放弃限速所要的时间,默认为30 -Y/--speed-limit 停止传输速度的限制,速度时间 实例 文件下载 curl命令可以用来执行下载、发送各种HTTP请求,指定HTTP头部等操作。如果系统没有curl可以使用
curl安装,也可以下载安装。curl是将下载文件输出到stdout,将进度信息输出到stderr,不显示进度信息使用--silent选项。
curl URL --silent 这条命令是将下载文件输出到终端,所有下载的数据都被写入到stdout。 使用选项-O将下载的数据写入到文件,必须使用文件的绝对地址:
curl http://.linuxde.net/text.iso --silent -O 选项-o将下载数据写入到指定名称的文件中,并使用--progress显示进度条:
curl http://man.linuxde.net/.iso -o filename.iso --progress
######################################### 100.0% 断点续传 curl能够从特定的文件偏移处继续下载,它可以通过指定一个便宜量来下载部分文件:
curl URL/File -C 偏移量
#偏移量是以字节为单位的整数,如果让curl自动推断出正确的续传位置使用-C -:
curl -C -URL 使用curl设置参照页字符串 参照页是位于HTTP头部中的一个字符串,用来表示用户是从哪个页面到达当前页面的,如果用户点击网页A中的某个连接,那么用户就会跳转到B网页,网页B头部的参照页字符串就包含网页A的URL。 使用--referer选项指定参照页字符串:
curl --referer http://www.google.com http://man.linuxde.net 用curl设置cookies 使用--cookie &COKKIES&选项来指定cookie,多个cookie使用分号分隔:
curl http://man.linuxde.net --cookie &user=pass=123456& 将cookie另存为一个文件,使用--cookie-jar选项:
curl URL --cookie-jar cookie_file 用curl设置用户代理字符串 有些网站访问会提示只能使用IE浏览器来访问,这是因为这些网站设置了检查用户代理,可以使用curl把用户代理设置为IE,这样就可以访问了。使用--user-agent或者-A选项:
curl URL --user-agent &Mozilla/5.0&
curl URL -A &Mozilla/5.0& 其他HTTP头部信息也可以使用curl来发送,使用-H&头部信息& 传递多个头部信息,例如:
curl -H &Host:man.linuxde.net& -H &-language:zh-cn& URL curl的带宽控制和下载配额 使用--limit-rate限制curl的下载速度:
curl URL --limit-rate 50k 命令中用k(千字节)和m(兆字节)指定下载速度限制。 使用--max-filesize指定可下载的最大文件大小:
curl URL --max-filesize bytes 如果文件大小超出限制,命令则返回一个非0退出码,如果命令正常则返回0。 用curl进行认证 使用curl选项&-u&可以完成HTTP或者FTP的认证,可以指定密码,也可以不指定密码在后续操作中输入密码:
curl -u user: http://man.linuxde.net
curl -u user http://man.linuxde.net 只打印响应头部信息 通过-I或者-head可以只打印出HTTP头部信息:
[root@localhost text]# curl -I http://man.linuxde.net
HTTP/1.1 200 OK
Server: nginx/1.2.5
: Mon, 10 Dec :34 GMT
Content-Type: text/ charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Pingback: http://man.linuxde.net/xmlrpc.送福利在Linux命令大全(man.linuxde.net)可以查询您所需要的Linux命令教程和相关实例。如果您觉得本站内容对您有所帮助,请推荐给更多需要帮助的人。Spring源码分析:配置文件读取流程 - ImportNew
| 标签: ,
Spring配置文件读取流程本来是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一起的,这两天在看Spring自定义标签的时候,感觉对Spring配置文件读取流程还是研究得不够,因此将Spring配置文件读取流程部分从之前的文章拆出来单独成为一文。
为了看一下Spring配置文件加载流程,先定义一个bean.xml:
&?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-3.0.xsd&&
&bean id=&beanPostProcessorBean& class=&org.xrq.action.BeanPostProcessorBean& /&
&bean id=&beanFactoryPostProcessorBean& class=&org.xrq.action.BeanFactoryPostProcessorBean& /&
&bean id=&multiFunctionBean& class=&org.xrq.action.MultiFunctionBean& init-method=&initMethod&&
&property name=&propertyA& value=&abc& /&
至于Bean是什么并不重要,有配置文件就够了。
Bean定义加载流程—-从Refresh到Bean定义加载前
首先看一下Bean加载前整个代码流程走向。Spring上下文刷新始于AbstractApplicationContext的refresh()方法:
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);
代码不全帖了,第7行的obtainFreshBeanFactory()方法进去:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug(&Bean factory for & + getDisplayName() + &: & + beanFactory);
return beanF
第2行的refreshBeanFactory()方法进去,它是AbstractApplicationContext的子类AbstractRefreshableApplicationContext中的方法:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanF
catch (IOException ex) {
throw new ApplicationContextException(&I/O error parsing bean definition source for & + getDisplayName(), ex);
首先第8行获取DefaultListableBeanFactory,然后执行第11行的方法,传入当前获取的BeanFactory,准备加载Bean定义。BeanFactory中有存储了些什么数据在【Spring源码分析】Bean加载流程概览一文中有画表格详细说明,看过表格的朋友应该知道为什么第8行要获取的是DefaultListableBeanFactory而不是它的接口BeanFactory,因为Bean定义存储在Map&String, BeanDefinition&中,这个Map的位置就是在DefaultListableBeanFactory里,因此这里直接获取DefaultListableBeanFactory并作为参数层层向后传,加载完Bean定义后直接向Map&String, BeanDefinition&里put键值对。
看下loadBeanDefinitions方法,它是AbstractRefreshableApplicationContext子类AbstractXmlApplicationContext中的一个方法:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
第3行的XmlBeanDefinitionReader是Bean加载的核心类,先构建出来,后面代码没什么值得看的,直接看第13行代码,传入XmlBeanDefinitionReader:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
由第8行的代码进去,这个就不跟了,直接走到XmlBeanDefinitionReader的父类AbstractBeanDefinitionReader的loadBeanDefinitions方法中:
public int loadBeanDefinitions(String location, Set&Resource& actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
&Cannot import bean definitions from location [& + location + &]: no ResourceLoader available&);
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
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 loadC
catch (IOException ex) {
throw new BeanDefinitionStoreException(
&Could not resolve bean definition resource pattern [& + location + &]&, ex);
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
if (logger.isDebugEnabled()) {
logger.debug(&Loaded & + loadCount + & bean definitions from location [& + location + &]&);
return loadC
我们研究Spring加载流程使用的ClassPathXmlApplicationContext是ResourcePatternResolver的实现类,进入第8行的判断,走第12行的方法,这里也不跟了,很简单,最终代码走到了XmlBeanDefinitionReader的loadBeanDefinitions方法中,也就是Bean定义加载的开始。
Bean定义加载流程—-Bena定义的存储
上面说到了Bean定义是存储在DefaultListableBeanFactory中的,我们来看一下具体代码:
/** Map of bean definition objects, keyed by bean name */
private final Map&String, BeanDefinition& beanDefinitionMap = new ConcurrentHashMap&String, BeanDefinition&();
/** List of bean definition names, in registration order */
private final List&String& beanDefinitionNames = new ArrayList&String&();
最终DefaultListableBeanFactory会先遍历beanDefinitionNames,从beanDefinitionMap中拿到对应的BeanDefinition,最终转为具体的Bean对象。BeanDefinition本身是一个接口,AbstractBeanDefinition这个抽象类存储了Bean的属性,看一下AbstractBeanDefinition这个抽象类的定义:
这个类的属性与方法很多,这里就列举了一些最主要的方法和属性,可以看到包含了bean标签中的所有属性,之后就是根据AbstractBeanDefinition中的属性值构造出对应的Bean对象。
Bean定义加载流程—-开始加载Bean定义
上面一部分的结尾说道,Bean定义加载的开始始于XmlBeanDefinitionReader的loadBeanDefinitions方法,看下loadBeanDefinitions方法定义:
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!&);
InputStream inputStream = encodedResource.getResource().getInputStream();
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
inputStream.close();
catch (IOException ex) {
throw new BeanDefinitionStoreException(
&IOException parsing XML document from & + encodedResource.getResource(), ex);
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
第17行根据XML文件获取输入字节流,接着流程走到23行doLoadBeanDefinitions方法:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
catch (BeanDefinitionStoreException 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);
首先是第4行,获取验证模式,代码不跟了,最终出来的是DetectMode,DetectMode的意思是XML文件的验证模式由XML文件本身决定,如果是DTD那就使用DTD验证,如果是XSD就使用XSD验证。
接着是第5行~第6行,这两行的作用是通过DOM得到org.w3c.dom.Document对象,Document将XML文件看成一棵树,Dociument即对这颗树数据结构的一个描述。
最近进入第7行,继续加载Bean定义的流程,跟一下registerBeanDefinitions方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// Read document based on new BeanDefinitionDocumentReader SPI.
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countB
因为是每个XML文件执行一次registerBeanDefinitions方法注册Bean定义,因此这整个方法的返回值表示的是当前XML里面一共注册了多少个Bean。直接进入第5行的代码,使用BeanDefintionDocumentReader的registerBeanDefinitions方法来注册Bean定义:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerC
logger.debug(&Loading bean definitions&);
Element root = doc.getDocumentElement();
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
preProcessXml(root);
parseBeanDefinitions(root, delegate);
postProcessXml(root);
这里面的方法,第9行的方法preProcessXml是个空方法,留给子类扩展用;第11行的方法postProcessXml是个空方法,留给子类扩展用。
第5行的方法得到根节点,也就是&beans …&&/beans&。
剩下的就是第7行的createHelper方法与第10行的parseBeanDefintions方法了,前者构造出一个Bean定义解析器的委托类,后者使用委托类解析Bean定义,下面分两部分分别来看。
Bean定义加载流程—-createHelper
先看createHelper,即根据根节点创建一个Bean定义解析器的委托类,看一下代码实现:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerC
logger.debug(&Loading bean definitions&);
Element root = doc.getDocumentElement();
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
preProcessXml(root);
parseBeanDefinitions(root, delegate);
postProcessXml(root);
第2行没有什么特别的,new一个BeanDefinitionParserDelegate出来,第3行的代码跟一下,用于设置默认属性:
public void initDefaults(Element root) {
populateDefaults(this.defaults, root);
this.readerContext.fireDefaultsRegistered(this.defaults);
跟一下第2行的代码:
protected void populateDefaults(DocumentDefaultsDefinition defaults, Element root) {
defaults.setLazyInit(root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE));
defaults.setMerge(root.getAttribute(DEFAULT_MERGE_ATTRIBUTE));
defaults.setAutowire(root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE));
defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE));
if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
defaults.setSource(this.readerContext.extractSource(root));
看到就是这个地方将&beans&标签下的default-lazy-init、default_merge、default_autowire、default-dependency-check、default-autowire-candidates、default-init-method、default-destroy-method这几个属性取出来,设置到DocumentDefaultsDefinition即defaults中。
Bean定义加载流程—-parseBeanDefintions
到了parseBeanDefintions方法了,这个方法开始真正遍历XML文件中的各个标签并转换为对应的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)
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
delegate.parseCustomElement(ele);
delegate.parseCustomElement(root);
首先说一下,这里的Namespace都是默认的Namespace,至于Namespace的问题,和自定义Spring标签相关,我想放到自定义Spring标签部分说,这里只要知道代码会进入第2行与第9行的判断即可。
第2行的判断进去,都在遍历Element下的节点不看了,直接跟第9行的代码parseDefaultElement:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
这边就是判断节点名称是import还是alias还是bean,是其中任意一个就进入相应的执行逻辑,import和alias不看了,这里就看Bean加载流程部分,也就是第9行的processBeanDefinition方法:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// 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));
先看第4行,第4行的意思是在需要的时候装饰Bean定义,比如AOP的场景会使用到,这个留在AOP的时候看这段代码。
再看第7行,第7行的意思是注册Bean定义,这在下一部分说,属于Bean定义加载流程的最后一步。
现在看来第2行的代码,顾名思义即解析Bean定义元素,跟一下代码:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List&String& aliases = new ArrayList&String&();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
String beanName =
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug(&No XML 'id' specified - using '& + beanName +
&' as bean name and & + aliases + & as aliases&);
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() & beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
if (logger.isDebugEnabled()) {
logger.debug(&Neither XML 'id' nor 'name' specified - & +
&using generated bean name [& + beanName + &]&);
catch (Exception ex) {
error(ex.getMessage(), ele);
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
第2行~第18行的代码都是用于获取BeanName,从这段逻辑中我们可以总结出BeanName定义的规则:
默认的BeanName就是&bean&标签重定义的id
&bean&标签中可以定义name属性,一个bean可以有多个别名(alias),都定义在name属性中,不同的别名以”,;”分割,假如beanId未定义,那么就以name属性中的第一个别名作为beanName
第20行~第22行的代码主要用于确保BeanName的唯一性,跟一下第21行的方法就知道,BeanName与Bean别名都会放在Set&String&中,然后每次加载Bean定义的时候都会去这个Set&String&中检查当前BeanName和Bean别名是否存在,如果存在就报错。
接着进入第24行的代码,开始解析Bean定义元素:
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className =
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
String parent =
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
catch (ClassNotFoundException ex) {
error(&Bean class [& + className + &] not found&, ele, ex);
catch (NoClassDefFoundError err) {
error(&Class that bean class [& + className + &] depends on not found&, ele, err);
catch (Throwable ex) {
error(&Unexpected failure during bean definition parsing&, ele, ex);
this.parseState.pop();
对这个方法逐行总结一下:
第4行,ParseState是一个基于栈的简单结构,在解析的流程中用于追踪逻辑上的位置,里面存放的是Entry接口。Entry接口有各种实现类比如AdviceEntry、AdvisorEntry、BeanEntry、PropertyEntry等,每一步操作开始将一个Entry推送至栈顶,每一步操作结束将Entry从栈顶弹出,这里将一个BeanEntry推送至栈顶,标识解析Bean定义开始
第6行~第9行的代码,用于获取Bean对应的类路径
第12行~第15行的代码,用于解析&bean&标签中的parent属性
第16行的代码,根据Bean对应的类路径以及parent,生成一个AbstractBeanDefinition,AbstractBeanDefinition是一个抽象类,生成出来的具体实例为GenericBeanDefinition
第18行的代码,就不跟了进去一看就很好理解,是用于解析Bean定义的属性的,包括scope、sington、abstract、lazy-init、autowire、dependency-check、depends-on、autowire-candidate、primary、init-method、destory-method、factory-method、factory-bean,createHelper部分说了这个方法会创建一个名为defaults的DocumentDefaultsDefinition,像lazy-init、autowire-candidate、init-method、destory-method未定义时都会尝试从DocumentDefaultsDefinition中获取
第19行用于设置Bean描述
第21行的方法parseMetaElements用于解析META元素
第22行的方法parseLookupOverrideSubElements用于解析&bean&标签下的&lookup-method&标签
第23行的方法parseReplacedMethodSubElements用于解析&bean&标签下的&replaced-method&标签,不过这个和&lookup-method&标签好像不太常用
第25行的方法parseConstructorArgElements用于解析&bean&标签下的&constructor-arg&标签,&constructor-arg&标签用于实现构造函数注入Bean属性
第26行的方法parsePropertyElements用于解析&bean&标签下的&property&标签,&property&标签是最常见的Bean属性注入的方式
第27行的方法parseQualifierElements用于解析&bean&标签下的&qualifier&标签,使用&qualifier&标签也是Spring属性注入的一种方式,不过不太常用
这样,就把整个Bean定义加载的流程跟完了,最后一步,就是将AbstractBeanDefinition写回到DefaultListableBeanFactory中了。
Bean定义加载流程—-Bean定义写回DefaultListableBeanFactory
最后一步,将Bean定义写回DefaultListableBeanFactory中。代码要追溯回DefaultBeanDefinitionDocumentReader的processBeanDefinition方法:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// 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));
Bean定义加载完毕后构造为一个BeanDefinitionHolder,第4行的代码之前说过的,用于在必要的情况下装饰Bean定义先不管。
第7行的代码用于注册Bean定义,跟一下代码:
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);
跟一下第7行的方法,调用DefaultListableBeanFactory的registerBeanDefinition方法:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, &Bean name must not be empty&);
Assert.notNull(beanDefinition, &BeanDefinition must not be null&);
if (beanDefinition instanceof AbstractBeanDefinition) {
((AbstractBeanDefinition) beanDefinition).validate();
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
&Validation of bean definition failed&, ex);
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
&Cannot register bean definition [& + beanDefinition + &] for bean '& + beanName +
&': There is already [& + oldBeanDefinition + &] bound.&);
if (this.logger.isInfoEnabled()) {
this.logger.info(&Overriding bean definition for bean '& + beanName +
&': replacing [& + oldBeanDefinition + &] with [& + beanDefinition + &]&);
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames =
this.beanDefinitionMap.put(beanName, beanDefinition);
resetBeanDefinition(beanName);
简单说这个方法做了几件事情:
beanDefinitionNames添加BeanName
beanDefinitionMap添加一对映射,Key为BeanName,Value为Bean定义
第38行的方法用于重置一下所有本地缓存了的Bean定义
&bean&中不定义id及id重复场景Spring的处理方式
这两天又想到了两个细节问题,&bean&中不定义id或者id重复,这两种场景Spring是如何处理的。首先看一下不定义id的场景,代码在BeanDefinitionParserDelegate类第398行的这个判断这里:
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
beanName = this.readerContext.generateBeanName(beanDefinition);
当bean的id未定义时,即beanName为空,进入第2行的if判断。containingBean可以看一下,这里是由方法传入的,是一个null值,因此进入第9行的判断,即beanName由第9行的方法生成,看一下生成方式,代码最终要追踪到BeanDefinitionReaderUtils的generateBeanName方法:
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + &$child&;
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + &$created&;
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException(&Unnamed bean definition specifies neither & +
&'class' nor 'parent' nor 'factory-bean' - can't generate bean name&);
String id = generatedBeanN
if (isInnerBean) {
// Inner bean: generate identity hashcode suffix.
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
// Top-level bean: use plain class name.
// Increase counter until the id is unique.
int counter = -1;
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR +
这段代码的逻辑很容易看懂,即:
假如是innerBean(比如Spring AOP产生的Bean),使用【类全路径+#+对象HashCode的16进制】的格式来命名Bean
假如不是innerBean,使用【类全路径+#+数字】的格式来命名Bean,其中数字指的是,同一个Bean出现1次,只要该Bean没有id,就从0开始依次向上累加,比如a.b.c#0、a.b.c#1、a.b.c#2
接着看一下id重复的场景Spring的处理方式,重复id是这样的,Spring使用XmlBeanDefinitionReader读取xml文件,在这个类的doLoadBeanDefinitions的方法中:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
catch (BeanDefinitionStoreException ex) {
第5行的代码将xml解析成Document,这里的解析使用的是JDK自带的DocumentBuilder,DocumentBuilder处理xml文件输入流,发现两个&bean&中定义的id重复即会抛出XNIException异常,最终将导致Spring容器启动失败。
因此,结论就是:Spring不允许两个&bean&定义相同的id。
应该是基本数据类型与Integer类的不同,先将b封包,再println就会出现5.
tony.chenjy
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
新浪微博:
推荐微信号
反馈建议:ImportNew.
广告与商务合作QQ:
– 好的话题、有启发的回复、值得信赖的圈子
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– UI,网页,交互和用户体验
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
& 2018 ImportNew}

我要回帖

更多关于 es文件管理 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信