복붙노트

[SPRING] 스프링 부트 JAX-WS 웹 서비스를 bean으로 등록

SPRING

스프링 부트 JAX-WS 웹 서비스를 bean으로 등록

내 봄 부팅 ws 기반 응용 프로그램에서 나는 jax-ws webservice 계약 첫 번째 접근법을 만들었습니다. 웹 서비스가 작동하지만 웹 서비스 내 다른 콩을 autowire 수 없습니다.

어떻게 스프링으로 웹 서비스를 bean으로 정의 할 수 있습니까?

다음은 내 webservice impl 클래스입니다.

@WebService(endpointInterface = "com.foo.bar.MyServicePortType")
@Service
public class MySoapService implements MyServicePortType {

@Autowired
private MyBean obj;


public Res method(final Req request) {
    System.out.println("\n\n\nCALLING.......\n\n" + obj.toString()); //obj is null here
    return new Res();
}
}

내 서비스 포트 유형이 wsdl 파일의 maven에 의해 생성됩니다.

SoapUi를 통해이 서비스를 호출하면 MyBean 객체가 자동 실행되지 않으므로 NullPointerException이 발생합니다.

내 응용 프로그램은 스프링 부팅을 기반으로하므로 xml 파일은 없습니다. 현재 엔드 포인트 구성을 가진 sun-jaxws.xml 파일이 있습니다. 스프링 부트 응용 프로그램에서 다음 구성을 수행하려면 어떻게해야합니까?

    <wss:binding url="/hello">
    <wss:service>
        <ws:service bean="#helloWs"/>
    </wss:service>
    </wss:binding>

다음은 SpringBootServletInitializer 클래스입니다.

@Configuration
public class WebXml extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
    return application.sources(WSApplication.class);
}

@Bean
public ServletRegistrationBean jaxws() {
    final ServletRegistrationBean jaxws = new ServletRegistrationBean(new WSServlet(), "/jaxws");
    return jaxws;
}

@Override
public void onStartup(final ServletContext servletContext) throws ServletException {
    super.onStartup(servletContext);
    servletContext.addListener(new WSServletContextListener());
}
}

감사

해결법

  1. ==============================

    1.SpringBeanAutowiringSupport를 확장하는 것은 JAX-WS 엔드 포인트 클래스에 삽입 된 콩을 현재의 스프링 루트 웹 애플리케이션 컨텍스트에서 가져 오는 데 권장되는 방법이다. 그러나 이것은 서블릿 컨텍스트 초기화에서 약간 다르기 때문에 스프링 부트에서는 작동하지 않습니다.

    SpringBeanAutowiringSupport를 확장하는 것은 JAX-WS 엔드 포인트 클래스에 삽입 된 콩을 현재의 스프링 루트 웹 애플리케이션 컨텍스트에서 가져 오는 데 권장되는 방법이다. 그러나 이것은 서블릿 컨텍스트 초기화에서 약간 다르기 때문에 스프링 부트에서는 작동하지 않습니다.

    SpringBootServletInitializer.startup ()은 사용자 정의 ContextLoaderListener를 사용하며 작성된 응용 프로그램 컨텍스트를 ContextLoader에 전달하지 않습니다. 나중에 JAX-WS 엔드 포인트 클래스의 객체가 초기화 될 때 SpringBeanAutowiringSupport는 ContextLoader에 의존하여 현재 애플리케이션 컨텍스트를 검색하고 항상 null을 가져온다.

    public abstract class SpringBootServletInitializer implements WebApplicationInitializer {
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            WebApplicationContext rootAppContext = createRootApplicationContext(
                    servletContext);
            if (rootAppContext != null) {
                servletContext.addListener(new ContextLoaderListener(rootAppContext) {
                    @Override
                    public void contextInitialized(ServletContextEvent event) {
                        // no-op because the application context is already initialized
                    }
                });
            }
            ......
        }
    }
    

    org.springframework.boot.context.embedded.ServletContextInitializer를 구현 한 bean을 등록하여 startup () 중에 애플리케이션 컨텍스트를 검색 할 수있다.

    @Configuration
    public class WebApplicationContextLocator implements ServletContextInitializer {
    
        private static WebApplicationContext webApplicationContext;
    
        public static WebApplicationContext getCurrentWebApplicationContext() {
            return webApplicationContext;
        }
    
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        }
    }
    

    그런 다음 JAX-WS 엔드 포인트 클래스에 자체 자동 와이어 링을 구현할 수 있습니다.

    @WebService
    public class ServiceImpl implements ServicePortType {
    
        @Autowired
        private FooBean bean;
    
        public ServiceImpl() {
            AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
            WebApplicationContext currentContext = WebApplicationContextLocator.getCurrentWebApplicationContext();
            bpp.setBeanFactory(currentContext.getAutowireCapableBeanFactory());
            bpp.processInjection(this);
        }
    
        // alternative constructor to facilitate unit testing.
        protected ServiceImpl(ApplicationContext context) {
            AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
            bpp.setBeanFactory(new DefaultListableBeanFactory(context));
            bpp.processInjection(this);
        }
    }
    

    유닛 테스트에서 현재 스프링 애플리케이션 컨텍스트를 주입하고 대체 생성자를 호출 할 수 있습니다.

    @Autowired 
    private ApplicationContext context;
    
    private ServicePortType service;
    
    @Before
    public void setup() {
        this.service = new ServiceImpl(this.context);
    }
    
  2. ==============================

    2.SpringBootServletInitializer에서 Configuration을 확장하거나 configure () 또는 onStartup () 메소드를 재정의하지 않아도됩니다. 그리고 WebApplicationInitializer를 구현하는 무언가를 만들어야합니다. 몇 단계 만 수행하면됩니다 (@Configuration 클래스의 모든 단계를 수행 할 수도 있습니다. @SpringBootApplication 클래스는 @ComponentScan 등을 통해 알 수 있습니다).

    SpringBootServletInitializer에서 Configuration을 확장하거나 configure () 또는 onStartup () 메소드를 재정의하지 않아도됩니다. 그리고 WebApplicationInitializer를 구현하는 무언가를 만들어야합니다. 몇 단계 만 수행하면됩니다 (@Configuration 클래스의 모든 단계를 수행 할 수도 있습니다. @SpringBootApplication 클래스는 @ComponentScan 등을 통해 알 수 있습니다).

    끝난.

    @SpringBootApplication
    public class SimpleBootCxfApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SimpleBootCxfApplication.class, args);
        }
    
        @Bean
        public ServletRegistrationBean dispatcherServlet() {
            return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
        }
    
        @Bean(name = Bus.DEFAULT_BUS_ID)
        public SpringBus springBus() {
            return new SpringBus();
        }    
    
        @Bean
        public WeatherService weatherService() {
            return new WeatherServiceEndpoint();
        }
    
        @Bean
        public Endpoint endpoint() {
            EndpointImpl endpoint = new EndpointImpl(springBus(), weatherService());
            endpoint.publish("/WeatherSoapService");
            return endpoint;
        }
    }
    
  3. ==============================

    3.기본적으로 Spring은 JAX-WS 엔드 포인트에 대해 아무것도 모릅니다. Spring이 아닌 JAX-WS 런타임에 의해 관리됩니다. SpringBeanAutowiringSupport를 사용하면이 문제를 극복 할 수 있습니다. 일반적으로 하위 클래스로 간단히 처리 할 수 ​​있습니다.

    기본적으로 Spring은 JAX-WS 엔드 포인트에 대해 아무것도 모릅니다. Spring이 아닌 JAX-WS 런타임에 의해 관리됩니다. SpringBeanAutowiringSupport를 사용하면이 문제를 극복 할 수 있습니다. 일반적으로 하위 클래스로 간단히 처리 할 수 ​​있습니다.

    public class MySoapService extends SpringBeanAutowiringSupport implements MyServicePortType {
        …
    }
    

    @PostConstruct로 주석 된 메소드에서 직접 호출 할 수도 있습니다.

    public class MySoapService implements MyServicePortType {
    
        @PostConstruct
        public void init() {
            SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        }
    }
    
  4. ==============================

    4.SpringBeanAutoWiringSupport를 사용하여 @Andy Wilkinson의 단서를 얻는 것은

    SpringBeanAutoWiringSupport를 사용하여 @Andy Wilkinson의 단서를 얻는 것은

    public class MySoapService extends SpringBeanAutowiringSupport implements MyServicePortType {
        …
    }
    

    @PostConstruct로 주석 된 메소드에서 직접 호출 할 수도 있습니다.

    @Service
    public class MySoapService implements MyServicePortType {
    
        @Autowired
        ServletContext servletContext;
    
        @PostConstruct
        public void init() {
            SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
                servletContext);
        }
    }
    
  5. from https://stackoverflow.com/questions/25177491/spring-boot-register-jax-ws-webservice-as-bean by cc-by-sa and MIT license