Spring Boot register JAX-WS webservice as bean

后端 未结 4 1786
天命终不由人
天命终不由人 2021-01-03 01:47

In my spring boot ws based application I have created a jax-ws webservice following a contract first approach. The Web service is up but I cannot autowire my other beans ins

相关标签:
4条回答
  • 2021-01-03 02:04

    By default Spring doesn't know anything about your JAX-WS endpoints – they're managed by the JAX-WS runtime rather than Spring. You can overcome this by using SpringBeanAutowiringSupport You'd typically do so simply by subclassing it:

    public class MySoapService extends SpringBeanAutowiringSupport implements MyServicePortType {
        …
    }
    

    You also have the option of calling it directly from a method annotated with @PostConstruct:

    public class MySoapService implements MyServicePortType {
    
        @PostConstruct
        public void init() {
            SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        }
    }
    
    0 讨论(0)
  • 2021-01-03 02:10

    Extending SpringBeanAutowiringSupport is the recommended way to get beans injected for JAX-WS endpoint class, from current spring root web application context. However this does not work with spring boot as it's a bit different on servlet context initialization.

    Problem

    SpringBootServletInitializer.startup() uses a custom ContextLoaderListener and does not pass the created application context to ContextLoader. Later when the object of JAX-WS endpoint class being initialized, SpringBeanAutowiringSupport depends on ContextLoader to retrieve the current application context, and always get 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
                    }
                });
            }
            ......
        }
    }
    

    Workaround

    You could register a bean that implements org.springframework.boot.context.embedded.ServletContextInitializer to retrieve the application context during 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);
        }
    }
    

    Then you could implement self-autowiring in your JAX-WS endpoint class.

    @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);
        }
    }
    

    Unit Testing

    In unit tests you could get current spring application context injected, and call alternative constructor with it.

    @Autowired 
    private ApplicationContext context;
    
    private ServicePortType service;
    
    @Before
    public void setup() {
        this.service = new ServiceImpl(this.context);
    }
    
    0 讨论(0)
  • 2021-01-03 02:13

    You don´t have to extend your Configuration from SpringBootServletInitializer, nor override configure() or onStartup() methods. And in no way you have to build something implementing WebApplicationInitializer. There are only few steps to do (you could also do all the steps in a seperate @Configuration-class, the class with @SpringBootApplication only needs to know, where this one is - e.g. via @ComponentScan).

    1. Register the CXFServlet in an ServletRegistrationBean
    2. Instantiate a SpringBus
    3. Instantiate your Implementation of the JAX-WS SEI (Service Interface)
    4. Instantiate a EndpointImpl with the SpringBus and SEI implementing Beans
    5. Call publish() method on that EndpointImpl

    Done.

    @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;
        }
    }
    
    0 讨论(0)
  • 2021-01-03 02:14

    Taking clue from @Andy Wilkinson using SpringBeanAutoWiringSupport is achieved by using

    public class MySoapService extends SpringBeanAutowiringSupport implements MyServicePortType {
        …
    }
    

    You also have the option of calling it directly from a method annotated with @PostConstruct:

    @Service
    public class MySoapService implements MyServicePortType {
    
        @Autowired
        ServletContext servletContext;
    
        @PostConstruct
        public void init() {
            SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
                servletContext);
        }
    }
    
    0 讨论(0)
提交回复
热议问题