java Spring Boot 将 JAX-WS 网络服务注册为 bean

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25177491/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 07:30:53  来源:igfitidea点击:

Spring Boot register JAX-WS webservice as bean

javaspringweb-servicesspring-boot

提问by amique

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 inside my webservice.

在我的基于 spring boot ws 的应用程序中,我按照契约优先的方法创建了一个 jax-ws 网络服务。Web 服务已启动,但我无法在我的 Web 服务中自动装配其他 bean。

How can I define, my webservice in spring as bean?

我如何将 spring 中的 webservice 定义为 bean?

Following is my webservice impl class

以下是我的 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 is geneated by maven from wsdl file

MyServicePortType 由 maven 从 wsdl 文件生成

When I call this service (via SoapUi) it gives NullPointerException as the MyBean object is not autowired.

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

Since my application is built on Spring boot, there is no xml file. Currently I have sun-jaxws.xmlfile with endpoint configuration. How can I do following configuration in spring boot application

由于我的应用程序是基于 Spring boot 构建的,因此没有 xml 文件。目前我有sun-jaxws.xml带有端点配置的文件。如何在 Spring Boot 应用程序中进行以下配置

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

Following is my SpringBootServletInitializer class

以下是我的 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());
}
}

thanks

谢谢

回答by Parker Wang

Extending SpringBeanAutowiringSupportis 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 bootas it's a bit different on servlet context initialization.

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

Problem

问题

SpringBootServletInitializer.startup()uses a custom ContextLoaderListenerand does not pass the created application context to ContextLoader. Later when the object of JAX-WS endpoint class being initialized, SpringBeanAutowiringSupportdepends on ContextLoaderto retrieve the current application context, and always get null.

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

Workaround

解决方法

You could register a bean that implements org.springframework.boot.context.embedded.ServletContextInitializerto retrieve the application context during startup().

您可以注册一个 bean 来实现org.springframework.boot.context.embedded.ServletContextInitializerstartup().

@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.

然后,您可以在 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);
    }
}

Unit Testing

单元测试

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

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

@Autowired 
private ApplicationContext context;

private ServicePortType service;

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

回答by jonashackt

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).

您不必从 SpringBootServletInitializer 扩展您的配置,也不必覆盖 configure() 或 onStartup() 方法。并且您绝不需要构建一些实现 WebApplicationInitializer 的东西。只需执行几个步骤(您也可以在单独的@Configuration-class 中完成所有步骤,带有@SpringBootApplication 的类只需要知道它在哪里——例如通过@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
  1. 在 ServletRegistrationBean 中注册 CXFServlet
  2. 实例化一个 SpringBus
  3. 实例化 JAX-WS SEI(服务接口)的实现
  4. 使用 SpringBus 和 SEI 实现 Bean 实例化 EndpointImpl
  5. 在该 EndpointImpl 上调用 publish() 方法

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

回答by Andy Wilkinson

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 SpringBeanAutowiringSupportYou'd typically do so simply by subclassing it:

默认情况下,Spring 对 JAX-WS 端点一无所知——它们由 JAX-WS 运行时而不是 Spring 管理。您可以通过使用SpringBeanAutowiringSupport来克服这个问题,您通常只需将其子类化即可:

public class MySoapService extends SpringBeanAutowiringSupport implements MyServicePortType {
    …
}

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

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

public class MySoapService implements MyServicePortType {

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

回答by rajadilipkolli

Taking clue from @Andy Wilkinson using SpringBeanAutoWiringSupportis achieved by using

使用SpringBeanAutoWiringSupport从@Andy Wilkinson获取线索是通过使用实现的

public class MySoapService extends SpringBeanAutowiringSupport implements MyServicePortType {
    …
}

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

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

@Service
public class MySoapService implements MyServicePortType {

    @Autowired
    ServletContext servletContext;

    @PostConstruct
    public void init() {
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
            servletContext);
    }
}