【www.gbppp.com--私藏美文】
单例模式相信大家都已经很熟悉,主要就是为了保证在堆区中只能有一个对象,下面给出一个典型的例子.
view plaincopy to clipboardprint?
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static Singleton newInstance(){
if(singleton==null)
singleton=new Singleton();
return singleton;
}
}
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static Singleton newInstance(){
if(singleton==null)
singleton=new Singleton();
return singleton;
}
}因为构造方法是私有的,不能直接通过new关键字实例化对象,那么只能调用静态方法,以得到相同的结果!
通常我们是这样做:Singleton singleton=Singleton.newInstance();的确,这样做是能保证我们得到相同对象,但前提是在单线程的程序.
假设有如下线程类生成Singleton对象:
view plaincopy to clipboardprint?
public class User extends Thread{
private Singleton singleton;
public User(){
}
public void run(){
singleton=Singleton.newInstance();
System.out.println(singleton);
}
}
public class User extends Thread{
private Singleton singleton;
public User(){
}
public void run(){
singleton=Singleton.newInstance();
System.out.println(singleton);
}
}测试类:
view plaincopy to clipboardprint?
public class SingletonTest{
public static void main(String args[]){
User user1=new User();
User user2=new User();
user1.start();
user2.start();
}
}
}
public class SingletonTest{
public static void main(String args[]){
User user1=new User();
User user2=new User();
user1.start();
user2.start();
}
}
}结果是什么?不清楚......
if(singleton==null) singleton=new Singleton();这行决定着我们的对象是否单例!
为了方便说明问题,把Singleton类稍微改动下:
view plaincopy to clipboardprint?
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static Singleton newInstance(){
if(singleton==null){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton=new Singleton();
}
return singleton;
}
}
public class Singleton {
private static Singleton singleton;
private Singleton(){
}
public static Singleton newInstance(){
if(singleton==null){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton=new Singleton();
}
return singleton;
}
}再运行下,发现什么?线程的run方法中打印的是2个不同的对象!
为什么?一个线程调用newInstance(),发现singleton是null,就进入了if(singleton==null){}方法体,这时当前线程sleep了500毫秒,就在这500毫秒期间,另一个线程也调用了newInstance(),发现singleton也是null,也进入了if(singleton==null){}方法体,当前线程也sleep了500毫秒,这时候第一个线程得到机会继续向下运行,执行singleton=new Singleton();返回后,第二个线程也得到运行的机会,同样也执行了singleton=new Singleton();这2次实例化的对象显然不是同一个对象
单例模式不就被我们打破了吗?!但这种打破是计算机的行为,如果不显示地让线程sleep,我们无法预知能生成几个对象!不过这种问题还是可以被解决,给对象加锁,也就是在你调用的方法前加synchronized关键字,或者把你的方法写在synchronized(lock){},lock可以是任何实例化的对象,因为任何对象都有个内部锁!
说了这么多,其实还没到正题,我们要人为地打破单例!
首先:得到一个对象的方法有几种呢?
1.new 关键字,最常用的,也最方便的
2.工厂模式,内部可以用任何实例化对象的方式返回对象,解耦用的
3.反序列化,从硬盘或网络中反序列化对象
4.反射
首先看个例子:
view plaincopy to clipboardprint?
public class SingletonTest{
public static void main(String args[]){
Singleton singleton1=null;
Singleton singleton2=null;
Class clazz=null;
Class[] param=new Class[]{};
try {
clazz=Singleton.class;
Constructor constructor=clazz.getDeclaredConstructor(param);
constructor.setAccessible(true);
singleton1=(Singleton)constructor.newInstance(new Object[]{});
singleton2=(Singleton)constructor.newInstance(new Object[]{});
System.out.println(singleton1==singleton2);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public class SingletonTest{
public static void main(String args[]){
Singleton singleton1=null;
Singleton singleton2=null;
Class clazz=null;
Class[] param=new Class[]{};
try {
clazz=Singleton.class;
Constructor constructor=clazz.getDeclaredConstructor(param);
constructor.setAccessible(true);
singleton1=(Singleton)constructor.newInstance(new Object[]{});
singleton2=(Singleton)constructor.newInstance(new Object[]{});
System.out.println(singleton1==singleton2);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}运行结果:false!充分说明singleton1,singleton1这2个引用指向的是2个不同的对象!,之所以做到这点,
就是充分利用了java的反射机制!constructor.setAccessible(true);使得构造方法的访问权限修饰符形同虚设!同样可以适用Field,Method!
至此,我们已经人为地打破了单例模式!
做这个,只是为了说明反射的重要及有趣!
一、摘要
上两篇文章分别介绍了Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法 和 Spring3 整合Hibernate3.5 动态切换SessionFactory (切换数据库方言),这篇文章将介绍Spring整合Mybatis 如何完成SqlSessionFactory的动态切换的。并且会简单的介绍下MyBatis整合Spring中的官方的相关代码。
Spring整合MyBatis切换SqlSessionFactory有两种方法,第一、 继承
SqlSessionDaoSupport,重写获取SqlSessionFactory的方法。第二、继承
SqlSessionTemplate 重写getSqlSessionFactory、getConfiguration和
SqlSessionInterceptor这个拦截器。其中最为关键还是继承SqlSessionTemplate 并重写里面的方法。
而Spring整合MyBatis也有两种方式,一种是配置MapperFactoryBean,另一种则是利用MapperScannerConfigurer进行扫描接口或包完成对象的自动创建。相对来说后者更方便些。MapperFactoryBean继承了SqlSessionDaoSupport也就是动态切换SqlSessionFactory的第一种方法,我们需要重写和实现SqlSessionDaoSupport方法,或者是继承
MapperFactoryBean来重写覆盖相关方法。如果利用MapperScannerConfigurer的配置整合来切换SqlSessionFactory,那么我们就需要继承SqlSessionTemplate,重写上面提到的方法。在整合的配置中很多地方都是可以注入SqlSessionTemplate代替SqlSessionFactory的注入的。因为SqlSessionTemplate的创建也是需要注入SqlSessionFactory的。
二、实现代码
1、继承SqlSessionTemplate 重写getSqlSessionFactory、getConfiguration和SqlSessionInterceptor package com.hoo.framework.mybatis.support;
import static java.lang.reflect.Proxy.newProxyInstance;
import static
org.apache.ibatis.reflection.ExceptionUtil.unwrapThrowable;
import static org.mybatis.spring.SqlSessionUtils.closeSqlSession; import static org.mybatis.spring.SqlSessionUtils.getSqlSession; import static
org.mybatis.spring.SqlSessionUtils.isSqlSessionTransactional;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.SqlSessionTemplate;
import
org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.util.Assert;
/**
* <b>function:</b> 继承SqlSessionTemplate 重写相关方法
* @author hoojo
* @createDate 2013-10-18 下午03:07:46
* @file CustomSqlSessionTemplate.java
* @package com.hoo.framework.mybatis.support
* @project SHMB
* @blog
* @email hoojo_@126.com
* @version 1.0
*/
public class CustomSqlSessionTemplate extends SqlSessionTemplate {
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
private Map<Object, SqlSessionFactory> targetSqlSessionFactorys; private SqlSessionFactory defaultTargetSqlSessionFactory;
public void setTargetSqlSessionFactorys(Map<Object,
SqlSessionFactory> targetSqlSessionFactorys) {
this.targetSqlSessionFactorys = targetSqlSessionFactorys; }
public void setDefaultTargetSqlSessionFactory(SqlSessionFactory defaultTargetSqlSessionFactory) {
this.defaultTargetSqlSessionFactory =
defaultTargetSqlSessionFactory;
}
public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
this(sqlSessionFactory,
sqlSessionFactory.getConfiguration().getDefaultExecutorType()); }
public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
this(sqlSessionFactory, executorType, new
MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration() .getEnvironment().getDataSource(), true));
}
public CustomSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
super(sqlSessionFactory, executorType, exceptionTranslator);
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
this.defaultTargetSqlSessionFactory = sqlSessionFactory; }
@Override
public SqlSessionFactory getSqlSessionFactory() {
SqlSessionFactory targetSqlSessionFactory =
targetSqlSessionFactorys.get(CustomerContextHolder.getContextType()); if (targetSqlSessionFactory != null) {
return targetSqlSessionFactory;
} else if (defaultTargetSqlSessionFactory != null) {
return defaultTargetSqlSessionFactory;
} else {
Assert.notNull(targetSqlSessionFactorys, "Property
'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
}
return this.sqlSessionFactory;
}
@Override
public Configuration getConfiguration() {
return this.getSqlSessionFactory().getConfiguration();
}
public ExecutorType getExecutorType() {
return this.executorType;
}
public PersistenceExceptionTranslator
getPersistenceExceptionTranslator() {
return this.exceptionTranslator;
}
/**
* {@inheritDoc}
*/
public <T> T selectOne(String statement) {
return this.sqlSessionProxy.<T> selectOne(statement);
}
/**
* {@inheritDoc}
*/
public <T> T selectOne(String statement, Object parameter) {
return this.sqlSessionProxy.<T> selectOne(statement, parameter); }
/**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.sqlSessionProxy.<K, V> selectMap(statement, mapKey); }
/**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey);
}
/**
* {@inheritDoc}
*/
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
return this.sqlSessionProxy.<K, V> selectMap(statement, parameter, mapKey, rowBounds);
}
/**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement) {
return this.sqlSessionProxy.<E> selectList(statement);
}
/**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement, Object parameter) { return this.sqlSessionProxy.<E> selectList(statement,
parameter);
}
/**
* {@inheritDoc}
*/
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds);
}
/**
* {@inheritDoc}
*/
为了避免硬编码的资源查找与应用程序对象紧密耦合,Spring允许你在application context中以bean的方式定义诸如JDBC DataSource或者Hibernate SessionFactory 的数据访问资源。任何需要进行资源访问的应用程序对象只需要持有这些事先定义好的实例的引用(DAO定义在下一章节介绍),下面的代码演示如何创建一个JDBC DataSource 和Hibernate SessionFactory【sessionfactory】
<beans>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<property name="mappingResources">
<list>
<value>product.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
</value>
</property>
</bean>
...
</beans>
将一个本地定义的,如Jakarta Commons DBCP的 BasicDataSource 切换为一个JNDI定位的DataSource(通常由J2EE Server管理),仅仅需要改变配置:
<beans>
<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/myds"/>
</bean>
...
</beans>
你也可以访问一个JNDI定位的Hibernate SessionFactory,通过使用Spring的 JndiObjectFactoryBean 来暴露和获取。 当然,如果在EJB上下文之外,这是不必要的。
12.2.3. HibernateTemplate
对于特定的数据访问对象或业务对象的方法来说,基本的模板编程模型看起来像下面所示的代码那样。 对于这些外部对象来说,没有任何实现特定接口的要求,仅仅要求提供一个Hibernate SessionFactory。 它可以从任何地方得到,不过比较适宜的方法是从Spring的application context中得到的bean引用:通过简单的 setSessionFactory(..) 这个bean的setter方法。 下面的代码展示了在application context中一个DAO的定义,它引用了上面定义的 SessionFactory,同时展示了一个DAO方法的具体实现。
<beans>
...
<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
</beans>
public class ProductDaoImpl implements ProductDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Collection loadProductsByCategory(final String category) throws DataAccessException {
HibernateTemplate ht = new HibernateTemplate(this.sessionFactory);
return (Collection) ht.execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException { Query query = session.createQuery(
"from test.Product product where product.category=?");
query.setString(0, category);
return query.list();
}
});
}
}
一个回调实现能够有效地在任何Hibernate数据访问中使用。HibernateTemplate 会确保当前Hibernate的 Session 对象的正确打开和关闭,并直接参与到事务管理中去。 Template实例不仅是线程安全的,同时它也是可重用的。因而他们可以作为外部对象的实例变量而被持有。对于那些简单的诸如find、load、saveOrUpdate或者delete操作的调用,HibernateTemplate 提
供可选择的快捷函数来替换这种回调的实现。 不仅如此,Spring还提供了一个简便的 HibernateDaoSupport 基类,这个类提供了 setSessionFactory(..) 方法来接受一个 SessionFactory 对象,同时提供了 getSessionFactory() 和 getHibernateTemplate() 方法给子类使用。 综合了这些,对于那些典型的业务需求,就有了一个非常简单的DAO实现:
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
public Collection loadProductsByCategory(String category) throws DataAccessException { return getHibernateTemplate().find(
"from test.Product product where product.category=?", category);
}
}
12.2.4. 不使用回调的基于Spring的DAO实现
作为不使用Spring的 HibernateTemplate 来实现DAO的替代解决方案,你依然可以用传统的编程风格来编写你的数据访问代码。 无需将你的Hibernate访问代码包装在一个回调中,只需符合Spring的通用的 DataAccessException 异常体系。 Spring的 HibernateDaoSupport 基类提供了访问与当前事务绑定的 Session 对象的函数,因而能保证在这种情况下异常的正确转化。 类似的函数同样可以在 SessionFactoryUtils 类中找到,但他们以静态方法的形式出现。 值得注意的是,通常将一个false作为参数(表示是否允许创建)传递到 getSession(..) 方法中进行调用。 此时,整个调用将在同一个事务内完成(它的整个生命周期由事务控制,避免了关闭返回的 Session 的需要)。
public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
public Collection loadProductsByCategory(String category)
throws DataAccessException, MyException {
Session session = getSession(getSessionFactory(), false);
try {
List result = session.find(
"from test.Product product where product.category=?",
category, Hibernate.STRING);
if (result == null) {
throw new MyException("invalid search result");
}
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
}
}
这种直接使用Hibernate访问代码的主要好处在于它允许你在数据访问代码中抛出checked exception,而 HibernateTemplate 却受限于回调中的unchecked exception。 注意,你通常可
以将这些应用程序的异常处理推迟到回调函数之后,这样,你依然可以正常使用 HibernateTemplate。 一般来说,HibernateTemplate 所提供的许多方法在许多情况下看上去更简单和便捷。
12.2.5. 基于Hibernate3的原生API实现DAO
Hibernate 3.0.1引入了一个新的特性:“带上下文环境的Session”。 这一特性使得Hibernate自身具备了每个事务绑定当前 Session 对象的功能。 这与Spring中每个Hibernate的 Session 与事务同步的功能大致相同。一个相应的基于原生的Hibernate API的DAO实现正如下例所示:
public class ProductDaoImpl implements ProductDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Collection loadProductsByCategory(String category) {
return this.sessionFactory.getCurrentSession()
.createQuery("from test.Product product where product.category=?") .setParameter(0, category)
.list();
}
}
这种Hibernate数据访问的风格与你在Hibernate文档和示例中见到的非常类似,除了DAO实现类中持有了一个 SessionFactory 的实例变量。 我们强烈推荐这种基于实例变量的DAO构建方式,而不是使用那种过去由Hibernate的示例程序中提到的 静态的 HibernateUtil 类。 (通常来说,不要在静态变量中保存任何资源信息除非 确实 有这个必要)。
上面我们所列出的DAO完全遵循IoC:它如同使用Spring的 HibernateTemplate 进行编程那样,适合在application context中进行配置。 具体来说,它使用了Setter注入;如果你愿意,完全可以使用Constructor注入方式替代。当然,这样的DAO还可以建立在一个普通的Java类中(诸如在Unit Test中): 仅仅需要初始化这个类, 调用 setSessionFactory(..) 方法设置你所期望的工厂资源。 以Spring的bean的定义方式,它看上去就像这样:
<beans>
...
<bean id="myProductDao" class="product.ProductDaoImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
</beans>
这种DAO访问方式的主要优势在于它仅仅依赖于Hibernate API本身而无需引入任何Spring的类。 从无入侵性的角度来看,这一点非常吸引人。同时,对于Hibernate开发人员来说也更自然。
然而,这样的DAO访问方式会抛出 HibernateException,这是一个无需声明或捕获的unchecked exception。 这意味着,DAO的调用者只能以普通的错误来处理这些异常,除非完全依赖Hibernate自身的异常体系。 因而,除非你将DAO的调用者绑定到具体的实现策略上去,否则你将无法捕获特定的异常原因(诸如乐观锁异常)。 这种折中平衡或许可以被接受,如果你的应用完全基于Hibernate或者无需进行特殊的异常处理。
幸运的是,Spring的 LocalSessionFactoryBean 可以在任何Spring的事务管理策略下, 提供对Hibernate的 SessionFactory.getCurrentSession() 函数的支持,它将返回当前受Spring事务管理(甚至是 HibernateTransactionManager 管理的)的 Session 对象。 当然,该函数的标准行为依然有效:返回当前与正在进行的JTA事务(无论是Spring的 JtaTransactionManager、EJB CMT或者普通的JTA)绑定的 Session 对象。
总体来说,DAO可以基于Hibernate3的原生API实现,同时,它依旧需要能够参与到Spring的事务管理中。 这对于那些已经对Hibernate非常熟悉的人来说很有吸引力,因为这种方式更加自然。 不过,此时的DAO将抛出HibernateException,因而,如果有必要的话,需要明确地去做由 HibernateException 到Spring的 DataAccessException 的转化。
本文来源:http://www.gbppp.com/jd/442050/
推荐访问:sessionfactory的作用 获取sessionfactory