问题
I am working with Spring Framework 4.3.3 in a Web Environment:
I have two contexts:
RootApplicationContext
ServletApplicationContext
I know the ServletApplicationContext
contains all the beans about the web side, for example @Controller
. Furthermore ServletApplicationContext
is able to access all the context or beans from the RootApplicationContext
for example @Service, @Repository
etc. Until here I am fine.
Note it applies for @Configuration
classes too. (Infrastructure)
Therefore with the previous introduction we can think in the following way:
ServletApplicationContext
-->RootApplicationContext
Something important to take in consideration is that the inverse is not possible.
Therefore
RootApplicationContext
-->ServletApplicationContext
is not possible. It has sense and is Ok. Server side should not access the Web side
About AspectJ. I have the following:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
Here one important point:
- This
AopConfig
is scanned byRootApplicationContext
- I trust that
ServletApplicationContext
can refer that@Configuration
through the access ofRootApplicationContext
- I trust that
Ok, when I run my @Test
methods.
When I execute a Test class from the server side I use
@ContextConfiguration(classes={RootApplicationContext.class} )
- Only
RootApplicationContext
- Only
And AOP works fine. I can confirm through AOP + logging
the following process:
@Service
->@Repository
When I execute a Test class from the Web side I use:
@ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
RootApplicationContext
andServletApplicationContext
And AOP works fine. I can confirm through AOP + logging
the following process:
@Controller
->@Service
->@Repository
Now for production I have:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootApplicationContext.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{ServletApplicationContext.class};
}
But when I export the project for a .war
file and through an URL/URI a Controller
is executed the expected behaviour or process works fine. But about AOP through AOP + logging
the following process happens:
@Service
->@Repository
@Controller
does not appear for the output. The expected flow process should be:
@Controller
->@Service
->@Repository
So why works in Testing and not in production?
I already did a research and I have found these two posts:
- Spring AOP Advice on Annotated Controllers
- AspectJ does not work on Spring @Controller methods
Practically they say that the @Configuration
class with @EnableAspectJAutoProxy
should be scanned through ServletApplicationContext
and not through RootApplicationContext
Even when it is true (according with new experiments), consider that Server side should be tested without a Web Environment.
For other @Bean
s about infrastructure through @Configuration
the relation already explained about ServletApplicationContext --> RootApplicationContext
works how is expected. Just with AOP has this situation.
Question 01: So why this behaviour?
Question 02: How keep the AopConfig
scanned by RootApplicationContext
and get the expected behaviour for production?
Note if AopConfig
is scanned by ServletApplicationContext
. The following about testing is valid and mandatory for the server side @ContextConfiguration(classes={RootApplicationContext.class, AopConfig.class} )
. See the addition of AopConfig.class
but I think AopConfig
should be scanned by RootApplicationContext
.
回答1:
The answer is that @ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
in test environment and context inheritance in production is not the same things.
In test environment you include RootApplicationContext
and ServletApplicationContext
as parts of your test application context. In production inheritance is used instead of a simple inclusion, as you described in your question.
Seems that BeanFactoryPostProcessor
(which is @EnableAspectJAutoProxy
in your case) from parent context not applied to child contexts. To make it work in production you must explicitly define @EnableAspectJAutoProxy
in child context too.
In this case Spring context definition should be about as code below:
@Configuration
@Import(AopConfig.class)
public class RootApplicationContext {
...
}
@Configuration
@Import(AopConfig.class)
public class ServletApplicationContext {
...
}
Or
@Configuration
@ComponentScan(basePackageClasses={AopConfig.Class, ...})
public class RootApplicationContext {
...
}
@Configuration
@ComponentScan(basePackageClasses={AopConfig.Class, ...})
public class ServletApplicationContext {
...
}
Related Task
来源:https://stackoverflow.com/questions/39694633/spring-mvc-and-aop-pointcuts-for-controllers-only-works-in-testing-and-not-fo