【问题标题】:JAX-RS dependency injectionJAX-RS 依赖注入
【发布时间】:2017-03-28 23:45:47
【问题描述】:

我已经使用 Spring Rest 完成了项目。现在我们有一个小型的休息项目,并计划使用 Jersey JAX-RS。我是新手,并参考 SO 和其他博客成功实现了带有依赖注入的 Rest api。

有以下代码。

AppConfig.java

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

@ApplicationPath("/")
public class AppConfig extends Application {    
    @Override
    public Set<Class<?>> getClasses() {
        System.out.println("AppConfig");
        final Set<Class<?>> s = new HashSet<Class<?>>();
        s.add(Controller.class);
        s.add(AppFeature.class);
        return s;
    }
}

AppBinder.java

import org.glassfish.hk2.utilities.binding.AbstractBinder;

public class AppBinder extends AbstractBinder {
    @Override
    protected void configure() {
        System.out.println("AppBinder");
        bind(ReflectionService.class).to(ReflectionService.class);
    }
}

AppFeature.java

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;

public class AppFeature implements Feature {
    @Override
    public boolean configure(FeatureContext context) {
        System.out.println("AppFeature");
        context.register(new AppBinder());
        return true;
    }
}

Controller.java

@Path("/")
public class Controller {   
    @Inject
    Service service;    
    public Controller(){
        System.out.println("Controller created");
    }
    // other methods
}

Service.java

@Singleton
public class Service    
    public Service(){
        System.out.println("Service instance created");
    }
    // other methods
}

我假设 Controller 和 Service 的每个实例都是在 Tomcat 8 服务器启动和依赖注入完成时创建的。但是在启动过程中,在控制台上得到了这个

INFO:注册 Jersey servlet 应用程序,名为 com.sample.auto2.AppConfig,在 servlet 映射 /* 处,带有 同名的应用程序类。

应用配置

应用配置

2016 年 11 月 15 日下午 12:22:20 org.glassfish.jersey.server.ApplicationHandler 初始化 INFO:启动 Jersey 应用程序,版本 Jersey:2.6 2014-02-18 21:52:53...

应用功能

AppBinder

2016 年 11 月 15 日下午 12:22:21 org.apache.catalina.startup.HostConfig 部署目录

每次,我们发送一个请求,在控制台中得到关注

已创建服务实例

控制器创建

我的问题

  1. 每当我们发送一个 http请求;它是在每个请求中创建实例还是只是 调用构造函数?
  2. 为什么 AppConfig 中的 System.out 会被调用两次?
  3. 有没有更好的方法来设置我的小项目,它没有任何数据库访问权限,只有三个 post 端点?

编辑:

根据@Harikrishnan 提供的链接,为Controller 类添加了@Singleton。现在构造函数只调用一次(在第一次请求时 - 为什么不在服务器启动期间!)。

但是为什么在每个请求上调用服务类构造函数(在将@Singleton 添加到控制器之前),即使它是单例的?其他问题也依然存在。

编辑 2:

谢谢@peeskillet。所以这些是我的结果。

  1. 这在第一次请求时只调用了一次构造函数

    bind(ReflectionService.class).to(ReflectionService.class).in(Singleton.class);
    bind(Controller.class).to(Controller.class).in(Singleton.class);
    
  2. 这给出了http请求的错误

    bind(ReflectionService.class).to(ReflectionService.class).in(Immediate.class);
    
    java.lang.IllegalStateException: Could not find an active context for org.glassfish.hk2.api.Immediate
    

    不过没关系,因为

  3. 这在服务器启动时调用了构造函数,并且只调用了一次

    bind(new ReflectionService()).to(ReflectionService.class);
    bind(new Controller()).to(Controller.class);
    
  4. 由此,启动时完成注入,但 http 请求出现 404 错误。 (思想Controller是在AppBinder中配置的,那为什么在AppConfig中)

    @ApplicationPath("/")
    public class AppConfig extends ResourceConfig  {    
        public AppConfig() {
            register(new AppBinder());
        }
    }
    
  5. 正如你所说,这让它运行!

    @ApplicationPath("/")
    public class AppConfig extends ResourceConfig  {        
        public AppConfig() {
            register(new AppBinder());
            register(Controller.class);
        }
    }
    

这些都是我需要的

public class AppBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(new ReflectionService()).to(ReflectionService.class);
        bind(new Controller()).to(Controller.class);
    }
}

@ApplicationPath("/")
public class AppConfig extends ResourceConfig  {    
    public AppConfig() {
        register(new AppBinder());
        register(Controller.class);
    }
}

【问题讨论】:

    标签: java jakarta-ee dependency-injection jersey jax-rs


    【解决方案1】:

    默认情况下,Jersey 会为每个请求创建一个资源类的新实例。所以如果你不注解 Jersey 资源类,它会隐式使用@RequestScoped 作用域

    请阅读https://stackoverflow.com/a/20505899/721597

    也看Jersey 2 singleton dependency injection creates multiple instances

    【讨论】:

      【解决方案2】:

      每当我们发送 http 请求时,都会调用 Service、Controller 构造函数;它是在每个请求中创建实例还是只是调用构造函数?

      不幸的是,@Singleton 在使用AbstractBinder 绑定时没有任何效果。我们需要明确地说它应该是一个单例

      bind(ReflectionService.class).to(ReflectionService.class).in(Singleton.class);
      

      默认行为是“每次查找”范围,这意味着每次请求服务时都会创建一个新实例

      (在第一次请求时 - 为什么不在服务器启动期间!!)

      这就是它的工作原理。还有一个ImmediateScope,会导致它在启动时被创建

       bind(ReflectionService.class).to(ReflectionService.class).in(ImmediateScope.class)
      

      或者你可以只使用一个实例而不是类,它会自动成为一个单例

      bind(new ReflectionService()).to(ReflectionService.class)
      

      每当我们发送 http 请求时,都会调用 Service、Controller 构造函数;它是在每个请求中创建实例还是只是调用构造函数?

      这是默认行为。每个请求的资源类的新实例。如前所述,如果您只想要一次实例,请将其标记为 @Singleton

      为什么 AppConfig 中的 System.out 会被调用两次?

      不确定,可能只是引导程序上的 Jersey 内部处理需要

      有没有更好的方法来设置我的小项目,它没有任何数据库访问权限,只有三个 post 端点?

      您的设置很好,但如果您使用的是 Jersey,那么您应该使用 ResourceConfig(Application 的扩展)类而不是 Application

      @ApplicationPath("/")
      public class AppConfig extends ResourceConfig {
          public AppConfig() {
              register(new AppBinder());
              register(Controller.class);
          }
      }
      

      有了这个,您不需要将AppBinder 包装在AppFeature 中。 Jersey 已经知道如何处理AppBinder

      【讨论】:

      • 谢谢@peeskillet。这很有帮助。我也想标记我的观察。不能评论,因为太长了。所以编辑你的答案
      • 我只是编辑了您的帖子,而不是将其添加到我的帖子中。哦,对于即时范围。我忘了你还需要配置this。此外,您不需要将控制器添加到活页夹
      • 从活页夹中移除控制器会在没有@Singleton 的情况下在每个请求上创建其对象。然后添加 @Singleton 在第一次请求时只创建一次。但是我在服务器启动期间需要它,并且将控制器添加到活页夹中。这有什么问题吗?
      • 查看我评论中的链接
      • 但实际上,如果您愿意,您可以简单地执行 register(new Controller(new Service()) 并忘记活页夹。无需注射
      猜你喜欢
      • 1970-01-01
      • 2016-07-13
      • 1970-01-01
      • 1970-01-01
      • 2017-03-22
      • 1970-01-01
      • 2019-02-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多