Spring MVC 和 AOP:@Controllers 的@Pointcuts 仅适用于测试,不适用于生产
Spring MVC and AOP: @Pointcuts for @Controllers only works in Testing and not for Production
我正在 Web 环境中使用 Spring Framework 4.3.3:
我有两个上下文:
RootApplicationContext
ServletApplicationContext
我知道 ServletApplicationContext
包含所有关于 web 端的 beans,例如 @Controller
。此外,ServletApplicationContext
能够从 RootApplicationContext
访问所有上下文或 bean,例如 @Service, @Repository
等。直到这里我都很好。
注意 它也适用于 @Configuration
classes。 (基础设施)
因此根据前面的介绍,我们可以这样思考:
ServletApplicationContext
--> RootApplicationContext
需要考虑的重要一点是,逆运算不可能。
因此
RootApplicationContext
--> ServletApplicationContext
不可能。它有道理并且可以。服务器端不应该访问Web端
关于 AspectJ。我有以下内容:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
重要的一点:
- 这个
AopConfig
被RootApplicationContext
扫描了
- 我相信
ServletApplicationContext
可以通过 RootApplicationContext
的访问引用 @Configuration
好的,当我 运行 我的 @Test
方法。
当我从服务器端执行测试 class 时,我使用
@ContextConfiguration(classes={RootApplicationContext.class} )
- 只有
RootApplicationContext
而且 AOP 工作正常。我可以通过AOP + logging
确认以下过程:
@Service
-> @Repository
当我从 Web 端执行测试 class 时,我使用:
@ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
RootApplicationContext
和 ServletApplicationContext
而且 AOP 工作正常。我可以通过AOP + logging
确认以下过程:
@Controller
-> @Service
-> @Repository
现在用于生产我有:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootApplicationContext.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{ServletApplicationContext.class};
}
但是当我为 .war
文件导出项目并通过 URL/URI 执行 Controller
时,预期的行为或过程工作正常。但是关于 AOP 通过 AOP + logging
会发生以下过程:
@Service
-> @Repository
@Controller
不会出现在输出中。预期流程应该是:
@Controller
-> @Service
-> @Repository
那么为什么在测试而不是在生产中工作?
我已经做了研究,找到了这两个帖子:
- Spring AOP Advice on Annotated Controllers
- AspectJ does not work on Spring @Controller methods
实际上他们说 @Configuration
class 和 @EnableAspectJAutoProxy
应该通过 ServletApplicationContext
而不是 RootApplicationContext
扫描
即使它是真的(根据新实验),也应考虑在没有 Web 环境的情况下测试服务器端。
对于其他 @Bean
关于基础设施的 @Configuration
关系已经解释过 ServletApplicationContext --> RootApplicationContext
是如何工作的。刚用AOP就有这种情况。
问题 01:为什么会有这种行为?
问题 02:如何让 RootApplicationContext
扫描 AopConfig
并获得预期的生产行为?
注意 如果 AopConfig
被 ServletApplicationContext
扫描。以下关于服务器端@ContextConfiguration(classes={RootApplicationContext.class, AopConfig.class} )
的测试有效和强制。看到 AopConfig.class
的添加,但我认为 AopConfig
应该被 RootApplicationContext
扫描。
答案是@ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
测试环境中的上下文继承和生产环境中的上下文继承不是一回事。
在测试环境中,您将 RootApplicationContext
和 ServletApplicationContext
作为测试应用程序上下文的一部分。正如您在问题中所述,在生产中使用继承而不是简单的包含。
父上下文中的 BeanFactoryPostProcessor
(在您的情况下为 @EnableAspectJAutoProxy
)似乎未应用于子上下文。要使其在生产中工作,您也必须在子上下文中显式定义 @EnableAspectJAutoProxy
。
在这种情况下 Spring 上下文定义应该如下代码所示:
@Configuration
@Import(AopConfig.class)
public class RootApplicationContext {
...
}
@Configuration
@Import(AopConfig.class)
public class ServletApplicationContext {
...
}
或
@Configuration
@ComponentScan(basePackageClasses={AopConfig.Class, ...})
public class RootApplicationContext {
...
}
@Configuration
@ComponentScan(basePackageClasses={AopConfig.Class, ...})
public class ServletApplicationContext {
...
}
我正在 Web 环境中使用 Spring Framework 4.3.3:
我有两个上下文:
RootApplicationContext
ServletApplicationContext
我知道 ServletApplicationContext
包含所有关于 web 端的 beans,例如 @Controller
。此外,ServletApplicationContext
能够从 RootApplicationContext
访问所有上下文或 bean,例如 @Service, @Repository
等。直到这里我都很好。
注意 它也适用于 @Configuration
classes。 (基础设施)
因此根据前面的介绍,我们可以这样思考:
ServletApplicationContext
-->RootApplicationContext
需要考虑的重要一点是,逆运算不可能。
因此
RootApplicationContext
-->ServletApplicationContext
不可能。它有道理并且可以。服务器端不应该访问Web端
关于 AspectJ。我有以下内容:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
重要的一点:
- 这个
AopConfig
被RootApplicationContext
扫描了- 我相信
ServletApplicationContext
可以通过RootApplicationContext
的访问引用
@Configuration
- 我相信
好的,当我 运行 我的 @Test
方法。
当我从服务器端执行测试 class 时,我使用
@ContextConfiguration(classes={RootApplicationContext.class} )
- 只有
RootApplicationContext
- 只有
而且 AOP 工作正常。我可以通过AOP + logging
确认以下过程:
@Service
->@Repository
当我从 Web 端执行测试 class 时,我使用:
@ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
RootApplicationContext
和ServletApplicationContext
而且 AOP 工作正常。我可以通过AOP + logging
确认以下过程:
@Controller
->@Service
->@Repository
现在用于生产我有:
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootApplicationContext.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{ServletApplicationContext.class};
}
但是当我为 .war
文件导出项目并通过 URL/URI 执行 Controller
时,预期的行为或过程工作正常。但是关于 AOP 通过 AOP + logging
会发生以下过程:
@Service
->@Repository
@Controller
不会出现在输出中。预期流程应该是:
@Controller
->@Service
->@Repository
那么为什么在测试而不是在生产中工作?
我已经做了研究,找到了这两个帖子:
- Spring AOP Advice on Annotated Controllers
- AspectJ does not work on Spring @Controller methods
实际上他们说 @Configuration
class 和 @EnableAspectJAutoProxy
应该通过 ServletApplicationContext
而不是 RootApplicationContext
即使它是真的(根据新实验),也应考虑在没有 Web 环境的情况下测试服务器端。
对于其他 @Bean
关于基础设施的 @Configuration
关系已经解释过 ServletApplicationContext --> RootApplicationContext
是如何工作的。刚用AOP就有这种情况。
问题 01:为什么会有这种行为?
问题 02:如何让 RootApplicationContext
扫描 AopConfig
并获得预期的生产行为?
注意 如果 AopConfig
被 ServletApplicationContext
扫描。以下关于服务器端@ContextConfiguration(classes={RootApplicationContext.class, AopConfig.class} )
的测试有效和强制。看到 AopConfig.class
的添加,但我认为 AopConfig
应该被 RootApplicationContext
扫描。
答案是@ContextConfiguration(classes={RootApplicationContext.class, ServletApplicationContext.class})
测试环境中的上下文继承和生产环境中的上下文继承不是一回事。
在测试环境中,您将 RootApplicationContext
和 ServletApplicationContext
作为测试应用程序上下文的一部分。正如您在问题中所述,在生产中使用继承而不是简单的包含。
父上下文中的 BeanFactoryPostProcessor
(在您的情况下为 @EnableAspectJAutoProxy
)似乎未应用于子上下文。要使其在生产中工作,您也必须在子上下文中显式定义 @EnableAspectJAutoProxy
。
在这种情况下 Spring 上下文定义应该如下代码所示:
@Configuration
@Import(AopConfig.class)
public class RootApplicationContext {
...
}
@Configuration
@Import(AopConfig.class)
public class ServletApplicationContext {
...
}
或
@Configuration
@ComponentScan(basePackageClasses={AopConfig.Class, ...})
public class RootApplicationContext {
...
}
@Configuration
@ComponentScan(basePackageClasses={AopConfig.Class, ...})
public class ServletApplicationContext {
...
}