一、Spring启动加载静态数据的方法
利用Spring启动加载静态数据,有那么几种方法
1. 实现BeanPostProcessor接口
@Component
public class LoadDataCache implements BeanPostProcessor {
@@Resource
private IDao dao;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Bean实例化之前执行
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// Bean实例化之后执行
List list = dao.findAll();
..... // 做一些事情
return bean;
}
}
2. 实现ApplicationListener接口
@Component
public class LoadDataCache implements ApplicationListener<ApplicationEvent> {
@Resource
private IDao dao;
@Override
public void onApplicationEvent(ApplicationEvent arg0) {
// 在spring所有资源(xml和注解)加载注入完毕后执行
List list = dao.findAll();
..... // 做一些事情
}
}
3. 利用bean便签中的init-method属性
@Component
public class LoadDataCache {
@Resource
private IDao dao;
public void init() {
// 在Bean实例化之前执行
List list = dao.findAll();
..... // 做一些事情
}
}
但是加载静态数据时,如果Dao层获取Hibernate的Session是CurrentSession,那么或许你就会遇到类似No Session found for current thread异常。
之所以产生这个异常,是因为该Dao的执行方法没有纳入事务的管理,可能一些人说,我明明配置了事务呀,配置也看不出什么问题,可为什么还会报异常呢?我也是困惑了好一阵才弄明白。
Spring实现事务的方式是采用Aop技术,既然是Aop,自然离不开代理,可要想代理一个类,该类就必须首先要实例化才行!
仔细看我上面几个例子的注释说明,如果在Bean实例化之前执行Dao层操作就会产生异常。那么上述几种方法中,第三种方法明显存在问题。但我说了第三种方法也是可以实现启动加载的,那么怎么做呢?
二、第三种方法解决方案
1). 另建一个类,Spring配置init-method指向到该类的init方法,在该类的init方法中再执行LoadDataCache类的init方法。其实就相当于建一个包装类封装一下。
2). 注入SessionFactory,开启一个session并绑定到事务里。
@Component
public class LoadDataCache {
@Resource
private IDao dao;
@Resource
private SessionFactory sessionFactory;
public void init() {
Session session = sessionFactory.openSession();
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
List list = dao.findAll();
..... // 做一些事情
}
}
3). 注入SessionFactory,开启一个session并自己打开事务并执行查询,换句话说,就不用IDao里的方法了。
三、内部方法的相互调用时产生的事务问题
另外,我们习惯将事务管理控制在service层,但如果发生如下情况时:
public interface AService {
public void a();
public void b();
}
@Service()
public class AServiceImpl1 implements AService {
// 或者采用声明式事务一样
@Transactional(propagation = Propagation.REQUIRED)
public void a() {
this.b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
}
}
这时同样会发生No Session found for current thread异常,其道理一样是Aop的原因,this.b()是自己直接调用自己内部的一个方法,因而在执行b方法时并不是通过代理去执行的。最简单的解决办法:
在Spring配置文件中配置,开启暴露Aop代理到ThreadLocal的支持
<aop:aspectj-autoproxy expose-proxy="true"/><!—注解风格支持-->
<aop:config expose-proxy="true"><!—xml风格支持-->
然后修改this.b()为((AService) AopContext.currentProxy()).b()。
四、AOP原理图
关于AOP知识的详细内容,大家可上网查阅相关资料,我也曾经写过介绍的文章,这里就不再叙述了。