Spring Boot将JAX-WS webservice注册为bean

时间:2014-08-07 08:20:12

标签: java spring web-services spring-boot

在我的spring boot基于ws的应用程序中,我根据合同第一种方法创建了一个jax-ws webservice。 Web服务已启动,但我无法在我的Web服务中自动装配其他bean。

我怎样才能将我的webservice定义为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();
}
}

MyServicePortType由来自wsdl文件的maven生成

当我调用此服务时(通过SoapUi),它会给出NullPointerException,因为MyBean对象没有自动装配。

由于我的应用程序是基于Spring启动的,因此没有xml文件。目前我有sun-jaxws.xml文件和端点配置。如何在Spring启动应用程序中执行以下配置

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

感谢

4 个答案:

答案 0 :(得分:4)

扩展SpringBeanAutowiringSupport是从当前Spring根Web应用程序上下文为JAX-WS端点类注入bean的推荐方法。但是这不适用于 spring boot ,因为它在servlet context initialization上有点不同。

问题

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

单元测试

在单元测试中,您可以获取当前的spring应用程序上下文,并使用它调用替代构造函数。

@Autowired 
private ApplicationContext context;

private ServicePortType service;

@Before
public void setup() {
    this.service = new ServiceImpl(this.context);
}

答案 1 :(得分:2)

您不必从SpringBootServletInitializer扩展配置,也不必覆盖configure()或onStartup()方法。并且绝不必构建实现WebApplicationInitializer的东西。只需要做很少的步骤(你也可以在单独的@Configuration类中完成所有步骤,只有@SpringBootApplication的类需要知道这个步骤 - 例如通过@ComponentScan)。

  1. 在ServletRegistrationBean中注册CXFServlet
  2. 实例化SpringBus
  3. 实例化JAX-WS SEI(服务接口)
  4. 的实现
  5. 使用实现Beans的SpringBus和SEI实例化EndpointImpl
  6. 在该EndpointImpl上调用publish()方法
  7. 完成。

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

答案 2 :(得分:1)

默认情况下,Spring对JAX-WS端点一无所知 - 它们由JAX-WS运行时而不是Spring管理。您可以通过使用SpringBeanAutowiringSupport来解决这个问题。您通常只需通过子类化来完成此操作:

public class MySoapService extends SpringBeanAutowiringSupport implements MyServicePortType {
    …
}

您还可以选择直接从使用@PostConstruct注释的方法中调用它:

public class MySoapService implements MyServicePortType {

    @PostConstruct
    public void init() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    }
}

答案 3 :(得分:0)

使用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);
    }
}