首页 > 代码库 > Spring源代码分析(1)---LocalSessionFactoryBean(工厂的工厂)

Spring源代码分析(1)---LocalSessionFactoryBean(工厂的工厂)

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(工厂的工厂)