java – 即使在日志中看到“添加事务方法”,方法也不被事务顾问程序拦截
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了java – 即使在日志中看到“添加事务方法”,方法也不被事务顾问程序拦截,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含7894字,纯文字阅读大概需要12分钟。
内容图文
我有一个@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)
来自控制器类的片段:
@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应用程序初始化类,我没有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
关键问题是我的root配置也包括所有其他配置类,包括WebMvcConfig,它再次作为子servlet配置加载.
相反违反直觉,当我删除servlet配置类,替换时,事情才开始工作
@Override
protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }
同
@Override
protected Class<?>[] getServletConfigClasses() { return null; }
直接针对文档:可能不为空或为空.如果我执行相反的操作,为rootConfigClasses和rootConfig为servletConfigClasses赋予null,那么一切都会失败,因为“找不到servlet上下文.”.
更新2
没有root应用程序上下文的故障已经追溯到Spring Web Security,它必须在根级别配置才能被SecurityWebApplicationInitializer选中,因为这似乎是在根应用程序上下文已经存在的阶段执行的,但不是网络应用程序上下文.所以我的问题解决方案是在root和webapp上下文之间引入分离,其中root加载安全性和webapp其他所有内容.
解决方法:
如果你还没有读过它们
> What is the difference between ApplicationContext and WebApplicationContext in Spring MVC?
> Difference between applicationContext.xml and spring-servlet.xml in Spring Framework
这同样适用于AbstractAnnotationConfigDispatcherServletInitializer的getRootConfigClasses()和getServletConfigClasses().基本上,WebApplicationInitializer将构造(并注册)一个ContextLoaderListener,其中AnnotationConfigWebApplicationContext从getRootConfigClasses()注册所有@Configuration(和其他@Component注释)类.然后,它将使用getServletConfigClasses()中的所有@Configuration(和其他…)类构造和注册DispatcherServlet.
作为Servlet生命周期的一部分,容器将首先初始化所有ServletContextListener对象.这意味着ContextLoaderListener将首先加载并刷新给它的AnnotationConfigWebApplicationContext(如果它尚未刷新,理想情况下它不应该刷新).它还将此ApplicationContext作为ServletContext中的属性.
然后容器将初始化已注册的DispatcherServlet.这里有更多的阅读
> How does the web container manage the lifecycle of a spring controller
> SpringMVC lifecycle– the overall view
基本上,DispatcherServlet将刷新它收到的ApplicationConfigWebApplicationContext,首先将其父级设置为ServletContext中的ApplicationContext(由ContextLoaderListener设置)(如果有的话).
然后它将开始从其ApplicationContext中选择和选择bean来设置MVC堆栈,控制器,处理程序方法,拦截器等.默认情况下,它只会在它加载的ApplicationContext中查找其处理程序bean,@ Controller bean,而不是其父母.
你似乎做的是
@Override
protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }
和
@Override
protected Class<?>[] getRootConfigClasses() { return new Class[] { RootConfig.class }; }
在这种情况下,ContextLoaderListener将加载RootConfig,它将创建一堆bean,包括@Controller类的bean,将通过@Transactional配置建议.
DispatcherServlet然后将加载具有自己的@ComponentScan的WebMvcConfig,这将创建新的@Controller bean,但是不会建议这些,因为没有注册TransactionInterceptor(在此上下文中没有@EnableTransactionManagement).然后DispatcherServlet将尝试在自己的ApplicationContext中查找所有@Controller bean(以及其他具有@RequestMapping方法的bean).它会找到这些不建议的@Controller bean.这些是它将注册为处理程序的那些,而不是ContextLoaderListener加载的那些.
如果您在日志中向下看,您应该看到正在创建一个新的控制器bean.
建议:
>根上下文:整个应用程序应该可见的内容
> Servlet上下文:MVC堆栈应该可见的内容
控制器不是整个应用程序应该访问的组件.只有DispatcherServlet应该关心它们.将它们放在servlet上下文中.
现在我显然不知道你的整个应用程序,但我建议你从处理程序方法和一些@Service方法中重构所有事务逻辑.这将使您更容易维护您的配置,并使您的控制器更多控制器,即.委托给模特.
内容总结
以上是互联网集市为您收集整理的java – 即使在日志中看到“添加事务方法”,方法也不被事务顾问程序拦截全部内容,希望文章能够帮你解决java – 即使在日志中看到“添加事务方法”,方法也不被事务顾问程序拦截所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。