Spring 未注入自动装配的 Bean

Spring Autowired Beans are not injected

我正在尝试使用 Jersey + Jetty 和 Spring 来设置一个简单的 REST 应用程序来为自动装配设置 bean。我也在通过 Gradle.

管理这个

我有 2 个主要端点 /users/greeting

A GET on /greeting only only returns text response and works fine.那里没有自动装配的依赖项。

但是,如果我在 /users 端点上进行获取,我 return 使用 API class 的响应应该由 spring 注入.该部分失败,因此每当我调用该端点时我都会得到一个 NullPointerException

还有一点要注意,我要用jersey 1.12和spring 4.2.0。原因是,这将是我必须在其他地方插入的原型。

在 /users 上执行 GET 时出现异常:

java.lang.NullPointerException
    at com.hateos.spring.test01.resources.user.controller.UserController.getAllUsers(UserController.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.sun.jersey.spi.container.JavaMethodInvokerFactory.invoke(JavaMethodInvokerFactory.java:60)
    at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)
    at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
    at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)
    at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
    at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
    at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
    at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1483)
    at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1414)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
    at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414)
    at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
    at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:841)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:535)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:168)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:166)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.Server.handle(Server.java:564)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:317)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:110)
    at org.eclipse.jetty.io.ChannelEndPoint.run(ChannelEndPoint.java:124)
    at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:128)
    at org.eclipse.jetty.util.thread.Invocable$InvocableExecutor.invoke(Invocable.java:222)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:294)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:126)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:673)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:591)
    at java.lang.Thread.run(Thread.java:748)

19-03-20 15:10:59:694  WARN qtp1567581361-16 server.HttpChannel:517 - /users
java.lang.NullPointerException
    at com.hateos.spring.test01.resources.user.controller.UserController.getAllUsers(UserController.java:35)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.sun.jersey.spi.container.JavaMethodInvokerFactory.invoke(JavaMethodInvokerFactory.java:60)
    at com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)
    at com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
    at com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)
    at com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
    at com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
    at com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
    at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1483)
    at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1414)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1363)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1353)
    at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:414)
    at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
    at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:841)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:535)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:168)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:166)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.Server.handle(Server.java:564)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:317)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:110)
    at org.eclipse.jetty.io.ChannelEndPoint.run(ChannelEndPoint.java:124)
    at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:128)
    at org.eclipse.jetty.util.thread.Invocable$InvocableExecutor.invoke(Invocable.java:222)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:294)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:126)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:673)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:591)
    at java.lang.Thread.run(Thread.java:748)

项目结构如下(跳过不相关的文件):

hateos-spring-test01
  |- src
  |  |- main
  |  |  |- java
  |  |  |  |- com.hateos.spring.test01 (nested folders)
  |  |  |     |- resources
  |  |  |     |  |- greeting
  |  |  |     |  |- user
  |  |  |     |     |- api
  |  |  |     |     |  |- impl
  |  |  |     |     |  |  |- UserAPIImpl.java
  |  |  |     |     |  |- UserAPI.java
  |  |  |     |     |- controller
  |  |  |     |        |- UserController.java
  |  |  |     |     |- exception
  |  |  |     |     |- model
  |  |  |     |        |- User.java
  |  |  |     |        |- UserList.java
  |  |  |     |- MyApplication.java
  |  |  |- resources
  |  |     |- hateos-spring-test01.xml
  |  |     |- log4j.properties
  |  |- test 
  |- build.gradle
  |- gradlew
  |- gradlew.bat
  |- settings.gradle

build.gradle:

apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'

version '1.0-SNAPSHOT'

sourceCompatibility = 1.8
mainClassName = 'com.hateos.spring.test01.MyApplication'

ext {
    jettyVersion = '9.4.6.v20170531'
}

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1'
    }
}


repositories {
    jcenter()
    mavenCentral()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.6.6'
    compile 'org.slf4j:slf4j-log4j12:1.6.6'

    compile "org.codehaus.jackson:jackson-core-asl:1.9.2"
    compile "org.codehaus.jackson:jackson-jaxrs:1.9.2"
    compile "org.codehaus.jackson:jackson-mapper-asl:1.9.2"
    compile "org.codehaus.jackson:jackson-xc:1.9.2"
    compile "com.sun.jersey:jersey-client:1.12"
    compile "com.sun.jersey:jersey-core:1.12"
    compile "com.sun.jersey:jersey-json:1.12"
    compile "com.sun.jersey:jersey-server:1.12"
    compile "com.sun.jersey:jersey-servlet:1.12"
    compile "org.codehaus.jettison:jettison:1.1"

    compile "org.eclipse.jetty:jetty-server:${jettyVersion}"
    compile "org.eclipse.jetty:jetty-servlet:${jettyVersion}"

    compile 'org.springframework:spring-core:4.2.0.RELEASE'
    compile 'org.springframework:spring-web:4.2.0.RELEASE'
    compile 'org.springframework.hateoas:spring-hateoas:0.25.1.RELEASE'
    compile 'org.springframework:spring-beans:4.2.0.RELEASE'
    compile 'org.springframework:spring-context:4.2.0.RELEASE'
    compile 'org.springframework:spring-context-support:4.2.0.RELEASE'

    testCompile group: 'junit', name: 'junit', version: '4.12'
}

spring xml 文件 (hateos-spring-text.xml):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com.hateos.spring.test01"/>

    <bean id="usersAPI" class="com.hateos.spring.test01.resources.user.api.impl.UserAPIImpl"/>
    <bean id="usersController" class="com.hateos.spring.test01.resources.user.controller.UserController" />
</beans>

MyApplication.java

package com.hateos.spring.test01;

import com.sun.jersey.spi.container.servlet.ServletContainer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyApplication {

    private static final Logger logger = LoggerFactory.getLogger(MyApplication.class);

    public static void main(String[] args) {
        logger.info("Hi guys!");
        Server server = new Server(9090);

        ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);

        servletContextHandler.setContextPath("/");
        server.setHandler(servletContextHandler);

        ServletHolder servletHolder = servletContextHandler.addServlet(ServletContainer.class, "/*");
        servletHolder.setInitOrder(0);
        servletHolder.setInitParameter(
                "com.sun.jersey.config.property.packages",
                "com.hateos.spring.test01.resources"
        );
        try {
            server.start();
            server.join();
        } catch (Exception ex) {
            logger.error("Error occurred while starting Jetty", ex);
            System.exit(1);
        } finally {
            server.destroy();
        }
    }
}

/resources/user/api/UserAPI.java

package com.hateos.spring.test01.resources.user.api;

import com.hateos.spring.test01.resources.user.model.User;
import com.hateos.spring.test01.resources.user.model.UserList;

public interface UserAPI {
    UserList getAll();
    User getById(String userId);
}

/resources/user/api/impl/UserAPIImpl.java

package com.hateos.spring.test01.resources.user.api.impl;

import com.hateos.spring.test01.resources.user.api.UserAPI;
import com.hateos.spring.test01.resources.user.model.User;
import com.hateos.spring.test01.resources.user.model.UserList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class UserAPIImpl implements UserAPI {
    private static final Logger logger = LoggerFactory.getLogger(UserAPIImpl.class);

    @Override
    public UserList getAll() {
        UserList userList = new UserList();
        for (int index = 0; index < userIds.length; index++) {
            userList.getUserList().add(toUser(index));
        }
        return userList;
    }

    @Override
    public User getById(String userId) {
        return findUserById(userId);
    }

    private User toUser(int index) {
        User newUser = null;
        if (index < userIds.length) {
            newUser = new User();
            newUser.setFirstName(firstNames[index]);
            newUser.setLastName(lastNames[index]);
            newUser.setUserId(userIds[index]);
        }
        return newUser;
    }

    private User findUserById(String userId) {
        for (int index = 0; index < userIds.length; index++) {
            if (userIds[index].equals(userId)) {
                return toUser(index);
            }
        }
        return null;
    }

    private static String[] firstNames = {"Bob", "Jerry", "Milton", "Carrie"};
    private static String[] lastNames = {"Builder", "Seinfeld", "Bombay", "Fisher"};
    private static String[] userIds = {"001", "153", "5566", "234"};
}

/resources/user/controller/UserController.java

package com.hateos.spring.test01.resources.user.controller;

import com.hateos.spring.test01.resources.user.api.UserAPI;
import com.hateos.spring.test01.resources.user.exception.UserNotFoundException;
import com.hateos.spring.test01.resources.user.model.User;
import com.hateos.spring.test01.resources.user.model.UserList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/users")
@Component
public class UserController {
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    private UserAPI userAPI;

    @Autowired
    public void setUserAPI(UserAPI usersAPI) {
        logger.info("userAPI is being injected");
        this.userAPI = usersAPI;
    }

    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public UserList getAllUsers() {
        logger.info("getAllUsers is being called");
        return this.userAPI.getAll();
    }

    @GET
    @Path("/{uid}")
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public User getUser(@PathParam("uid") String userId) {
        User foundUser = userAPI.getById(userId);
        if (foundUser != null) {
            return foundUser;
        } else {
            throw new UserNotFoundException(userId);
        }
    }
}

编辑 1启动时的控制台日志

5:06:41 PM: Executing task 'run'...

> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE

> Task :run
19-03-20 17:06:42:452  INFO main test01.MyApplication:15 - Hi guys!
19-03-20 17:06:42:496  INFO main util.log:192 - Logging initialized @446ms to org.eclipse.jetty.util.log.Slf4jLog
19-03-20 17:06:42:618  INFO main server.Server:372 - jetty-9.4.6.v20170531
Mar 20, 2019 5:06:42 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
  com.hateos.spring.test01.resources
Mar 20, 2019 5:06:42 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
  class com.hateos.spring.test01.resources.greeting.controller.GreetingController
  class com.hateos.spring.test01.resources.user.controller.UserController
  class com.hateos.spring.test01.resources.shared.RootController
Mar 20, 2019 5:06:42 PM com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Provider classes found:
  class com.hateos.spring.test01.resources.shared.controller.BaseExceptionMapper
Mar 20, 2019 5:06:42 PM com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.12 02/15/2012 04:51 PM'
19-03-20 17:06:43:540  INFO main handler.ContextHandler:788 - Started o.e.j.s.ServletContextHandler@4b29d1d2{/,null,AVAILABLE}
19-03-20 17:06:44:316  INFO main server.AbstractConnector:280 - Started ServerConnector@4a003cbe{HTTP/1.1,[http/1.1]}{0.0.0.0:9090}
19-03-20 17:06:44:316  INFO main server.Server:444 - Started @2270ms

编辑 2:这个问题与链接的副本不同。我用球衣,他们不用。我的解决方案(由 LppEdd 提供)是通过初始 main 函数让 jersey + spring 相互配合。我的问题是关于基于 xml 的 Spring bean 与 Jersey 的自动装配。

您很可能需要指定与 UserAPI

关联的 property
<bean id="usersController" class="com.hateos.spring.test01.resources.user.controller.UserController" />
   <property name="userAPI" ref="usersAPI" />
</bean>

但是,老实说,我会选择构造函数注入。干净多了。

@Path("/users")
@Component
class UserController {
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    private final UserAPI userApi;

    UserController(final UserAPI userApi) {
       this.userApi = userApi;
    }

    ...
}

正在使用

<bean id="usersController" class="com.hateos.spring.test01.resources.user.controller.UserController" />
   <constructor-arg ref="usersAPI" />
</bean>

尽管您已经在使用

<context:annotation-config/>
<context:component-scan base-package="com.hateos.spring.test01"/>

因此根本不需要 XML 文件中的 <bean> 条目。


查看评论。

// You forgot about this!
servletContextHandler.addEventListener(new ContextLoaderListener());
servletContextHandler.setInitParameter("contextConfigLocation", "classpath*:**/hateos-spring-text.xml");

// You basically already have this part. Added for clarity
final ServletHolder jerseyServlet = new ServletHolder(SpringServlet.class);
jerseyServlet.setInitOrder(0);
jerseyServlet.setInitParameter(
     "com.sun.jersey.config.property.packages",
     "com.hateos.spring.test01"
);

servletContextHandler.addServlet(jerseyServlet, "/*");

应该足够了。