Hibernate原始碼學習四 _服務註冊

bsr1983發表於2013-09-12

接學習三中的內容,首先看一下如何根據配置檔案建立一個SessionFactory,通過跟蹤相關程式碼來了解其中的來龍去脈。

      主方法中建立SessionFactory相關的程式碼為:

         

ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
            .applySettings(configuration.getProperties())
            .buildServiceRegistry();
      //根據所註冊的服務建立sessionFactory
      SessionFactory sessionFactory = configuration
           .buildSessionFactory(serviceRegistry);

 

其中,首先要獲取Configuration中的屬性。那麼這個屬性是如何獲取的呢?

通過檢視Configuration的原始碼,我們知道在呼叫protected Configuration doConfigure(Document doc) throws HibernateException方法的時候,在其內部呼叫了addProperties( sfNode );程式碼如下:

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.debugf( "%s=%s", name, value );
         properties.setProperty( name, value );
         if ( !name.startsWith( "hibernate" ) ) {
            properties.setProperty( "hibernate." + name, value );
         }
      }
      Environment.verifyProperties( properties );
  }

 

  這裡通過獲取根節點下的所有property元素來獲取對應的屬性。通過獲取property元素的name屬性和對應的值來設定屬性,如果該屬性不是以hibernate開頭的,那麼就預設給屬性新增一個hibernate.的字首。最後一步是Environment.verifyProperties( properties );

具體的程式碼如下:

public static void verifyProperties(Map<?,?> configurationValues) {
      final Map propertiesToAdd = new HashMap();
      for ( Map.Entry entry : configurationValues.entrySet() ) {
         final Object replacementKey = OBSOLETE_PROPERTIES.get( entry.getKey() );
         if ( replacementKey != null ) {
            LOG.unsupportedProperty( entry.getKey(), replacementKey );
         }
         final Object renamedKey = RENAMED_PROPERTIES.get( entry.getKey() );
         if ( renamedKey != null ) {
            LOG.renamedProperty( entry.getKey(), renamedKey );
            propertiesToAdd.put( renamedKey, entry.getValue() );
         }
      }
      configurationValues.putAll( propertiesToAdd );
  }

 

此處的校驗主要是去除一些已廢棄的屬性和需要進行重新命名的屬性,應該是為了相容性考慮,將之前用過的一些已廢棄的屬性名稱轉換為當前版本中正確的屬性名稱,除錯發現OBSOLETE_PROPERTIESRENAMED_PROPERTIES為無元素的Map,其宣告的程式碼為:

private static final Map OBSOLETE_PROPERTIES = new HashMap();
private static final Map RENAMED_PROPERTIES = new HashMap();

 

宣告為靜態私有的不可變的Map,且併為給該Map新增元素可以證明之前的推測此處僅是為了相容性考慮的推斷是正確的。

  再看

public final class Environment implements AvailableSettings

 ,這個類是實現了AvailableSettings介面的類,而AvailableSettings介面中宣告瞭大量的屬性名,看到很多都是我們在hibernate.cfg.xml中配置過,或者是在hibernate.properties檔案中使用的key名稱。包括hibernate.connection.driver_classhibernate.connection.urlhibernate.connection.username等等。

  通過Debug來檢視configuration.getProperties()的值,具體值見下圖



 

可以看到這個Map中包含了95個元素,其中的與hibernate配置相關的屬性的key並未以hibernate開頭,而是和我們在hibernate.cfg.xml中配置的key名稱是一致的,我們可以修改一下我們的配置檔案,改成標準的屬性名稱,在除錯到此處,看看值有沒有變化。

這次我將hibernate.cfg.xml修改為如下程式碼:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernatecode</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <mapping resource="com/ibsrapp/hibernatecode/domain/UserInfo.hbm.xml" />
    </session-factory>
</hibernate-configuration>

 

此時再通過Debug來檢視configuration.getProperties()的值,具體值見下圖:

  

 

此時的key和我們配置檔案中的一致,都加上了hibernate的字首。也就是可以得出一個結論,在我們的配置檔案中可以省略hibernate.這個字首,hibernate在校驗其標準屬性的時候會自動為增加字首,以便通過校驗,如果我們增加一些未定義的屬性名稱,則會將該屬性忽略。如果進行Debug可以看到,在當前的HashMap並沒有包含自定義的那些屬性。

  但具體定義這些規定配置屬性的屬性名稱的類是

org.hibernate.cfg.AvailableSettings,在該類中定義了配置項中的屬性名稱,可以看到,這裡的屬性名稱都是包含“hibernate.”字首的。具體的使用就是通過定義的靜態字串,從屬性的Map中獲取對應的值。例如在org.hibernate.cfg.SettingsFactory類中的方法buildSettings,程式碼如下:

public Settings buildSettings(Properties props, ServiceRegistry serviceRegistry) {
      final boolean debugEnabled =  LOG.isDebugEnabled();
      final JdbcServices jdbcServices = serviceRegistry.getService( JdbcServices.class );
      Settings settings = new Settings();
 
      //SessionFactory name:
 
      String sessionFactoryName = props.getProperty( AvailableSettings.SESSION_FACTORY_NAME );
      settings.setSessionFactoryName( sessionFactoryName );
      settings.setSessionFactoryNameAlsoJndiName(
            ConfigurationHelper.getBoolean( AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI, props, true )
      );

 

以下程式碼略,中的String sessionFactoryName = props.getProperty( AvailableSettings.SESSION_FACTORY_NAME );

這裡就使用了定義的靜態變數AvailableSettings.SESSION_FACTORY_NAME作為key,其實際值為“hibernate.session_factory_name”。

  從上述截圖中可以看到,這個屬性map中還包含了很多系統屬性。

  具體初始化的部分是在Environment的一個靜態語句塊中進行的初始化,除了獲取系統屬性,同時還會設定一些其他的全域性設定程式碼如下:

   

static {
		Version.logVersion();
		//初始化事務級別靜態常量與對應的字串值的Map
		Map<Integer,String> temp = new HashMap<Integer,String>();
		temp.put( Connection.TRANSACTION_NONE, "NONE" );
		temp.put( Connection.TRANSACTION_READ_UNCOMMITTED, "READ_UNCOMMITTED" );
		temp.put( Connection.TRANSACTION_READ_COMMITTED, "READ_COMMITTED" );
		temp.put( Connection.TRANSACTION_REPEATABLE_READ, "REPEATABLE_READ" );
		temp.put( Connection.TRANSACTION_SERIALIZABLE, "SERIALIZABLE" );
		ISOLATION_LEVELS = Collections.unmodifiableMap( temp );
		GLOBAL_PROPERTIES = new Properties();
		//Set USE_REFLECTION_OPTIMIZER to false to fix HHH-227
		GLOBAL_PROPERTIES.setProperty( USE_REFLECTION_OPTIMIZER, Boolean.FALSE.toString() );
//讀取hibernate.properties屬性檔案,獲取全域性屬性
		try {
			InputStream stream = ConfigHelper.getResourceAsStream( "/hibernate.properties" );
			try {
				GLOBAL_PROPERTIES.load(stream);
				LOG.propertiesLoaded( ConfigurationHelper.maskOut( GLOBAL_PROPERTIES, PASS ) );
			}
			catch (Exception e) {
				LOG.unableToLoadProperties();
			}
			finally {
				try{
					stream.close();
				}
				catch (IOException ioe){
					LOG.unableToCloseStreamError( ioe );
				}
			}
		}
		catch (HibernateException he) {
			LOG.propertiesNotFound();
		}
		//獲取當前系統屬性,也放入到全域性屬性中
		try {
		    Properties systemProperties = System.getProperties();
		    // Must be thread-safe in case an application changes System properties during Hibernate initialization.
		    // See HHH-8383.
		    synchronized (systemProperties) {
		    	GLOBAL_PROPERTIES.putAll(systemProperties);
		    }
		} catch (SecurityException se) {
		    LOG.unableToCopySystemProperties();
		}
//校驗全域性屬性,具體的程式碼參見上文
		verifyProperties(GLOBAL_PROPERTIES);

		ENABLE_BINARY_STREAMS = ConfigurationHelper.getBoolean(USE_STREAMS_FOR_BINARY, GLOBAL_PROPERTIES);
		if ( ENABLE_BINARY_STREAMS ) {
			LOG.usingStreams();
		}

		ENABLE_REFLECTION_OPTIMIZER = ConfigurationHelper.getBoolean(USE_REFLECTION_OPTIMIZER, GLOBAL_PROPERTIES);
		if ( ENABLE_REFLECTION_OPTIMIZER ) {
			LOG.usingReflectionOptimizer();
		}

		BYTECODE_PROVIDER_INSTANCE = buildBytecodeProvider( GLOBAL_PROPERTIES );
//校驗當前的jvm是否存在timestamp的bug
		long x = 123456789;
		JVM_HAS_TIMESTAMP_BUG = new Timestamp(x).getTime() != x;
		if ( JVM_HAS_TIMESTAMP_BUG ) {
			LOG.usingTimestampWorkaround();
		}
	}

 

       

  註冊服務服務部分程式碼分析:

  主類中程式碼:

ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
            . applySettings (configuration.getProperties())
           .buildServiceRegistry();

 

      下面看看ServiceRegistryBuilder類中相關的程式碼都做了哪些操作:

  首先是new ServiceRegistryBuilder(),用來呼叫無參建構函式建立一個ServiceRegistryBuilder物件。看一下對應的方法內容:

  

 public ServiceRegistryBuilder() {
      this( new BootstrapServiceRegistryImpl() );
  }

 

其實是呼叫了另一個構造方法,從Environment獲取當前的全域性設定屬性,並將上一個建構函式中傳遞的BootstrapServiceRegistry實現類物件設定為當前的引導服務註冊變數:

  

 public ServiceRegistryBuilder(BootstrapServiceRegistry bootstrapServiceRegistry) {
      this.settings = Environment.getProperties();
      this.bootstrapServiceRegistry = bootstrapServiceRegistry;
  }

 

BootstrapServiceRegistryImpl的定義如下

public class BootstrapServiceRegistryImpl
     implements ServiceRegistryImplementor, BootstrapServiceRegistry, ServiceBinding.ServiceLifecycleOwner

 

可以看到其實現了3個不同的介面,但都和服務註冊和繫結有關,而其建構函式在初始化的時候會初始化其所擁有的兩個與類載入和攔截器有關的服務,具體參見其原始碼。

applySettings方法的程式碼如下,其實就是將configuration中配置的資料新增到當前的settingMap中。

public ServiceRegistryBuilder applySettings(Map settings) {
      this.settings.putAll( settings );
      return this;
  }

 

接下來看一下buildServiceRegistry

public ServiceRegistry buildServiceRegistry() {
       Map<?,?> settingsCopy = new HashMap();
       settingsCopy.putAll( settings );
       Environment.verifyProperties( settingsCopy );
       //處理以${系統屬性名}為value的屬性,即將其value設定為具體的屬性值,如//果指定的屬性不存在,置為””
       ConfigurationHelper.resolvePlaceHolders( settingsCopy );
//貌似在處理攔截器服務的順序,感覺和struts的攔截器類似,暫未深入研究
       for ( Integrator integrator : bootstrapServiceRegistry.getService( IntegratorService.class ).getIntegrators() ) {
           if ( ServiceContributingIntegrator.class.isInstance( integrator ) ) {
              ServiceContributingIntegrator.class.cast( integrator ).prepareServices( this );
           }
       }
//呼叫本地另一個構造方法建立一個例項物件返回
       return new StandardServiceRegistryImpl( bootstrapServiceRegistry, initiators, providedServices, settingsCopy );
  }

 

呼叫的構造方法如下,主要還是進行了服務的繫結:

public StandardServiceRegistryImpl(
         BootstrapServiceRegistry bootstrapServiceRegistry,
         List<BasicServiceInitiator> serviceInitiators,
         List<ProvidedService> providedServices,
         Map<?, ?> configurationValues) {
      super( bootstrapServiceRegistry );
 
      this.configurationValues = configurationValues;
 
      // process initiators
      for ( ServiceInitiator initiator : serviceInitiators ) {
         createServiceBinding( initiator );
      }
 
      // then, explicitly provided service instances
      for ( ProvidedService providedService : providedServices ) {
         createServiceBinding( providedService );
      }
  }

 

相關文章