hibernate源码-配置文件加载过程分析

Hibernate建议,在一个应用系统当中Configuration与SessionFactory为单例,Session为多例.

当我们执行如下代码,hibernate开始加载默认的配置文件

new Configuration().configure()

hibernate会在classath的根路径下,查找名为"hibernate.cfg.xml" 的配置文件,并解析它,过程如图1所示


图1:配置文件加载过程时序图


下面一起分析一下配置文件加载过程

Step 1.Configuration.configure()

该方法定义在 org/hibernate/cfg/Configuration.java文件中

public Configuration configure() throws HibernateException {
		configure( "/hibernate.cfg.xml" );
		return this;
	}

调用重载方法configure( "/hibernate.cfg.xml" ),传入默认的配置文件名称作为实参


Step 2.Configuration.doConfigure

该方法定义在 org/hibernate/cfg/Configuration.java文件中

protected Configuration doConfigure(Document doc) throws HibernateException {
		Element sfNode = doc.getRootElement().element( "session-factory" );
		String name = sfNode.attributeValue( "name" );
		if ( name != null ) {
			properties.setProperty( Environment.SESSION_FACTORY_NAME, name );
		}
		addProperties( sfNode );
		parseSessionFactory( sfNode, name );

		Element secNode = doc.getRootElement().element( "security" );
		if ( secNode != null ) {
			parseSecurity( secNode );
		}

		log.info( "Configured SessionFactory: " + name );
		log.debug( "properties: " + properties );

		return this;
	}

这个方法将加载配置的过程,转交给两个间接层方法来处理,addProperties()与parseSessionFactory


Step 3.Configuration.addProperties

该方法定义在 org/hibernate/cfg/Configuration.java文件中

private void addProperties(Element parent) {
		Iterator itr = parent.elementIterator( "property" );
		while ( itr.hasNext() ) {
			Element node = (Element) itr.next();
			String name = node.attributeValue( "name" );
			String value = node.getText().trim();
			log.debug( name + "=" + value );
			properties.setProperty( name, value );
			if ( !name.startsWith( "hibernate" ) ) {
				properties.setProperty( "hibernate." + name, value );
			}
		}
		Environment.verifyProperties( properties );
	}


这个方法主要将配置文件中的property元素的内容解析,存放至properties成员变量当中,properties是一个Properties实例,

在if判断条件中发现,如果name的值不是以"hibernate"开头,将自动补全


<hibernate-configuration>
	<session-factory >
		<!-- 显示sql -->
		<property name="show_sql">true</property>
		<!-- 数据库连接配置 -->
		<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
		<property name="hibernate.connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orcl</property>
		<property name="hibernate.connection.username">diankun</property>
		<property name="hibernate.connection.password">diankun</property> 
		<!-- 生成sql语句采用的语法 -->
		 <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
		<!-- 自动创建表 -->
		<property name="hibernate.hbm2ddl.auto">update</property>
		<!-- bean与表的映射 -->
		<mapping resource="com/demo/model/Student.hbm.xml"/>
		<mapping resource="com/demo/model/Certificate.hbm.xml"/>
		<mapping resource="com/demo/model/User.hbm.xml"/>
		<mapping resource="com/demo/model/Role.hbm.xml"/>
	</session-factory>
</hibernate-configuration>


Step 4. Configuration.parseSessionFactory

该方法定义在 org/hibernate/cfg/Configuration.java文件中

private void parseSessionFactory(Element sfNode, String name) {
	Iterator elements = sfNode.elementIterator();
	while ( elements.hasNext() ) {
		Element subelement = (Element) elements.next();
		String subelementName = subelement.getName();
		if ( "mapping".equals( subelementName ) ) {
			parseMappingElement( subelement, name );
		}
		else if ( "class-cache".equals( subelementName ) ) {
			......
		}
		else if ( "collection-cache".equals( subelementName ) ) {
			......
		}
		else if ( "listener".equals( subelementName ) ) {
			parseListener( subelement );
		}
		else if ( "event".equals( subelementName ) ) {
			parseEvent( subelement );
		}
	}
}

形参sfNode指向session-factory元素节点
形参name的为session-factory的name属性值,等于null

由于我们在配置文件中,添加了如下配置:

<mapping resource="com/demo/model/Student.hbm.xml"/>
		<mapping resource="com/demo/model/Certificate.hbm.xml"/>
		<mapping resource="com/demo/model/User.hbm.xml"/>
		<mapping resource="com/demo/model/Role.hbm.xml"/>

这时满足条件为""mapping".equals( subelementName )" 的分支


Step 5.Configuration.parseMappingElement

该方法定义在 org/hibernate/cfg/Configuration.java文件中

private void parseMappingElement(Element mappingElement, String name) {
		final Attribute resourceAttribute = mappingElement.attribute( "resource" );
		......

		if ( resourceAttribute != null ) {
			final String resourceName = resourceAttribute.getValue();
			log.debug( "session-factory config [{}] named resource [{}] for mapping", name, resourceName );
			addResource( resourceName );
		}
		else if ( fileAttribute != null ) {
			......
		}
		else if ( jarAttribute != null ) {
			......
		}
		else if ( packageAttribute != null ) {
			......
		}
		else if ( classAttribute != null ) {
			......
		}
		else {
			throw new MappingException( "<mapping> element in configuration specifies no known attributes" );
		}
	}

我们在hibernate.cfg.xml中配置 <mapping resource="com/demo/model/User.hbm.xml"/> ,

满足条件 resourceAttribute != null ,调用addResource( resourceName ) 方法


Step 6.Configuration.addResource

该方法定义在 org/hibernate/cfg/Configuration.java文件中

public Configuration addResource(String resourceName) throws MappingException {
		log.info( "Reading mappings from resource : " + resourceName );
		ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
		InputStream resourceInputStream = null;
		if ( contextClassLoader != null ) {
			resourceInputStream = contextClassLoader.getResourceAsStream( resourceName );
		}
		if ( resourceInputStream == null ) {
			resourceInputStream = Environment.class.getClassLoader().getResourceAsStream( resourceName );
		}
		if ( resourceInputStream == null ) {
			throw new MappingNotFoundException( "resource", resourceName );
		}
		add( resourceInputStream, "resource", resourceName );
		return this;
	}

这个方法功能比较简单,通过类加载器来开启输入流,

并将输入流作为实参,转发给 add( resourceInputStream, "resource", resourceName ) 来处理


Step 7.Configuration.add

该方法定义在 org/hibernate/cfg/Configuration.java文件中

public void add(XmlDocument metadataXml) {
		if ( inSecondPass || !isOrmXml( metadataXml ) ) {
			metadataSourceQueue.add( metadataXml );
		}
		else {
			final MetadataProvider metadataProvider = ( (MetadataProviderInjector) reflectionManager ).getMetadataProvider();
			JPAMetadataProvider jpaMetadataProvider = ( JPAMetadataProvider ) metadataProvider;
			List<String> classNames = jpaMetadataProvider.getXMLContext().addDocument( metadataXml.getDocumentTree() );
			for ( String className : classNames ) {
				try {
					metadataSourceQueue.add( reflectionManager.classForName( className, this.getClass() ) );
				}
				catch ( ClassNotFoundException e ) {
					throw new AnnotationException( "Unable to load class defined in XML: " + className, e );
				}
			}
		}
	}

add(InputSource inputSource, String originType, String originName) 

add(InputSource inputSource, Origin origin)

以上两上重载add()方法,调用过程跳过

在add(XmlDocument metadataXml)方法中,我们可以看到它作了一个委托处理,

交给成员变量metadataSourceQueue处理,metadataSourceQueue是一个MetadataSourceQueue实例,是Configuration的内部类


Step 8.MetadataSourceQueue.add

该方法定义在 org/hibernate/cfg/Configuration.java文件中

public void add(XmlDocument metadataXml) {
			final Document document = metadataXml.getDocumentTree();
			final Element hmNode = document.getRootElement();
			Attribute packNode = hmNode.attribute( "package" );
			String defaultPackage = packNode != null ? packNode.getValue() : "";
			Set<String> entityNames = new HashSet<String>();
			findClassNames( defaultPackage, hmNode, entityNames );
			for ( String entity : entityNames ) {
				hbmMetadataByEntityNameXRef.put( entity, metadataXml );
			}
			this.hbmMetadataToEntityNamesMap.put( metadataXml, entityNames );
		}

metadataXml指向一个文档根节点

findClassNames( defaultPackage, hmNode, entityNames ) 方法作用:

将实体映射文件中的class元素的name属性,存储在局部变量entityNames之中

再将entityNames集合元素的值作为key,文档根结点作为value,存储在成员变量hbmMetadataByEntityNameXRef中,

hbmMetadataByEntityNameXRef是 Map<String, XmlDocument> 实例


将文档根结点作为key,将集合entityNames作为value,存储在成员变量hbmMetadataToEntityNamesMap中,

hbmMetadataToEntityNamesMap是 LinkedHashMap<XmlDocument, Set<String>> 的实例,


protected class MetadataSourceQueue implements Serializable {
	private LinkedHashMap<XmlDocument, Set<String>> hbmMetadataToEntityNamesMap
			= new LinkedHashMap<XmlDocument, Set<String>>();
	private Map<String, XmlDocument> hbmMetadataByEntityNameXRef = new HashMap<String, XmlDocument>();

	//XClass are not serializable by default
	private transient List<XClass> annotatedClasses = new ArrayList<XClass>();
	//only used during the secondPhaseCompile pass, hence does not need to be serialized
	private transient Map<String, XClass> annotatedClassesByEntityNameMap = new HashMap<String, XClass>();

	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
		......
	}

	private void writeObject(java.io.ObjectOutputStream out) throws IOException {
		......
	}

	public void add(XmlDocument metadataXml) {
		final Document document = metadataXml.getDocumentTree();
		final Element hmNode = document.getRootElement();
		Attribute packNode = hmNode.attribute( "package" );
		String defaultPackage = packNode != null ? packNode.getValue() : "";
		Set<String> entityNames = new HashSet<String>();
		findClassNames( defaultPackage, hmNode, entityNames );
		for ( String entity : entityNames ) {
			hbmMetadataByEntityNameXRef.put( entity, metadataXml );
		}
		this.hbmMetadataToEntityNamesMap.put( metadataXml, entityNames );
	}
}



hbmMetadataByEntityNameXRef与hbmMetadataToEntityNamesMap的存储结构,如图2所示:


图2:两个Map的存储结构



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。