欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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又是何物?
且听下回分解