为什么@EnableWs 从 spring bean 中删除了 aop 代理
Why @EnableWs removed aop proxy from spring bean
我正在尝试在我的 spring 引导 Web 服务项目中添加自定义拦截器。我按照 this 示例创建了这个配置:
package org.example;
import java.util.List;
import org.aspect.PersistentAspect;
import org.springframework.aop.support.AopUtils;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
@EnableWs
@Configuration
public class WsConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
final MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/v1/*");
}
@Bean
public XsdSchema schema() {
return new SimpleXsdSchema(new ClassPathResource("country.xsd"));
}
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
String[] jaxbContext = new String[] { "io.spring.guides.gs_producing_web_service" };
marshaller.setContextPaths(jaxbContext);
return marshaller;
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
// aop not working
interceptors.add(new CustomValidatingInterceptor(schema(), config()));
// aop working
// interceptors.add(new CustomValidatingInterceptor(schema(), null));
}
@Bean
public AppConfig config() {
return new AppConfig();
}
@Bean
public PersistentAspect persistentAspect() {
PersistentAspect persistentAspect = new PersistentAspect();
return persistentAspect;
}
@Bean
public Object testAop() {
System.out.println("is config aop proxy: " + AopUtils.isAopProxy(config()));
return null;
}
}
然而,当我在 addInterceptors
方法中添加新的拦截器时,我在配置 class 中删除了 aop 代理时遇到问题。知道为什么吗?整个项目在 git.
问题出在 Spring 中的初始化顺序。从技术上讲,因为 WS 端点有一个 BeanPostProcessor
(spring-ws 中的 AnnotationActionEndpointMapping
),它将强制提前初始化所需的任何依赖项 - 特别是任何 EndpointInterceptor
豆子。
解决这个问题的一种方法是重新排列 BeanPostProcessor,甚至创建一个您自己的,但通常更简单的做法是保留 Spring 中的默认配置 - 以避免在初始化序列的其他地方出现类似的意外.
也许避免该问题的更简单方法是在 EndpointInterceptor
bean 中使用 ObjectFactory
。这将延迟 AppConfig
bean 的实例化,直到它被引用,到那时 Aop 编织也将发生。
@Component
public class CustomValidatingInterceptor extends PayloadValidatingInterceptor {
@Autowired
private ObjectFactory<AppConfig> konfigurace;
@Override
public boolean handleRequest(MessageContext messageContext, Object endpoint)
throws IOException, SAXException, TransformerException {
System.out.println("is config aop proxy in interceptor: " +
AopUtils.isAopProxy(konfigurace.getObject()));
return super.handleRequest(messageContext, endpoint);
}
显然,这意味着 CustomValidatingInterceptor
必须从 WsConfig
引用为注入(自动装配)bean。
感谢您提供示例 - 有一个使用 ObjectFactory
技术的分支 here。当我从 SoapUI 发送请求时,config
bean 在 WsConfig.testAop()
、CountryEndpoint
和 CustomValidatingInterceptor
中显示为 Aop 代理。
这是解决此问题的另一种可能性。这涉及以下堆栈溢出问题:Spring WS interceptors with injected DAO's @Transactional not working。简而言之,问题来自于方法
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
在 Spring 依赖注入有时间注册 Spring AOP bean 之前被调用。在我的例子中,Spring-WS 忽略了 @Transactional,但它可以是任何东西。
对我们来说幸运的是 Spring-WS 使用可变集合而不是不可变集合。当 addInterceptors()
方法
被调用,我们可以只保存集合,因此我们有一个对 Spring-WS 使用的同一个集合实例的引用。稍后您可以正确初始化拦截器 bean 并将其添加到集合中。
您还必须回避这样一个事实:如果您使用 @Autowired
,bean 在注释发生之前就已经准备好了。因此,您必须通过调用 ApplicationContext.getBean()
方法手动创建它。
@EnableWs
@Configuration
// The magic is to implement both ApplicationContextAware
// that injects the applicationContext for us
// and BeanPostProcessor that gives us postProcessBeforeInitialization()
// where we initialize our interceptor correctly
// and add it to the collection
public class WebServiceConfig extends WsConfigurerAdapter implements ApplicationContextAware, BeanPostProcessor {
// This is the interceptor that uses dependencies with @Transactional annotation.
// It will not work with @Autowired
private MyInterceptorThatHasTransactionalDependencies myInterceptorThatHasTransactionalDependencies;
// Fortunately Spring WS uses mutable collections so we can fill
// this list later on as long as we just initialize it with
private List<EndpointInterceptor> interceptors;
// This is our application context where all the beans are defined
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// save application context for later use
this.context = applicationContext;
}
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// This method gets called multiple times so initialize interceptor just once
if(myInterceptorThatHasTransactionalDependencies == null){
myInterceptorThatHasTransactionalDependencies = context.getBean(MyInterceptorThatHasTransactionalDependencies.class);
interceptors.add(myInterceptorThatHasTransactionalDependencies);
}
return bean;
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
// Save the list of interceptors so we can modify it later on
this.interceptors = interceptors;
if (myInterceptorThatHasTransactionalDependencies == null) {
System.out.println("myInterceptorThatHasTransactionalDependencies was null like we expected");
} else {
interceptors.add(myInterceptorThatHasTransactionalDependencies);
}
}
}
只是想让你知道我不是 Spring bean 生命周期专家,所以可能有比 postProcessBeforeInitialization()
更好的地方来放置拦截器初始化。也就是说,这对我有用。
我正在尝试在我的 spring 引导 Web 服务项目中添加自定义拦截器。我按照 this 示例创建了这个配置:
package org.example;
import java.util.List;
import org.aspect.PersistentAspect;
import org.springframework.aop.support.AopUtils;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
@EnableWs
@Configuration
public class WsConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
final MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/v1/*");
}
@Bean
public XsdSchema schema() {
return new SimpleXsdSchema(new ClassPathResource("country.xsd"));
}
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
String[] jaxbContext = new String[] { "io.spring.guides.gs_producing_web_service" };
marshaller.setContextPaths(jaxbContext);
return marshaller;
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
// aop not working
interceptors.add(new CustomValidatingInterceptor(schema(), config()));
// aop working
// interceptors.add(new CustomValidatingInterceptor(schema(), null));
}
@Bean
public AppConfig config() {
return new AppConfig();
}
@Bean
public PersistentAspect persistentAspect() {
PersistentAspect persistentAspect = new PersistentAspect();
return persistentAspect;
}
@Bean
public Object testAop() {
System.out.println("is config aop proxy: " + AopUtils.isAopProxy(config()));
return null;
}
}
然而,当我在 addInterceptors
方法中添加新的拦截器时,我在配置 class 中删除了 aop 代理时遇到问题。知道为什么吗?整个项目在 git.
问题出在 Spring 中的初始化顺序。从技术上讲,因为 WS 端点有一个 BeanPostProcessor
(spring-ws 中的 AnnotationActionEndpointMapping
),它将强制提前初始化所需的任何依赖项 - 特别是任何 EndpointInterceptor
豆子。
解决这个问题的一种方法是重新排列 BeanPostProcessor,甚至创建一个您自己的,但通常更简单的做法是保留 Spring 中的默认配置 - 以避免在初始化序列的其他地方出现类似的意外.
也许避免该问题的更简单方法是在 EndpointInterceptor
bean 中使用 ObjectFactory
。这将延迟 AppConfig
bean 的实例化,直到它被引用,到那时 Aop 编织也将发生。
@Component
public class CustomValidatingInterceptor extends PayloadValidatingInterceptor {
@Autowired
private ObjectFactory<AppConfig> konfigurace;
@Override
public boolean handleRequest(MessageContext messageContext, Object endpoint)
throws IOException, SAXException, TransformerException {
System.out.println("is config aop proxy in interceptor: " +
AopUtils.isAopProxy(konfigurace.getObject()));
return super.handleRequest(messageContext, endpoint);
}
显然,这意味着 CustomValidatingInterceptor
必须从 WsConfig
引用为注入(自动装配)bean。
感谢您提供示例 - 有一个使用 ObjectFactory
技术的分支 here。当我从 SoapUI 发送请求时,config
bean 在 WsConfig.testAop()
、CountryEndpoint
和 CustomValidatingInterceptor
中显示为 Aop 代理。
这是解决此问题的另一种可能性。这涉及以下堆栈溢出问题:Spring WS interceptors with injected DAO's @Transactional not working。简而言之,问题来自于方法
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
在 Spring 依赖注入有时间注册 Spring AOP bean 之前被调用。在我的例子中,Spring-WS 忽略了 @Transactional,但它可以是任何东西。
对我们来说幸运的是 Spring-WS 使用可变集合而不是不可变集合。当 addInterceptors()
方法
被调用,我们可以只保存集合,因此我们有一个对 Spring-WS 使用的同一个集合实例的引用。稍后您可以正确初始化拦截器 bean 并将其添加到集合中。
您还必须回避这样一个事实:如果您使用 @Autowired
,bean 在注释发生之前就已经准备好了。因此,您必须通过调用 ApplicationContext.getBean()
方法手动创建它。
@EnableWs
@Configuration
// The magic is to implement both ApplicationContextAware
// that injects the applicationContext for us
// and BeanPostProcessor that gives us postProcessBeforeInitialization()
// where we initialize our interceptor correctly
// and add it to the collection
public class WebServiceConfig extends WsConfigurerAdapter implements ApplicationContextAware, BeanPostProcessor {
// This is the interceptor that uses dependencies with @Transactional annotation.
// It will not work with @Autowired
private MyInterceptorThatHasTransactionalDependencies myInterceptorThatHasTransactionalDependencies;
// Fortunately Spring WS uses mutable collections so we can fill
// this list later on as long as we just initialize it with
private List<EndpointInterceptor> interceptors;
// This is our application context where all the beans are defined
private ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// save application context for later use
this.context = applicationContext;
}
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// This method gets called multiple times so initialize interceptor just once
if(myInterceptorThatHasTransactionalDependencies == null){
myInterceptorThatHasTransactionalDependencies = context.getBean(MyInterceptorThatHasTransactionalDependencies.class);
interceptors.add(myInterceptorThatHasTransactionalDependencies);
}
return bean;
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
// Save the list of interceptors so we can modify it later on
this.interceptors = interceptors;
if (myInterceptorThatHasTransactionalDependencies == null) {
System.out.println("myInterceptorThatHasTransactionalDependencies was null like we expected");
} else {
interceptors.add(myInterceptorThatHasTransactionalDependencies);
}
}
}
只是想让你知道我不是 Spring bean 生命周期专家,所以可能有比 postProcessBeforeInitialization()
更好的地方来放置拦截器初始化。也就是说,这对我有用。