【发布时间】:2014-06-23 13:41:02
【问题描述】:
我有一个@Transactional @Controller,但它的方法正在由 Spring MVC 框架调用而没有事务。在异常跟踪中,我没有找到拦截调用的事务顾问:
org.hibernate.HibernateException: No Session found for current thread
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:106)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
org.example.businesslogic.MyController.userLoggedIn(SwiperRest.java:48)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:483)
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
另一方面,日志清楚地表明控制器方法被检测为事务性:
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'metaDataSourceAdvisor'
DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'MyController.userLoggedIn' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG o.s.a.f.a.InfrastructureAdvisorAutoProxyCreator - Creating implicit proxy for bean 'myController' with 0 common interceptors and 1 specific interceptors
DEBUG o.s.a.f.CglibAopProxy - Creating CGLIB proxy: target source is SingletonTargetSource for target object [org.example.businesslogic.MyController@7c0f1b7c]
DEBUG o.s.a.f.CglibAopProxy - Unable to apply any optimisations to advised method: public java.lang.String org.example.businesslogic.MyController.userLoggedIn(java.lang.String,java.lang.String)
DEBUG o.s.t.a.AnnotationTransactionAttributeSource - Adding transactional method 'MyController.locationProfiles' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG o.s.a.f.CglibAopProxy - Unable to apply any optimisations to advised method: public java.util.List org.example.businesslogic.MyController.locationProfiles(java.lang.String)
来自控制器类的 sn-p:
@Transactional
@Controller
@RequestMapping("/zendor")
public class MyController
{
@Autowired private SessionFactory sf;
@RequestMapping(method=POST, value="userLoggedIn")
public @ResponseBody String userLoggedIn(@RequestParam String u_id, @RequestParam String d_id) {
Session hb = sf.getCurrentSession();
...
}
}
这是我的网络应用程序初始化类,我没有web.xml:
public class WebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
@Override
protected Class<?>[] getRootConfigClasses() { return new Class[] { RootConfig.class }; }
@Override
protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }
@Override
protected String[] getServletMappings() { return new String[] { "/" }; }
@Override public void onStartup(ServletContext ctx) throws ServletException {
ctx.setInitParameter("spring.profiles.active", "production");
super.onStartup(ctx);
}
}
这是引用的根配置:
package org.example.config;
@Configuration
@ComponentScan
public class RootConfig
{
}
它与这些在同一个包中,被默认组件扫描范围拾取:
@Configuration
@EnableWebMvc
@ComponentScan("org.example.businesslogic")
public class WebMvcConfig extends WebMvcConfigurationSupport
{
}
@Configuration
@EnableTransactionManagement
@ComponentScan("org.example.businesslogic")
public class DataConfig implements TransactionManagementConfigurer
{
@Autowired private DataSource dataSource;
...
}
当 Spring-test 的 SpringJUnit4ClassRunner 使用相同的配置时,方法会得到建议并且事务会起作用。
我也尝试将userLoggedIn方法提取为@Autowired@Transactional @Component,但结果是一样的。
我应该从哪个方向研究来解决这个问题?
我在 Spring 4.0.5。
更新 1
关键问题是我的根配置还引入了所有其他配置类,包括 WebMvcConfig,它作为子 servlet 配置再次加载。
非常违反直觉,只有当我删除 servlet 配置类,替换
@Override
protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }
与
@Override
protected Class<?>[] getServletConfigClasses() { return null; }
这直接违背了文档:may not be empty or null。如果我反其道而行之,为rootConfigClasses 提供null,为servletConfigClasses 提供RootConfig,那么一切都会更加失败,“找不到servlet 上下文”。
更新 2
在没有根应用上下文的情况下发生的故障已追溯到 Spring Web Security,显然必须在根级别配置它才能被SecurityWebApplicationInitializer 拾取,因为这似乎是在根时执行的阶段应用上下文已经存在,但网络应用上下文不存在。所以我的问题解决方案是在 root 和 webapp 上下文之间引入分离,其中 root 加载安全性和 webapp 其他所有内容。
【问题讨论】:
-
一个明显的问题:为什么您希望您的控制器具有事务性?通常,控制器调用事务性方法。
-
为了简单起见,目前。在应用程序生命周期的这一点上,我不需要额外的层。
-
你是否尝试过基于类的代理,例如
? -
@NathanHughes 这不是 Spring 的默认方式吗?我没有使用 AspectJ。
-
@Marko:再次查看您的示例,我认为它可能不相关。文档说“但是,如果控制器必须实现不是 Spring Context 回调的接口(例如 InitializingBean、*Aware 等),您可能需要显式配置基于类的代理。”docs.spring.io/spring/docs/4.1.0.BUILD-SNAPSHOT/…
标签: java spring spring-mvc transactions spring-jdbc