[SPRING] @EnableWs가 Spring 빈에서 aop 프록시를 제거한 이유
SPRING@EnableWs가 Spring 빈에서 aop 프록시를 제거한 이유
스프링 부트 웹 서비스 프로젝트에 사용자 정의 인터셉터를 추가하려고합니다. 나는이 예제를 따르고이 설정을 만들었다.
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 메소드에 새로운 인터셉터를 추가 할 때 config 클래스의 remove aop proxy에 문제가 있습니다. 어떤 생각? 전체 프로젝트는 자식이다.
해결법
-
==============================
1.문제는 Spring의 초기화 순서이다. 기술적으로, WS 엔드 포인트 (Spring-ws의 AnnotationActionEndpointMapping)를위한 BeanPostProcessor가 있기 때문에, 이것은 모든 의존성 (특히 EndpointInterceptor beans)의 초기 초기화를 강요합니다.
문제는 Spring의 초기화 순서이다. 기술적으로, WS 엔드 포인트 (Spring-ws의 AnnotationActionEndpointMapping)를위한 BeanPostProcessor가 있기 때문에, 이것은 모든 의존성 (특히 EndpointInterceptor beans)의 초기 초기화를 강요합니다.
이를 막기위한 한 가지 방법은 BeanPostProcessor를 재배치하거나, 심지어 자신의 하나를 생성하는 것입니다. 그러나 초기화 시퀀스의 다른 곳에서도 비슷한 놀라움을 피하기 위해 Spring의 기본 구성을 유지하는 것이 더 간단합니다.
문제를 피하는 간단한 방법은 EndpointInterceptor 빈에서 ObjectFactory를 사용하는 것입니다. 이것은 참조 될 때까지 AppConfig 빈을 인스턴스화하는 것을 지연시킬 것이며, 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에서 주입 된 (자동 와이어 된) 빈으로 참조되어야 함을 의미합니다.
예를 들어 주셔서 감사합니다. 여기에 ObjectFactory 기술을 사용하는 포크가 있습니다. 이것은 WsConfig.testAop (), CountryEndpoint 및 CustomValidatingInterceptor 모두에서 Aop 프록시로서 config bean을 보여 주었고, SoapUI에서 요청을 보냈습니다.
-
==============================
2.이 문제를 해결할 또 다른 가능성이 있습니다. 스택 오버플로 문제는 다음과 관련이 있습니다. 삽입 된 DAO의 @Transactional이 작동하지 않는 Spring WS 인터셉터. 요컨대,이 문제는이 방법이
이 문제를 해결할 또 다른 가능성이 있습니다. 스택 오버플로 문제는 다음과 관련이 있습니다. 삽입 된 DAO의 @Transactional이 작동하지 않는 Spring WS 인터셉터. 요컨대,이 문제는이 방법이
@Override public void addInterceptors(List<EndpointInterceptor> interceptors) {
Spring 의존성 주입 전에 Spring AOP beans를 등록 할 시간이 있기 전에 호출된다. 제 경우에는 @Transactional이 Spring-WS에 의해 무시되었지만 그것은 아무것도 될 수 있습니다.
다행스럽게도 Spring-WS는 변경 불가능한 컬렉션 대신 변경 가능한 컬렉션을 사용합니다. addInterceptors () 메소드가 실행될 때 콜렉션을 저장할 수 있기 때문에 Spring-WS가 사용하는 콜렉션 인스턴스에 대한 참조가있다. 나중에 당신은 인터셉터 빈을 적절하게 초기화하고 그것을 콜렉션에 추가 할 수 있습니다.
또한 @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 lifecycle 전문가가 아니므로 postProcessBeforeInitialization ()보다 인터셉터 초기화의 위치를 정할 수있는 좋은 곳이있을 수 있습니다. 즉, 이것은 나를 위해 작동합니다.
from https://stackoverflow.com/questions/50072312/why-enablews-removed-aop-proxy-from-spring-bean by cc-by-sa and MIT license