gpt4 book ai didi

java - 即使在日志中看到 "adding transactional method",事务顾问也不会拦截方法

转载 作者:搜寻专家 更新时间:2023-11-01 02:45:06 26 4
gpt4 key购买 nike

我有一个@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)

另一方面,日志清楚地表明 Controller 方法被检测为事务性的:

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)

来自 Controller 类的片段:

@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

关键问题是我的根配置也引入了所有其他配置类,包括 WebMvcConfig,它作为子 servlet 配置再次加载。

非常违反直觉,只有当我删除 servlet 配置类,替换

    @Override
protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }

    @Override
protected Class<?>[] getServletConfigClasses() { return null; }

这直接违背了文档:可能不为空或 null。如果我反其道而行之,为 rootConfigClasses 提供 null,为 servletConfigClasses 提供 RootConfig,那么一切都将更加失败,因为“找不到 servlet 上下文。”。

更新2

在没有根应用程序上下文的情况下发生的故障已追溯到 Spring Web Security,显然必须在根级别配置它才能被 SecurityWebApplicationInitializer 获取,因为这似乎在执行根应用程序上下文已经存在但网络应用程序上下文不存在的阶段。所以我的问题解决方案是在根和 webapp 上下文之间引入一个分离,其中 root 加载安全和 webapp 一切。

最佳答案

如果你还没有读过它们

同样适用于AbstractAnnotationConfigDispatcherServletInitializergetRootConfigClasses()getServletConfigClasses() .基本上就是WebApplicationInitializer将构造(并注册)一个 ContextLoaderListenerAnnotationConfigWebApplicationContext注册所有 @Configuration (和其他 @Component 注释)类来自 getRootConfigClasses() .然后它将构造并注册一个DispatcherServlet。所有@Configuration (和其他...)类来自 getServletConfigClasses() .

作为Servlet生命周期的一部分,容器首先会初始化所有ServletContextListener对象。这意味着 ContextLoaderListener将首先加载 refresh AnnotationConfigWebApplicationContext那是给它的(如果它还没有被刷新,理想情况下它不应该被刷新)。它还会把这个 ApplicationContext作为 ServletContext 中的一个属性.

然后容器会初始化注册的DispatcherServlet .这里有一些更多的阅读

基本上,DispatcherServletrefresh ApplicationConfigWebApplicationContext它通过首先将其父级设置为 ApplicationContext 来接收在ServletContext (由 ContextLoaderListener 设置),如果有的话。

然后它将开始从其 ApplicationContext 中挑选 bean 设置 MVC 堆栈、 Controller 、处理程序方法、拦截器等。默认情况下,它只会查找其处理程序 bean,@Controller bean ,在ApplicationContext它加载了,而不是它的父级。


你好像做了什么

@Override
protected Class<?>[] getServletConfigClasses() { return new Class[] { WebMvcConfig.class }; }

@Override
protected Class<?>[] getRootConfigClasses() { return new Class[] { RootConfig.class }; }

在这种情况下,ContextLoaderListener将加载 RootConfig这将创建一堆 beans,包括你的 @Controller @Transactional 将建议的类(class)配置。

DispatcherServlet然后将加载 WebMvcConfig它有自己的 @ComponentScan这将创建新的 @Controller bean ,但这些不会被建议,因为没有 TransactionInterceptor已注册(在此上下文中没有 @EnableTransactionManagement)。 DispatcherServlet然后将尝试找到所有 @Controller bean(和其他有 @RequestMapping 方法的 bean)在它自己的 ApplicationContext 中.它会找到这些 @Controller不建议使用的 bean 类。这些是它将注册为处理程序的那些,而不是由 ContextLoaderListener 加载的那些.

如果您进一步查看日志,您应该会看到正在创建一个新的 Controller bean。


建议:

  • 根上下文:应该对整个应用可见的东西
  • Servlet 上下文:应该对 MVC 堆栈可见的东西

Controller 不是整个应用程序都应该访问的组件。只有 DispatcherServlet应该关心他们。将它们放在 servlet 上下文中。

现在我显然不了解您的整个应用程序,但我建议您将所有事务逻辑从处理程序方法中重构到一些 @Service 中。方法。它将使您更容易维护您的配置并使您的 Controller 更像 Controller ,即。委托(delegate)给模型。

关于java - 即使在日志中看到 "adding transactional method",事务顾问也不会拦截方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24367503/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com