Method not being intercepted by transaction advisor even though “adding transactional method” seen in logs

南楼画角 提交于 2019-12-05 11:48:01
Sotirios Delimanolis

If you haven't already read them

The same applies to AbstractAnnotationConfigDispatcherServletInitializer's getRootConfigClasses() and getServletConfigClasses(). Basically that WebApplicationInitializer will construct (and register) a ContextLoaderListener with a AnnotationConfigWebApplicationContext registering all the @Configuration (and other @Component annotated) classes from getRootConfigClasses(). It will then construct and register a DispatcherServlet with all the @Configuration (and other...) classes from the getServletConfigClasses().

As part of the Servlet lifecycle, the container will first initialize all ServletContextListener objects. This means ContextLoaderListener will load first and refresh the AnnotationConfigWebApplicationContext that was given to it (if it wasn't already refreshed, which ideally it shouldn't be). It will also put this ApplicationContext as an attribute in the ServletContext.

The container will then initialize the registered DispatcherServlet. Here's some more reading

Basically, the DispatcherServlet will refresh the ApplicationConfigWebApplicationContext that it received by first setting its parent to the ApplicationContext in the ServletContext (set by the ContextLoaderListener), if there is one.

It will then start picking and choosing beans from its ApplicationContext to set up the MVC stack, controllers, handler methods, interceptors, etc. By default, it will only look up its handler beans, @Controller beans, in the ApplicationContext it loaded, not its parent(s).


What you seem to have done is

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

and

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

In this case, the ContextLoaderListener will load RootConfig which will create a bunch of beans, including ones for your @Controller classes which will be advised with the @Transactional configuration.

The DispatcherServlet will then load WebMvcConfig which has its own @ComponentScan and this will create new @Controller beans, but these won't be advised because no TransactionInterceptor was registered (no @EnableTransactionManagement in this context). The DispatcherServlet will then try to find all @Controller beans (and other beans that have @RequestMapping methods) in its own ApplicationContext. It will find these @Controller beans which aren't advised. Those are the ones it will register as handlers, not the ones loaded by the ContextLoaderListener.

If you look further down in your logs, you should see a new controller bean(s) being created.


Suggestions:

  • Root context: things that should be visible to the entire application
  • Servlet context: things that should be visible to the MVC stack

Controllers are not components that the whole application should have access to. Only the DispatcherServlet should care about them. Put them in the servlet context.

Now I obviously don't know your whole application, but I recommend you refactor all the transactional logic out of the handler methods and into some @Service methods. It will make it easier to maintain your configs and make your controllers more controller-y, ie. delegate to the model.

You are doing something wrong: both RootConfig and WebMvcConfig are in the same package. RootConfig does component scanning in its own package, discovers WebMvcConfig which in turn does component scanning. In the end the root application context will contain all transactional related stuff (txManager, datasource, sessionfactorybean etc) but, also, everything web related: controllers, handlermappings etc.

Then, WebMvcConfig kicks in (because it's defined in WebApplicationInitializer) and all web-related stuff is re-defined again. And I think it's happening the way it does, because the root context has one version of your controller (the transactional one) and the servlet context has another version (the simple one).

I think you need to keep your RootConfig and WebMvcConfig in separate packages.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!