1. 程式人生 > >Spring源代碼分析(1)---LocalSessionFactoryBean(工廠的工廠)

Spring源代碼分析(1)---LocalSessionFactoryBean(工廠的工廠)

self action interface 開始 environ mac hbm upd put

LocalSessionFacotoryBean其實就是適配了Configuration對象,或者說是一個工廠的工廠,他是Configuration的工廠,生成了Configuration以後,再利用他生成了SessioFactory;

  

public class LocalSessionFactoryBean implements FactoryBean, InitializingBean, DisposableBean {
    
    //線程變量的DataSource
    private static final ThreadLocal configTimeDataSourceHolder = new
ThreadLocal(); //線程變量TransactionManager private static final ThreadLocal configTimeTransactionManagerHolder = new ThreadLocal(); //線程變量LoBHandler private static final ThreadLocal configTimeLobHandlerHolder = new ThreadLocal(); //取得當前的線程變量,每個線程對應不同的變量; public static DataSource getConfigTimeDataSource() {
return (DataSource) configTimeDataSourceHolder.get(); } public static TransactionManager getConfigTimeTransactionManager() { return (TransactionManager) configTimeTransactionManagerHolder.get(); } public static LobHandler getConfigTimeLobHandler() { return (LobHandler) configTimeLobHandlerHolder.get
(); } protected final Log logger = LogFactory.getLog(getClass()); //一下是一些Hibernate的SessionFactory的配置屬性,以前我們可以在hibernate.cfg.xml中配置; private Class configurationClass = Configuration.class; private Resource[] configLocations; //如下是對象和數據庫表對應的hbm.xml文件 private Resource[] mappingLocations; private Resource[] cacheableMappingLocations; private Resource[] mappingJarLocations; private Resource[] mappingDirectoryLocations; //Hibernate的屬性設置 private Properties hibernateProperties; //數據源 private DataSource dataSource; private boolean useTransactionAwareDataSource = false; private boolean exposeTransactionAwareSessionFactory = true; private TransactionManager jtaTransactionManager; private LobHandler lobHandler; private Interceptor entityInterceptor; private NamingStrategy namingStrategy; private Properties entityCacheStrategies; private Properties collectionCacheStrategies; private TypeDefinitionBean[] typeDefinitions; private FilterDefinition[] filterDefinitions; private Map eventListeners; private boolean schemaUpdate = false; private Configuration configuration; //該對象是FactoryBean,會用getObject返回正真的對象; private SessionFactory sessionFactory; public void setConfigurationClass(Class configurationClass) { if (configurationClass == null || !Configuration.class.isAssignableFrom(configurationClass)) { throw new IllegalArgumentException( "configurationClass must be assignable to [org.hibernate.cfg.Configuration]"); } this.configurationClass = configurationClass; } public void setMappingJarLocations(Resource[] mappingJarLocations) { this.mappingJarLocations = mappingJarLocations; } public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) { this.mappingDirectoryLocations = mappingDirectoryLocations; } public void setHibernateProperties(Properties hibernateProperties) { this.hibernateProperties = hibernateProperties; } public Properties getHibernateProperties() { if (this.hibernateProperties == null) { this.hibernateProperties = new Properties(); } return this.hibernateProperties; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) { this.useTransactionAwareDataSource = useTransactionAwareDataSource; } public void setExposeTransactionAwareSessionFactory(boolean exposeTransactionAwareSessionFactory) { this.exposeTransactionAwareSessionFactory = exposeTransactionAwareSessionFactory; } public void setJtaTransactionManager(TransactionManager jtaTransactionManager) { this.jtaTransactionManager = jtaTransactionManager; } public void setLobHandler(LobHandler lobHandler) { this.lobHandler = lobHandler; } public void setEntityInterceptor(Interceptor entityInterceptor) { this.entityInterceptor = entityInterceptor; } public void setNamingStrategy(NamingStrategy namingStrategy) { this.namingStrategy = namingStrategy; } = public void setEntityCacheStrategies(Properties entityCacheStrategies) { this.entityCacheStrategies = entityCacheStrategies; } = public void setCollectionCacheStrategies(Properties collectionCacheStrategies) { this.collectionCacheStrategies = collectionCacheStrategies; } public void setTypeDefinitions(TypeDefinitionBean[] typeDefinitions) { this.typeDefinitions = typeDefinitions; } public void setFilterDefinitions(FilterDefinition[] filterDefinitions) { this.filterDefinitions = filterDefinitions; } = public void setEventListeners(Map eventListeners) { this.eventListeners = eventListeners; } /** * Set whether to execute a schema update after SessionFactory initialization. * <p>For details on how to make schema update scripts work, see the Hibernate * documentation, as this class leverages the same schema update script support * in org.hibernate.cfg.Configuration as Hibernate‘s own SchemaUpdate tool. * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript * @see org.hibernate.tool.hbm2ddl.SchemaUpdate */ public void setSchemaUpdate(boolean schemaUpdate) { this.schemaUpdate = schemaUpdate; } /** * Initialize the SessionFactory for the given or the default location. * @throws IllegalArgumentException in case of illegal property values * @throws HibernateException in case of Hibernate initialization errors */ //在這裏開始生成一個真正的SessionFactory對象;然後利用getObjecy生成出來; public void afterPropertiesSet() throws IllegalArgumentException, HibernateException, IOException { // Create Configuration instance. //根據反射成成一個Configuration類;準備用他來生成SessioNFactory; //把接受到的configuration的屬性設置給Configuration Configuration config = newConfiguration(); if (this.dataSource != null) { // Make given DataSource available for SessionFactory configuration. configTimeDataSourceHolder.set(this.dataSource); } if (this.jtaTransactionManager != null) { // Make Spring-provided JTA TransactionManager available. configTimeTransactionManagerHolder.set(this.jtaTransactionManager); } if (this.lobHandler != null) { // Make given LobHandler available for SessionFactory configuration. // Do early because because mapping resource might refer to custom types. configTimeLobHandlerHolder.set(this.lobHandler); } try { if (this.jtaTransactionManager != null) { // Set Spring-provided JTA TransactionManager as Hibernate property. config.setProperty( Environment.TRANSACTION_MANAGER_STRATEGY, LocalTransactionManagerLookup.class.getName()); config.setProperty( Environment.TRANSACTION_STRATEGY, JTATransactionFactory.class.getName()); } else { // Set connection release mode "on_close" as default. // This was the case for Hibernate 3.0; Hibernate 3.1 changed // it to "auto" (i.e. "after_statement" or "after_transaction"). // However, for Spring‘s resource management (in particular for // HibernateTransactionManager), "on_close" is the better default. config.setProperty(Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.ON_CLOSE.toString()); } if (this.entityInterceptor != null) { // Set given entity interceptor at SessionFactory level. config.setInterceptor(this.entityInterceptor); } if (this.namingStrategy != null) { // Pass given naming strategy to Hibernate Configuration. config.setNamingStrategy(this.namingStrategy); } if (this.typeDefinitions != null) { // Register specified Hibernate type definitions. Mappings mappings = config.createMappings(); for (int i = 0; i < this.typeDefinitions.length; i++) { TypeDefinitionBean typeDef = this.typeDefinitions[i]; mappings.addTypeDef(typeDef.getTypeName(), typeDef.getTypeClass(), typeDef.getParameters()); } } if (this.filterDefinitions != null) { // Register specified Hibernate FilterDefinitions. for (int i = 0; i < this.filterDefinitions.length; i++) { config.addFilterDefinition(this.filterDefinitions[i]); } } if (this.configLocations != null) { for (int i = 0; i < this.configLocations.length; i++) { // Load Hibernate configuration from given location. config.configure(this.configLocations[i].getURL()); } } if (this.hibernateProperties != null) { // Add given Hibernate properties to Configuration. config.addProperties(this.hibernateProperties); } if (this.dataSource != null) { boolean actuallyTransactionAware = (this.useTransactionAwareDataSource || this.dataSource instanceof TransactionAwareDataSourceProxy); // Set Spring-provided DataSource as Hibernate ConnectionProvider. config.setProperty(Environment.CONNECTION_PROVIDER, actuallyTransactionAware ? TransactionAwareDataSourceConnectionProvider.class.getName() : LocalDataSourceConnectionProvider.class.getName()); } if (this.mappingLocations != null) { // Register given Hibernate mapping definitions, contained in resource files. for (int i = 0; i < this.mappingLocations.length; i++) { config.addInputStream(this.mappingLocations[i].getInputStream()); } } if (this.cacheableMappingLocations != null) { // Register given cacheable Hibernate mapping definitions, read from the file system. for (int i = 0; i < this.cacheableMappingLocations.length; i++) { config.addCacheableFile(this.cacheableMappingLocations[i].getFile()); } } if (this.mappingJarLocations != null) { // Register given Hibernate mapping definitions, contained in jar files. for (int i = 0; i < this.mappingJarLocations.length; i++) { Resource resource = this.mappingJarLocations[i]; config.addJar(resource.getFile()); } } if (this.mappingDirectoryLocations != null) { // Register all Hibernate mapping definitions in the given directories. for (int i = 0; i < this.mappingDirectoryLocations.length; i++) { File file = this.mappingDirectoryLocations[i].getFile(); if (!file.isDirectory()) { throw new IllegalArgumentException( "Mapping directory location [" + this.mappingDirectoryLocations[i] + "] does not denote a directory"); } config.addDirectory(file); } } if (this.entityCacheStrategies != null) { // Register cache strategies for mapped entities. for (Enumeration classNames = this.entityCacheStrategies.propertyNames(); classNames.hasMoreElements();) { String className = (String) classNames.nextElement(); config.setCacheConcurrencyStrategy(className, this.entityCacheStrategies.getProperty(className)); } } if (this.collectionCacheStrategies != null) { // Register cache strategies for mapped collections. for (Enumeration collRoles = this.collectionCacheStrategies.propertyNames(); collRoles.hasMoreElements();) { String collRole = (String) collRoles.nextElement(); config.setCollectionCacheConcurrencyStrategy( collRole, this.collectionCacheStrategies.getProperty(collRole)); } } //增加時間處理器; if (this.eventListeners != null) { // Register specified Hibernate event listeners. for (Iterator it = this.eventListeners.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); String listenerType = (String) entry.getKey(); Object listenerObject = entry.getValue(); config.setListener(listenerType, listenerObject); } } //註意,這裏是一個默認實現,其實我們可以叫鉤子,他能夠在Configuration生成之際是對其進行攔截處理; postProcessConfiguration(config); // Build SessionFactory instance. logger.info("Building new Hibernate SessionFactory"); this.configuration = config; SessionFactory sf = newSessionFactory(config); //是否生成代理sessionFactoty對象,這個代理對象不同的是,他不能調用close方法,無用; if (this.exposeTransactionAwareSessionFactory) { this.sessionFactory = getTransactionAwareSessionFactoryProxy(sf); } else { this.sessionFactory = sf; } // Execute schema update if requested. if (this.schemaUpdate) { updateDatabaseSchema(); } } finally { if (this.dataSource != null) { // Reset DataSource holder. configTimeDataSourceHolder.set(null); } if (this.jtaTransactionManager != null) { // Reset TransactionManager holder. configTimeTransactionManagerHolder.set(null); } if (this.lobHandler != null) { // Reset LobHandler holder. configTimeLobHandlerHolder.set(null); } } } /** * Subclasses can override this method to perform custom initialization * of the Configuration instance used for SessionFactory creation. * The properties of this LocalSessionFactoryBean will be applied to * the Configuration object that gets returned here. * <p>The default implementation creates a new Configuration instance. * A custom implementation could prepare the instance in a specific way, * or use a custom Configuration subclass. * @return the Configuration instance * @throws HibernateException in case of Hibernate initialization errors * @see org.hibernate.cfg.Configuration#Configuration() */ //以下是Configuration生成SessionFactory的典型做法; protected Configuration newConfiguration() throws HibernateException { return (Configuration) BeanUtils.instantiateClass(this.configurationClass); } /** * To be implemented by subclasses that want to to perform custom * post-processing of the Configuration object after this FactoryBean * performed its default initialization. * @param config the current Configuration object * @throws HibernateException in case of Hibernate initialization errors */ protected void postProcessConfiguration(Configuration config) throws HibernateException { } /** * Subclasses can override this method to perform custom initialization * of the SessionFactory instance, creating it via the given Configuration * object that got prepared by this LocalSessionFactoryBean. * <p>The default implementation invokes Configuration‘s buildSessionFactory. * A custom implementation could prepare the instance in a specific way, * or use a custom SessionFactoryImpl subclass. * @param config Configuration prepared by this LocalSessionFactoryBean * @return the SessionFactory instance * @throws HibernateException in case of Hibernate initialization errors * @see org.hibernate.cfg.Configuration#buildSessionFactory */ protected SessionFactory newSessionFactory(Configuration config) throws HibernateException { return config.buildSessionFactory(); } /** * Wrap the given SessionFactory with a proxy that delegates every method call * to it but delegates <code>getCurrentSession</code> calls to SessionFactoryUtils, * for participating in Spring-managed transactions. * @param target the original SessionFactory to wrap * @return the wrapped SessionFactory * @see org.hibernate.SessionFactory#getCurrentSession() * @see SessionFactoryUtils#doGetSession(org.hibernate.SessionFactory, boolean) */ protected SessionFactory getTransactionAwareSessionFactoryProxy(SessionFactory target) { Class sfInterface = SessionFactory.class; if (target instanceof SessionFactoryImplementor) { sfInterface = SessionFactoryImplementor.class; } return (SessionFactory) Proxy.newProxyInstance(sfInterface.getClassLoader(), new Class[] {sfInterface}, new TransactionAwareInvocationHandler(target)); } = public void dropDatabaseSchema() throws DataAccessException { logger.info("Dropping database schema for Hibernate SessionFactory"); HibernateTemplate hibernateTemplate = new HibernateTemplate(this.sessionFactory); hibernateTemplate.execute( new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException, SQLException { Connection con = session.connection(); Dialect dialect = Dialect.getDialect(configuration.getProperties()); String[] sql = configuration.generateDropSchemaScript(dialect); executeSchemaScript(con, sql); return null; } } ); } /** * Execute schema creation script, determined by the Configuration object * used for creating the SessionFactory. A replacement for Hibernate‘s * SchemaExport class, to be invoked on application setup. * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed * SessionFactory to be able to invoke this method, e.g. via * <code>LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");</code>. * <p>Uses the SessionFactory that this bean generates for accessing a JDBC * connection to perform the script. * @throws DataAccessException in case of script execution errors * @see org.hibernate.cfg.Configuration#generateSchemaCreationScript * @see org.hibernate.tool.hbm2ddl.SchemaExport#create */ public void createDatabaseSchema() throws DataAccessException { logger.info("Creating database schema for Hibernate SessionFactory"); HibernateTemplate hibernateTemplate = new HibernateTemplate(this.sessionFactory); hibernateTemplate.execute( new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException, SQLException { Connection con = session.connection(); Dialect dialect = Dialect.getDialect(configuration.getProperties()); String[] sql = configuration.generateSchemaCreationScript(dialect); executeSchemaScript(con, sql); return null; } } ); } /** * Execute schema update script, determined by the Configuration object * used for creating the SessionFactory. A replacement for Hibernate‘s * SchemaUpdate class, for automatically executing schema update scripts * on application startup. Can also be invoked manually. * <p>Fetch the LocalSessionFactoryBean itself rather than the exposed * SessionFactory to be able to invoke this method, e.g. via * <code>LocalSessionFactoryBean lsfb = (LocalSessionFactoryBean) ctx.getBean("&mySessionFactory");</code>. * <p>Uses the SessionFactory that this bean generates for accessing a JDBC * connection to perform the script. * @throws DataAccessException in case of script execution errors * @see #setSchemaUpdate * @see org.hibernate.cfg.Configuration#generateSchemaUpdateScript * @see org.hibernate.tool.hbm2ddl.SchemaUpdate */ public void updateDatabaseSchema() throws DataAccessException { logger.info("Updating database schema for Hibernate SessionFactory"); HibernateTemplate hibernateTemplate = new HibernateTemplate(this.sessionFactory); hibernateTemplate.setFlushMode(HibernateTemplate.FLUSH_NEVER); hibernateTemplate.execute( new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException, SQLException { Connection con = session.connection(); Dialect dialect = Dialect.getDialect(configuration.getProperties()); DatabaseMetadata metadata = new DatabaseMetadata(con, dialect); String[] sql = configuration.generateSchemaUpdateScript(dialect, metadata); executeSchemaScript(con, sql); return null; } } ); } protected void executeSchemaScript(Connection con, String[] sql) throws SQLException { if (sql != null && sql.length > 0) { boolean oldAutoCommit = con.getAutoCommit(); if (!oldAutoCommit) { con.setAutoCommit(true); } try { Statement stmt = con.createStatement(); try { for (int i = 0; i < sql.length; i++) { executeSchemaStatement(stmt, sql[i]); } } finally { JdbcUtils.closeStatement(stmt); } } finally { if (!oldAutoCommit) { con.setAutoCommit(false); } } } } protected void executeSchemaStatement(Statement stmt, String sql) throws SQLException { if (logger.isDebugEnabled()) { logger.debug("Executing schema statement: " + sql); } try { stmt.executeUpdate(sql); } catch (SQLException ex) { if (logger.isWarnEnabled()) { logger.warn("Unsuccessful schema statement: " + sql, ex); } } } /** * Return the Configuration object used to build the SessionFactory. * Allows access to configuration metadata stored there (rarely needed). */ public Configuration getConfiguration() { return this.configuration; } /** * Return the singleton SessionFactory. */ public Object getObject() { return this.sessionFactory; } public Class getObjectType() { return (this.sessionFactory != null) ? this.sessionFactory.getClass() : SessionFactory.class; } public boolean isSingleton() { return true; } /** * Close the SessionFactory on bean factory shutdown. */ public void destroy() throws HibernateException { logger.info("Closing Hibernate SessionFactory"); if (this.dataSource != null) { // Make given DataSource available for potential SchemaExport, // which unfortunately reinstantiates a ConnectionProvider. configTimeDataSourceHolder.set(this.dataSource); } try { this.sessionFactory.close(); } finally { if (this.dataSource != null) { // Reset DataSource holder. configTimeDataSourceHolder.set(null); } } } /** * Invocation handler that delegates <code>getCurrentSession()</code> calls * to SessionFactoryUtils, for being aware of thread-bound transactions. */ private static class TransactionAwareInvocationHandler implements InvocationHandler { private final SessionFactory target; public TransactionAwareInvocationHandler(SessionFactory target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Invocation on SessionFactory/SessionFactoryImplementor interface coming in... if (method.getName().equals("getCurrentSession")) { // Handle getCurrentSession method: return transactional Session, if any. try { return SessionFactoryUtils.doGetSession((SessionFactory) proxy, false); } catch (IllegalStateException ex) { throw new HibernateException(ex.getMessage()); } } else if (method.getName().equals("equals")) { // Only consider equal when proxies are identical. return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); } else if (method.getName().equals("hashCode")) { // Use hashCode of SessionFactory proxy. return new Integer(hashCode()); } // Invoke method on target SessionFactory. try { return method.invoke(this.target, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } }

Spring源代碼分析(1)---LocalSessionFactoryBean(工廠的工廠)