Hibernate源码分析
程序员文章站
2022-06-11 12:02:48
...
先扯二句蛋:做Java也有很久了,安卓也搞了半年,回想自己当初学习java,j2ee的时候,全靠自己,没有老师,没有同学,书籍就是老师,搜索引擎就是同学,磕磕绊绊下来,终于有所心得,从今天开始准备好好的写一些博客,希望能帮助到那些刚上路,或者在路上却有些迷茫的童鞋````
最近有在研究hibernate这个框架的源码,所以这一阶段会将自己的研究进度和心得用简单的方式贴出来,希望帮到大家的同时,大家也能指出其中的问题,一起讨论,共同进步!(注意:这是源码分析,需要对hibernate有一定了解,起码使用过,所以没有使用过hibernate的童鞋还是先去使用使用在来看吧,不然只能一头雾水`````)
那么就从头开始,且听我细细道来:
hibernate大家都用过,使用下面这个东西大家应该都很熟悉
Configuration cfg = new Configuration().configure();
SessionFactory factory = cfg.buildSessionFactory();
这样就获取到了我们Dao层中所需要的SessionFactory了
那么configure()这个方法中做了什么?上源码
public Configuration configure() throws HibernateException {
configure( "/hibernate.cfg.xml" );
return this;
}
看,这里就是去加载解析/hibernate.cfg.xml去了,显然是用configure(String resource)方法,这个方法做了什么?上源码
public Configuration configure(String resource) throws HibernateException {
LOG.configuringFromResource( resource ); //打了个日志
InputStream stream = getConfigurationInputStream( resource ); //获取这个xml文件的输入流
return doConfigure( stream, resource );//用这个输入流去生成orm映射的对象了!
}
那doConfigure( stream, resource )做了什么?上源码
protected Configuration doConfigure(InputStream stream, String resourceName) throws HibernateException {
try {
ErrorLogger errorLogger = new ErrorLogger( resourceName );
Document document = xmlHelper.createSAXReader( errorLogger, entityResolver )
.read( new InputSource( stream ) );//这里就用这个输入流去生成一个Document 对象了,很显然,hibernate使用了SAX解析
if ( errorLogger.hasErrors() ) {
throw new MappingException( "invalid configuration", errorLogger.getErrors().get( 0 ) );
}
doConfigure( document );//看,再一次调用了重载的 doConfigure( Document document )方法
}
catch (DocumentException e) {
throw new HibernateException( "Could not parse configuration: " + resourceName, e );
}
finally {
try {
stream.close();
}
catch (IOException ioe) {
LOG.unableToCloseInputStreamForResource( resourceName, ioe );
}
}
return this;
}
那么重载的 doConfigure( Document document )方法中做了什么?上源码
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.configuredSessionFactory( name );
LOG.debugf( "Properties: %s", properties );
return this;
}
很显然,这个方法处理了两个节点:session-factory和security
session-factory这个节点很是熟悉啊,自然是用来指明各个映射文件的
security节点再议
parseSessionFactory( sfNode, name );就是去利用这个节点去找到各个映射文件,然后做映射,完成orm
那他做了啥?上源码
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 ) ) {
String className = subelement.attributeValue( "class" );
Attribute regionNode = subelement.attribute( "region" );
final String region = ( regionNode == null ) ? className : regionNode.getValue();
boolean includeLazy = !"non-lazy".equals( subelement.attributeValue( "include" ) );
setCacheConcurrencyStrategy( className, subelement.attributeValue( "usage" ), region, includeLazy );
}
else if ( "collection-cache".equals( subelementName ) ) {
String role = subelement.attributeValue( "collection" );
Attribute regionNode = subelement.attribute( "region" );
final String region = ( regionNode == null ) ? role : regionNode.getValue();
setCollectionCacheConcurrencyStrategy( role, subelement.attributeValue( "usage" ), region );
}
}
}
这里处理了3个节点mapping,class-cache,collection-cache,mapping节点很是熟悉啊,这就是咱为每个domain对象做的映射文件啊,其他两再议
处理mapping节点时调用了parseMappingElement( subelement, name );,那他做了啥?上源码
private void parseMappingElement(Element mappingElement, String name) {
final Attribute resourceAttribute = mappingElement.attribute( "resource" );
final Attribute fileAttribute = mappingElement.attribute( "file" );
final Attribute jarAttribute = mappingElement.attribute( "jar" );
final Attribute packageAttribute = mappingElement.attribute( "package" );
final Attribute classAttribute = mappingElement.attribute( "class" );
if ( resourceAttribute != null ) {
final String resourceName = resourceAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named resource [%s] for mapping", name, resourceName );
addResource( resourceName );
}
else if ( fileAttribute != null ) {
final String fileName = fileAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named file [%s] for mapping", name, fileName );
addFile( fileName );
}
else if ( jarAttribute != null ) {
final String jarFileName = jarAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named jar file [%s] for mapping", name, jarFileName );
addJar( new File( jarFileName ) );
}
else if ( packageAttribute != null ) {
final String packageName = packageAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named package [%s] for mapping", name, packageName );
addPackage( packageName );
}
else if ( classAttribute != null ) {
final String className = classAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named class [%s] for mapping", name, className );
try {
addAnnotatedClass( ReflectHelper.classForName( className ) );
}
catch ( Exception e ) {
throw new MappingException(
"Unable to load class [ " + className + "] declared in Hibernate configuration <mapping/> entry",
e
);
}
}
else {
throw new MappingException( "<mapping> element in configuration specifies no known attributes" );
}
}
很长,本着解决主要矛盾,忽略次要矛盾的马列哲学思想,我们还是来关注我们最熟悉的,用class来配置,也就是上面的
else if ( classAttribute != null ) {
final String className = classAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named class [%s] for mapping", name, className );
try {
addAnnotatedClass( ReflectHelper.classForName( className ) );
}
catch ( Exception e ) {
throw new MappingException(
"Unable to load class [ " + className + "] declared in Hibernate configuration <mapping/> entry",
e
);
}
两个LOG.debugf输出的文字很熟悉吧,就是hibernate启动时常常看见的,控制台打出的日志
那么 addAnnotatedClass( ReflectHelper.classForName( className ) );就是正式进入解析阶段了,且看具体做了什么,
首先不着急,我们看看ReflectHelper.classForName(className ),从名字就可以看出来用了反射技术去从className这个名字反射出一个类出来,暂时把他当做一个黑箱好了,只要知道传入一个对的名字就可以得到一个正确的Class类就ok了。
在得到类后传入看看做了啥:
@SuppressWarnings({ "unchecked" })
public Configuration addAnnotatedClass(Class annotatedClass) {
XClass xClass = reflectionManager.toXClass( annotatedClass );
metadataSourceQueue.add( xClass );
return this;
}
言简意赅,用了一个ReflectionManager对象的toXClass将我们的Class变成了Hibernate自定义的XClass,然后在metadataSourceQueue这个队列中加入了这个XClass,然后一个个映射就出炉了!以后我们就直接使用内存中的队列metadataSourceQueue就ok了
整个解析过程就是这个样子,看Hibernate源码果真是一种享受,很通俗易懂,言简意赅,不想某些公司的项目代码,简直一团乱麻!
ok,不抱怨了,且看几个细节问题,XClass究竟为何物,为何要将Class转化为XClass,MetadataSourceQueue又是何物?
且听下回分解
最近有在研究hibernate这个框架的源码,所以这一阶段会将自己的研究进度和心得用简单的方式贴出来,希望帮到大家的同时,大家也能指出其中的问题,一起讨论,共同进步!(注意:这是源码分析,需要对hibernate有一定了解,起码使用过,所以没有使用过hibernate的童鞋还是先去使用使用在来看吧,不然只能一头雾水`````)
那么就从头开始,且听我细细道来:
hibernate大家都用过,使用下面这个东西大家应该都很熟悉
Configuration cfg = new Configuration().configure();
SessionFactory factory = cfg.buildSessionFactory();
这样就获取到了我们Dao层中所需要的SessionFactory了
那么configure()这个方法中做了什么?上源码
public Configuration configure() throws HibernateException {
configure( "/hibernate.cfg.xml" );
return this;
}
看,这里就是去加载解析/hibernate.cfg.xml去了,显然是用configure(String resource)方法,这个方法做了什么?上源码
public Configuration configure(String resource) throws HibernateException {
LOG.configuringFromResource( resource ); //打了个日志
InputStream stream = getConfigurationInputStream( resource ); //获取这个xml文件的输入流
return doConfigure( stream, resource );//用这个输入流去生成orm映射的对象了!
}
那doConfigure( stream, resource )做了什么?上源码
protected Configuration doConfigure(InputStream stream, String resourceName) throws HibernateException {
try {
ErrorLogger errorLogger = new ErrorLogger( resourceName );
Document document = xmlHelper.createSAXReader( errorLogger, entityResolver )
.read( new InputSource( stream ) );//这里就用这个输入流去生成一个Document 对象了,很显然,hibernate使用了SAX解析
if ( errorLogger.hasErrors() ) {
throw new MappingException( "invalid configuration", errorLogger.getErrors().get( 0 ) );
}
doConfigure( document );//看,再一次调用了重载的 doConfigure( Document document )方法
}
catch (DocumentException e) {
throw new HibernateException( "Could not parse configuration: " + resourceName, e );
}
finally {
try {
stream.close();
}
catch (IOException ioe) {
LOG.unableToCloseInputStreamForResource( resourceName, ioe );
}
}
return this;
}
那么重载的 doConfigure( Document document )方法中做了什么?上源码
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.configuredSessionFactory( name );
LOG.debugf( "Properties: %s", properties );
return this;
}
很显然,这个方法处理了两个节点:session-factory和security
session-factory这个节点很是熟悉啊,自然是用来指明各个映射文件的
security节点再议
parseSessionFactory( sfNode, name );就是去利用这个节点去找到各个映射文件,然后做映射,完成orm
那他做了啥?上源码
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 ) ) {
String className = subelement.attributeValue( "class" );
Attribute regionNode = subelement.attribute( "region" );
final String region = ( regionNode == null ) ? className : regionNode.getValue();
boolean includeLazy = !"non-lazy".equals( subelement.attributeValue( "include" ) );
setCacheConcurrencyStrategy( className, subelement.attributeValue( "usage" ), region, includeLazy );
}
else if ( "collection-cache".equals( subelementName ) ) {
String role = subelement.attributeValue( "collection" );
Attribute regionNode = subelement.attribute( "region" );
final String region = ( regionNode == null ) ? role : regionNode.getValue();
setCollectionCacheConcurrencyStrategy( role, subelement.attributeValue( "usage" ), region );
}
}
}
这里处理了3个节点mapping,class-cache,collection-cache,mapping节点很是熟悉啊,这就是咱为每个domain对象做的映射文件啊,其他两再议
处理mapping节点时调用了parseMappingElement( subelement, name );,那他做了啥?上源码
private void parseMappingElement(Element mappingElement, String name) {
final Attribute resourceAttribute = mappingElement.attribute( "resource" );
final Attribute fileAttribute = mappingElement.attribute( "file" );
final Attribute jarAttribute = mappingElement.attribute( "jar" );
final Attribute packageAttribute = mappingElement.attribute( "package" );
final Attribute classAttribute = mappingElement.attribute( "class" );
if ( resourceAttribute != null ) {
final String resourceName = resourceAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named resource [%s] for mapping", name, resourceName );
addResource( resourceName );
}
else if ( fileAttribute != null ) {
final String fileName = fileAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named file [%s] for mapping", name, fileName );
addFile( fileName );
}
else if ( jarAttribute != null ) {
final String jarFileName = jarAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named jar file [%s] for mapping", name, jarFileName );
addJar( new File( jarFileName ) );
}
else if ( packageAttribute != null ) {
final String packageName = packageAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named package [%s] for mapping", name, packageName );
addPackage( packageName );
}
else if ( classAttribute != null ) {
final String className = classAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named class [%s] for mapping", name, className );
try {
addAnnotatedClass( ReflectHelper.classForName( className ) );
}
catch ( Exception e ) {
throw new MappingException(
"Unable to load class [ " + className + "] declared in Hibernate configuration <mapping/> entry",
e
);
}
}
else {
throw new MappingException( "<mapping> element in configuration specifies no known attributes" );
}
}
很长,本着解决主要矛盾,忽略次要矛盾的马列哲学思想,我们还是来关注我们最熟悉的,用class来配置,也就是上面的
else if ( classAttribute != null ) {
final String className = classAttribute.getValue();
LOG.debugf( "Session-factory config [%s] named class [%s] for mapping", name, className );
try {
addAnnotatedClass( ReflectHelper.classForName( className ) );
}
catch ( Exception e ) {
throw new MappingException(
"Unable to load class [ " + className + "] declared in Hibernate configuration <mapping/> entry",
e
);
}
两个LOG.debugf输出的文字很熟悉吧,就是hibernate启动时常常看见的,控制台打出的日志
那么 addAnnotatedClass( ReflectHelper.classForName( className ) );就是正式进入解析阶段了,且看具体做了什么,
首先不着急,我们看看ReflectHelper.classForName(className ),从名字就可以看出来用了反射技术去从className这个名字反射出一个类出来,暂时把他当做一个黑箱好了,只要知道传入一个对的名字就可以得到一个正确的Class类就ok了。
在得到类后传入看看做了啥:
@SuppressWarnings({ "unchecked" })
public Configuration addAnnotatedClass(Class annotatedClass) {
XClass xClass = reflectionManager.toXClass( annotatedClass );
metadataSourceQueue.add( xClass );
return this;
}
言简意赅,用了一个ReflectionManager对象的toXClass将我们的Class变成了Hibernate自定义的XClass,然后在metadataSourceQueue这个队列中加入了这个XClass,然后一个个映射就出炉了!以后我们就直接使用内存中的队列metadataSourceQueue就ok了
整个解析过程就是这个样子,看Hibernate源码果真是一种享受,很通俗易懂,言简意赅,不想某些公司的项目代码,简直一团乱麻!
ok,不抱怨了,且看几个细节问题,XClass究竟为何物,为何要将Class转化为XClass,MetadataSourceQueue又是何物?
且听下回分解
上一篇: 世道沧桑,红尘哀彻,事事悲 情感