java - JAX-RS 依赖注入(inject)

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

我已经使用 Spring Rest 完成了项目。现在我们有一个小型的休息项目并计划使用 Jersey JAX-RS。我对此并不陌生,并引用了 SO 和其他博客以成功实现具有依赖项注入(inject)的 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
}

服务.java

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

我假设 Controller 和服务的每个实例都是在 Tomcat 8 服务器启动时创建的,并且依赖注入(inject)已完成。但是在启动期间,在控制台上得到了这个

INFO: Registering the Jersey servlet application, named com.sample.auto2.AppConfig, at the servlet mapping /*, with the Application class of the same name.

AppConfig

AppConfig

Nov 15, 2016 12:22:20 PM org.glassfish.jersey.server.ApplicationHandler initialize INFO: Initiating Jersey application, version Jersey: 2.6 2014-02-18 21:52:53...

AppFeature

AppBinder

Nov 15, 2016 12:22:21 PM org.apache.catalina.startup.HostConfig deployDirectory

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

Service instance created

Controller created

我的问题

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

编辑:

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

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

编辑 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. 由此,注入(inject)在启动时完成,但在 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);
    }
}

最佳答案

Service,Controller constructors is being called whenever we send an http request; does it create instances in each request or just calling constructor?

不幸的是,@Singleton 在使用 AbstractBinder 进行绑定(bind)时没有任何效果。我们需要明确的说应该是单例

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

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

(at the very first request - Why not during server startup!!)

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

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

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

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

Service,Controller constructors is being called whenever we send an http request; does it create instances in each request or just calling constructor?

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

Why System.out in AppConfig is called twice?

不确定,可能只是 Jersey 内部 Bootstrap 需要的

Is there a better way for setting up my small project, which does not have any db access and only three post endpoints?

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

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

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

关于java - JAX-RS 依赖注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40603988/

相关文章:

Java并发

java - 为什么很多 Maven 依赖版本都以 SNAPSHOT 结尾?

java - 如何使用 MyFaces Trinidad 输入日期组件?

c# - TDD 模拟篮子里的所有鸡蛋?

asp.net-core - Blazor HttpClient 注入(inject)到 ViewModel 构造函数中

c# - 使用 spring.net 将类注入(inject)到 using 语句中

java - 我应该选择为 Android 购物应用程序使用数据库吗

Java 字符串第 n 位切分

java - servlet 映射 url 模式上的双通配符 (*) 是什么意思?

security - 在缓存中安全地存储密码哈希