Mar*_*nik 5 java spring transactions spring-mvc spring-jdbc
我有一个@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)
Run Code Online (Sandbox Code Playgroud)
另一方面,日志清楚地表明控制器方法被检测为事务性的:
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)
Run Code Online (Sandbox Code Playgroud)
控制器类的一个片段:
@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();
...
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的 Web 应用程序初始值设定项类,我没有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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是引用的根配置:
package org.example.config;
@Configuration
@ComponentScan
public class RootConfig
{
}
Run Code Online (Sandbox Code Playgroud)
它与这些在同一个包中,由默认组件扫描范围拾取:
@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;
...
}
Run Code Online (Sandbox Code Playgroud)
当 Spring-test 使用相同的配置时SpringJUnit4ClassRunner,这些方法会得到建议并且事务工作。
我也尝试将userLoggedIn方法提取到 an @Autowired @Transactional @Component,但结果是相同的。
我应该向哪个方向调查来解决这个问题?
我在 Spring 4.0.5。
关键问题是我的根配置也引入了所有其他配置类,包括WebMvcConfig,它作为子 servlet 配置再次加载。
与直觉相反,当我删除servlet 配置类时,事情才开始工作,替换
@Override
protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }
Run Code Online (Sandbox Code Playgroud)
和
@Override
protected Class<?>[] getServletConfigClasses() { return null; }
Run Code Online (Sandbox Code Playgroud)
这直接与文档背道而驰:may not be empty or null. 如果我做相反的事情,给nullforrootConfigClasses和RootConfigfor servletConfigClasses,那么一切都会更加失败,“找不到 servlet 上下文。”。
失败没有根应用方面存在的已追踪到的Spring Web安全,这显然必须在根级别配置,以由被拾起SecurityWebApplicationInitializer,因为这似乎当根应用程序上下文已经存在的阶段执行,但不是网络应用程序上下文。所以我的问题解决方案是在 root 和 webapp 上下文之间引入分离,其中 root 加载安全性和 webapp 其他所有内容。
如果您还没有阅读过它们
这同样适用于AbstractAnnotationConfigDispatcherServletInitializer和getRootConfigClasses()。getServletConfigClasses()基本上,这WebApplicationInitializer将构造(并注册) aContextLoaderListener并AnnotationConfigWebApplicationContext注册 中的所有@Configuration(和其他@Component带注释的)类getRootConfigClasses()。然后它将构造并注册aDispatcherServlet与.@ConfigurationgetServletConfigClasses()
作为 Servlet 生命周期的一部分,容器将首先初始化所有ServletContextListener对象。这意味着ContextLoaderListener将首先加载并提供给它的refresh内容AnnotationConfigWebApplicationContext(如果尚未刷新,理想情况下不应刷新)。它还会将其ApplicationContext作为属性放入ServletContext.
然后容器将初始化注册的DispatcherServlet. 这里还有一些阅读内容
基本上,它通过首先将其父级设置为(由 设置)(如果有的话)来接收的DispatcherServlet意志。refreshApplicationConfigWebApplicationContextApplicationContextServletContextContextLoaderListener
然后,它将开始从中挑选 bean,ApplicationContext以设置 MVC 堆栈、控制器、处理程序方法、拦截器等。默认情况下,它只会在加载的文件中查找其处理程序 bean、@Controllerbean ApplicationContext,而不是其父级 bean。 )。
你似乎做了什么
@Override
protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }
Run Code Online (Sandbox Code Playgroud)
和
@Override
protected Class<?>[] getRootConfigClasses() { return new Class[] { RootConfig.class }; }
Run Code Online (Sandbox Code Playgroud)
在这种情况下,ContextLoaderListener将加载RootConfig,这将创建一堆 bean,包括您的@Controller类的 bean,这些 bean 将通过配置进行建议@Transactional。
然后将DispatcherServlet加载WebMvcConfig它自己的@ComponentScanbean,这将创建新的@Controllerbean,但不会建议这些bean,因为没有TransactionInterceptor注册(@EnableTransactionManagement在本上下文中没有注册)。然后,它将DispatcherServlet尝试在其自己的 .xml 文件中查找所有@Controllerbean(以及其他具有@RequestMapping方法的 bean)ApplicationContext。它会找到这些@Controller不建议的豆子。这些是它将注册为处理程序的那些,而不是由ContextLoaderListener.
如果您进一步查看日志,您应该会看到正在创建一个新的控制器 bean。
建议:
控制器不是整个应用程序应该有权访问的组件。只有他们才DispatcherServlet应该关心他们。将它们放入 servlet 上下文中。
现在我显然不知道您的整个应用程序,但我建议您将所有事务逻辑从处理程序方法重构为某些@Service方法。它将使维护您的配置变得更容易,并使您的控制器更加控制器化,即。委托给模型。
| 归档时间: |
|
| 查看次数: |
2191 次 |
| 最近记录: |