场景:
对spring mvc的controller进行拦截,且在s(springmvc)s(spring)h(hibernate)环境中异步处理数据。
问题:
1 spring aop配置:
如果是对service/dao进行拦截处理,在applicationContext.xml中配置且点/处理切面等数据(项目中此文件名称会变化);
如果是对controller进行拦截出,在servlet.xml中配置且点/处理切面等数据(项目中此文件名称会变化);
具体不同主要是因为controller和service/dao的初始化时间不一致,如果配置错误,将不能对对应的类进行正确的aop增强,导致aop无效(可以参考代理的原理)。
2 hibernate的session问题:
在ssh环境中,为了保证session的一致性,spring通过filter在请求创建时对线程进行了绑定,保证controller/service/dao使用的是同一个session;但是如果我们使用线程池创建线程对数据进行处理,不存在绑定问题,在查询实体关联属性的时候会出现session提前关闭。具体异常如could not initialize proxy - no Session(即使你使用官方ThreadLocal绑定session,但是在dao拿到的session和此时绑定的必然不一样),解决方法,如下:
// 创建session给TransactionSynchronizationManager /**reference OpenSessionInViewFilter.doFilterInternal*/ public void sessionToSpring() { // single session mode if (TransactionSynchronizationManager.hasResource(sessionFactory)) { // Do not modify the Session: } else { Session session = getSession(sessionFactory); TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); } } // 创建session /**copy OpenSessionInViewFilter.getSession*/ public Session getSession(SessionFactory sessionFactory) { Session session = SessionFactoryUtils.getSession(sessionFactory, true); FlushMode flushMode = FlushMode.MANUAL; if (flushMode != null) { session.setFlushMode(flushMode); } return session; } // 关闭 /**reference OpenSessionInViewFilter.doFilterInternal*/ public void sessionClose() { SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory); SessionFactoryUtils.closeSession(sessionHolder.getSession()); }
另外,这样只是解决了session问题,但是如果你需要用到事务,你会发现问题依然存在,spring的事务标记 没有作用,同样session问题。因为此时你操作数据的session和代理生成的session不一致,不能正确的把你的数据进行提交回滚。解决方式:
1 获得spring配置时配置的事务管理器 @Autowired private HibernateTransactionManager transactionManager; private TransactionDefinition definition = new DefaultTransactionDefinition(); 2 在事务起始地点 获取事务 TransactionStatus status = transactionManager.getTransaction(definition); 3 在事务结束地点 提交事务 或回滚事务 transactionManager.commit(status); transactionManager.rollback(status);
具体可以看源码,原理核心就是将一个session和事务起始点、结束点能对应起来。