2017-07-28 227 views
1

當使用Apache's CXF JAX-RS Spring Boot starterCXF CDI dependencycxf-integration-cdi)時,Spring會嘗試執行自動裝配,因爲它只支持JSR 330not CDI。有沒有辦法讓CDI與Spring Boot一起工作?帶Apache CXF和CDI的彈簧啓動

代碼:

package com.ibm.test.webservices; 

import javax.enterprise.inject.Any; 
import javax.enterprise.inject.Default; 
import javax.enterprise.inject.Instance; 
import javax.inject.Inject; 
import javax.inject.Named; 
import javax.ws.rs.ApplicationPath; 
import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 
import javax.ws.rs.core.Application; 

import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 

@SpringBootApplication 
@ApplicationPath("/") 
@Path("/") 
public class TestWebServices extends Application { 
    public static void main(String[] args) { 
     SpringApplication.run(
      TestWebServices.class, 
      "--cxf.path=/", 
      "--cxf.jaxrs.classes-scan=true", 
      "--cxf.jaxrs.classes-scan-packages=" + 
       TestWebServices.class.getPackage().getName() 
     ); 
    } 

    @Inject 
    @Any 
    private Instance<InvokerInterface> impl; 

    @GET 
    @Produces("text/plain") 
    @Path("/") 
    public String helloWorld() { 
     return impl.get().invoke(); 
    } 

    public interface InvokerInterface { 
     String invoke(); 
    } 

    @Named 
    @Default 
    public static class Implementation1 implements InvokerInterface { 
     public String invoke() { 
      return "Hello World 1\n"; 
     } 
    } 

    @Named 
    public static class Implementation2 implements InvokerInterface { 
     public String invoke() { 
      return "Hello World 2\n"; 
     } 
    } 
} 

的pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <groupId>com.ibm.test</groupId> 
    <artifactId>test-spring-boot-with-cdi</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    <packaging>jar</packaging> 

    <name>Test Spring Boot with Apache CXF and CDI</name> 

    <!-- We need to use cxf-spring-boot-starter-jaxrs 3.2.0 because of https://issues.apache.org/jira/browse/CXF-7237 
     At the time of writing this code, the latest available version in Maven central 
     is 3.1.7 so we need to use the Apache snapshot repository. --> 
    <repositories> 
     <repository> 
      <id>apache.snapshots</id> 
      <name>Apache Development Snapshot Repository</name> 
      <url>https://repository.apache.org/content/repositories/snapshots/</url> 
      <releases> 
       <enabled>false</enabled> 
      </releases> 
      <snapshots> 
       <enabled>true</enabled> 
      </snapshots> 
     </repository> 
    </repositories> 

    <parent> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-parent</artifactId> 
     <version>1.5.6.RELEASE</version> 
    </parent> 
    <dependencies> 
     <dependency> 
      <groupId>org.apache.cxf</groupId> 
      <artifactId>cxf-spring-boot-starter-jaxrs</artifactId> 
      <version>3.2.0-SNAPSHOT</version> 
     </dependency> 
     <dependency> 
      <groupId>javax.inject</groupId> 
      <artifactId>javax.inject</artifactId> 
      <version>1</version> 
     </dependency> 
     <dependency> 
      <groupId>org.apache.cxf</groupId> 
      <artifactId>cxf-integration-cdi</artifactId> 
      <version>3.1.11</version> 
     </dependency> 
    </dependencies> 

    <!-- Required for a standalone JAR: --> 
    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.springframework.boot</groupId> 
       <artifactId>spring-boot-maven-plugin</artifactId> 
       <executions> 
        <execution> 
         <goals> 
          <goal>repackage</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 
     </plugins> 
    </build> 
</project> 

例外:

2017-07-28 16:32:59.527 ERROR 9630 --- [   main] o.s.b.d.LoggingFailureAnalysisReporter : 

*************************** 
APPLICATION FAILED TO START 
*************************** 

Description: 

Field impl in com.ibm.test.webservices.TestWebServices required a bean of type 'javax.enterprise.inject.Instance' that could not be found. 


Action: 

Consider defining a bean of type 'javax.enterprise.inject.Instance' in your configuration. 

[WARNING] 
java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55) 
    at java.lang.reflect.Method.invoke(Method.java:508) 
    at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:527) 
    at java.lang.Thread.run(Thread.java:785) 
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testWebServices': Unsatisfied dependency expressed through field 'impl'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.enterprise.inject.Instance<com.ibm.test.webservices.TestWebServices$InvokerInterface>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject(), @javax.enterprise.inject.Any()} 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588) 
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553) 
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) 
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) 
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) 
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) 
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) 
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) 
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) 
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) 
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) 
    at com.ibm.test.webservices.TestWebServices.main(TestWebServices.java:22) 
    ... 6 more 
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.enterprise.inject.Instance<com.ibm.test.webservices.TestWebServices$InvokerInterface>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@javax.inject.Inject(), @javax.enterprise.inject.Any()} 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1493) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104) 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066) 
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585) 
    ... 25 more 
+1

春天是不是CDI。 Spring不執行CDI。如果你正在尋找更輕量級的Java EE框架,那麼[Hammock](https://github.com/hammock-project/hammock)和[Wildfly Swarm](http://wildfly-swarm.io/) –

回答

1

關鍵的見解是必須避免Spring自動裝配(例如,scanBasePackages,@SpringBootApplication,@ComponentScan等)。以下工作:

TestSpringBootApplication.java:

package com.test.webservices; 

import org.apache.cxf.cdi.CXFCdiServlet; 
import org.jboss.weld.environment.se.Weld; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.boot.web.servlet.ServletComponentScan; 
import org.springframework.boot.web.servlet.ServletRegistrationBean; 
import org.springframework.boot.web.support.SpringBootServletInitializer; 
import org.springframework.context.ApplicationContextInitializer; 
import org.springframework.context.ConfigurableApplicationContext; 
import org.springframework.context.annotation.Bean; 

@SpringBootApplication 
// @ServletComponentScan only occurs with an embedded web server, and this is 
// needed for Tomcat: 
// https://docs.jboss.org/weld/reference/latest/en-US/html/environments.html#_tomcat 
@ServletComponentScan(basePackageClasses = { org.jboss.weld.environment.servlet.Listener.class }) 
public class TestSpringBootApplication extends SpringBootServletInitializer { 

    public static void main(String[] args) { 
     SpringApplication app = new SpringApplication(TestSpringBootApplication.class); 
     app.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() { 
      @Override 
      public void initialize(ConfigurableApplicationContext applicationContext) { 
       new Weld().initialize(); 
      } 
     }); 
     app.run(args); 
    } 

    @Bean 
    public ServletRegistrationBean cxfServletRegistration() { 
     // http://cxf.apache.org/docs/using-cxf-and-cdi-11-jsr-346.html 
     ServletRegistrationBean registration = new ServletRegistrationBean(new CXFCdiServlet(), "/*"); 
     registration.setLoadOnStartup(1); 
     return registration; 
    } 
} 

TestWebServicesApplication.java:

package com.test.webservices; 

import javax.ws.rs.ApplicationPath; 
import javax.ws.rs.core.Application; 

@ApplicationPath("/") 
public class TestWebServicesApplication extends Application { 
} 

TestWebServices.java:

package com.test.webservices; 

import javax.enterprise.inject.Any; 
import javax.enterprise.inject.Instance; 
import javax.enterprise.inject.Vetoed; 
import javax.inject.Inject; 
import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 

@Path("/") 
public class TestWebServices { 
    @Inject 
    @Any 
    private Instance<InvokerInterface> impl; 

    @GET 
    @Produces("text/plain") 
    @Path("/") 
    public String helloWorld() { 
     return impl.get().invoke(); 
    } 

    public interface InvokerInterface { 
     String invoke(); 
    } 

    public static class Implementation1 implements InvokerInterface { 
     public String invoke() { 
      return "Hello World 1\n"; 
     } 
    } 

    @Vetoed 
    public static class Implementation2 implements InvokerInterface { 
     public String invoke() { 
      return "Hello World 2\n"; 
     } 
    } 
} 

的pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <groupId>com.test</groupId> 
    <artifactId>test-spring-boot-with-cdi</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    <packaging>war</packaging> 

    <name>Test Spring Boot with Apache CXF and CDI</name> 

    <properties> 
     <maven.compiler.source>1.8</maven.compiler.source> 
     <maven.compiler.target>1.8</maven.compiler.target> 
    </properties> 

    <!-- We need to use cxf-spring-boot-starter-jaxrs 3.2.0 because of https://issues.apache.org/jira/browse/CXF-7237 
     At the time of writing this code, the latest available version in Maven central 
     is 3.1.7 so we need to use the Apache snapshot repository. --> 
    <repositories> 
     <repository> 
      <id>apache.snapshots</id> 
      <name>Apache Development Snapshot Repository</name> 
      <url>https://repository.apache.org/content/repositories/snapshots/</url> 
      <releases> 
       <enabled>false</enabled> 
      </releases> 
      <snapshots> 
       <enabled>true</enabled> 
      </snapshots> 
     </repository> 
    </repositories> 

    <parent> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-parent</artifactId> 
     <version>1.5.6.RELEASE</version> 
    </parent> 
    <dependencies> 
     <dependency> 
      <groupId>org.apache.cxf</groupId> 
      <artifactId>cxf-spring-boot-starter-jaxrs</artifactId> 
      <version>3.2.0-SNAPSHOT</version> 
     </dependency> 
     <dependency> 
      <groupId>javax.inject</groupId> 
      <artifactId>javax.inject</artifactId> 
      <version>1</version> 
     </dependency> 
     <dependency> 
      <groupId>org.apache.cxf</groupId> 
      <artifactId>cxf-integration-cdi</artifactId> 
      <version>3.1.11</version> 
     </dependency> 
     <dependency> 
      <groupId>org.jboss.weld.servlet</groupId> 
      <artifactId>weld-servlet</artifactId> 
      <version>2.4.4.Final</version> 
     </dependency> 
     <dependency> 
      <groupId>org.jboss.weld.se</groupId> 
      <artifactId>weld-se</artifactId> 
      <version>2.4.4.Final</version> 
     </dependency> 
     <dependency> 
      <groupId>com.fasterxml.jackson.jaxrs</groupId> 
      <artifactId>jackson-jaxrs-json-provider</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>org.glassfish</groupId> 
      <artifactId>javax.json</artifactId> 
      <version>1.0.4</version> 
     </dependency> 
    </dependencies> 

    <!-- Required for a standalone JAR: --> 
    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.springframework.boot</groupId> 
       <artifactId>spring-boot-maven-plugin</artifactId> 
       <executions> 
        <execution> 
         <goals> 
          <goal>repackage</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 
     </plugins> 
    </build> 
</project> 

的src /主/資源/ META-INF/beans.xml文件:

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" 
     version="1.1" bean-discovery-mode="all"> 
</beans> 

的src /主/資源/ META-INF/context.xml中:

<!-- Required for Tomcat: https://docs.jboss.org/weld/reference/latest/en-US/html/environments.html#_tomcat --> 
<Context> 
    <Resource name="BeanManager" auth="Container" 
     type="javax.enterprise.inject.spi.BeanManager" factory="org.jboss.weld.resources.ManagerObjectFactory" /> 
</Context> 

要使用JAXB在除了JSON,似乎CXF的JAXRSCdiResourceExtension沒有找到任何@Provider S,所以我還添加了CDI Extension創建JacksonJaxbJsonProviderAnnotatedType(這也似乎需要的JAX-RS Application對象不覆蓋getClassesgetSingletons,而是可以自動發現所有@Path S):

package com.test; 

import javax.enterprise.event.Observes; 
import javax.enterprise.inject.spi.AnnotatedType; 
import javax.enterprise.inject.spi.BeanManager; 
import javax.enterprise.inject.spi.BeforeBeanDiscovery; 

import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 

public class RegisterCDIBeans implements javax.enterprise.inject.spi.Extension { 
    public void beforeBeanDiscovery(@Observes BeforeBeanDiscovery bbd, BeanManager beanManager) { 
     final AnnotatedType<?> annotatedType = beanManager.createAnnotatedType(JacksonJaxbJsonProvider.class); 
     bbd.addAnnotatedType(annotatedType, annotatedType.toString()); 
    } 
} 

然後在src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension註冊CDI擴展:

com.test.RegisterCDIBeans 
0

您需要創建bean或使用@Autowired注入javax.enterprise.inject.Instance類爲TestWebServices。您可以創建bean到ApplicationContaxt.xml中,並使用@Autowired @Qualifier來注入類。

+0

,我不明白你的意思。 '@ Autowired'是Spring的概念,但Spring不理解CDI。這個異常表明Spring已經在嘗試連接它,但它不知道如何做一個CDI實例。我認爲這個解決方案可能需要讓Spring將配線委託給cxf-integration-cdi。 – kgibm