[英]Why can't I pass a String to a @Service class constructor in Spring?
I have a Spring Boot app that provides files to users via a web front-end.我有一个 Spring 引导应用程序,它通过 web 前端向用户提供文件。 Users can browse various file back-ends (dropbox, local filesystem etc) and then add that file to their library.
用户可以浏览各种文件后端(保管箱、本地文件系统等),然后将该文件添加到他们的库中。 The class below concerns only part of the service layer that queries the Dropbox API.
下面的 class 仅涉及查询 Dropbox API 的服务层的一部分。 The final version will have several other methods but for now, this all that has been implemented:
最终版本将有其他几种方法,但目前,这一切都已实现:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import okhttp3.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@Service
public class DbxService {
private final String dropboxApiAccessToken;
private final OkHttpClient httpClient;
private final String dropboxApiAccessTokenFilePath;
private final ObjectMapper jsonObjectMapper; //fasterxml.jackson.databind.ObjectMapper
DbxService(String dropboxApiAccessTokenFilePath) throws IOException {
this.dropboxApiAccessTokenFilePath = dropboxApiAccessTokenFilePath;
this.httpClient = new OkHttpClient();
this.jsonObjectMapper = new ObjectMapper();
this.dropboxApiAccessToken = Files.readString(Path.of(dropboxApiAccessTokenFilePath));
}
public String listDropboxDirectory(String path) throws IOException {
//jackson.databind.node.ObjectNode
ObjectNode jsonPostRequestNode = jsonObjectMapper.createObjectNode();
//need JSON string {"path": "[path]"} for Dropbox API Post request
jsonPostRequestNode.put("path", path);
//okhttp3 request
Request dropboxApiRequest = new Request.Builder()
.url("https://api.dropboxapi.com/2/files/list_folder")
.post(RequestBody.create(jsonPostRequestNode.asText(), MediaType.parse("application/json")))
.addHeader("Authorization", "Bearer " + this.dropboxApiAccessToken)
.addHeader("Content-Type", "application/json")
.build();
Response dropboxApiResponse = this.httpClient.newCall(dropboxApiRequest).execute();
return dropboxApiResponse.body().string();
}
}
My class is thread-safe as it is immutable - there should not be a problem with it being a singleton (indeed, I very much want it to be a singleton).我的 class 是线程安全的,因为它是不可变的 - 作为 singleton 应该没有问题(实际上,我非常希望它是单例)。 It would be totally overkill for me to have some other back-end/data repository storing this path, however I do want it to be definable once - say in a configuration class - without having to hard-code the path string literal into a field.
让其他一些后端/数据存储库存储此路径对我来说完全是矫枉过正,但是我确实希望它可以定义一次- 例如在配置 class 中 - 无需将路径字符串文字硬编码到字段中.
The above code-snippet results in the following error: "Could not autowire. No beans of 'String' type found.".上面的代码片段导致以下错误:“无法自动装配。找不到'String'类型的bean。”。 My own investigations suggest it is not possible to inject objects into Spring managed components that are not, in themselves, Spring beans.
我自己的调查表明不可能将对象注入到 Spring 托管组件中,这些组件本身不是 Spring bean。
How can I refactor this snippet/service to avoid this error?如何重构此代码段/服务以避免此错误?
You might be able to inject a String
if you actually register the instance as a been first (never tried).如果您实际上将实例注册为第一个(从未尝试过),您可能能够注入一个
String
。 Maybe you also need a @Qualifier
-annotation, if there is more than one String bean.如果有多个 String bean,也许您还需要一个
@Qualifier
-annotation。
Usually, in Spring Boot the application.yml
/ application.properties
is used for environment configuration.通常,在 Spring Boot 中
application.yml
/ application.properties
用于环境配置。 The values can either be mapped to a POJO which is annotated with @ConfigurationProperties
.这些值可以映射到使用
@ConfigurationProperties
注释的 POJO。 This allows way you get type safe properties which can be injected in other beans/ services.这允许您获得可以注入其他 bean/服务的类型安全属性。 Or you can use the
@Value
-annotation to inject a single specific property in a bean/ service.或者您可以使用
@Value
-annotation 在 bean/ 服务中注入单个特定属性。
It turns out that it is not possible to inject objects that are not Spring Beans into a Spring @Service or @Component.事实证明,不可能将不是 Spring Bean 的对象注入到 Spring @Service 或 @Component 中。 In my particular case, I am trying to allow for the injection of a String representing the path to a file containing an API key into my @Service class constructor.
在我的特殊情况下,我试图允许将表示包含 API 密钥的文件的路径的字符串注入到我的 @Service class 构造函数中。
I managed to get my code to work, as desired, by using Spring's @Value annotation.通过使用 Spring 的 @Value 注释,我设法让我的代码按照需要工作。 I created en entry in my application.properties file:
我在 application.properties 文件中创建了条目:
DropboxService.AccessToken.FilePath="/foo/bar/credentialsFile" DropboxService.AccessToken.FilePath="/foo/bar/credentialsFile"
and changed my code to the following (note the @Value annotation before the class constructor method parameter):并将我的代码更改为以下内容(注意 class 构造函数方法参数之前的 @Value 注释):
package org.jdnet.audioBookPlayer.Utility;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@Service
public class DbxService {
private final String dropboxApiAccessToken;
private final OkHttpClient httpClient;
private final String dropboxApiAccessTokenFilePath;
private final ObjectMapper jsonObjectMapper; //fasterxml.jackson.databind.ObjectMapper
DbxService(@Value("${DropboxService.AccessToken.FilePath}") String dropboxApiAccessTokenFilePath) throws IOException {
this.dropboxApiAccessTokenFilePath = dropboxApiAccessTokenFilePath;
this.httpClient = new OkHttpClient();
this.jsonObjectMapper = new ObjectMapper();
this.dropboxApiAccessToken = Files.readString(Path.of(dropboxApiAccessTokenFilePath));
}
public String listDropboxDirectory(String path) throws IOException {
//jackson.databind.node.ObjectNode
ObjectNode jsonPostRequestNode = jsonObjectMapper.createObjectNode();
//need JSON string {"path": "[path]"} for Dropbox API Post request
jsonPostRequestNode.put("path", path);
//okhttp3 request
Request dropboxApiRequest = new Request.Builder()
.url("https://api.dropboxapi.com/2/files/list_folder")
.post(RequestBody.create(jsonPostRequestNode.asText(), MediaType.parse("application/json")))
.addHeader("Authorization", "Bearer " + this.dropboxApiAccessToken)
.addHeader("Content-Type", "application/json")
.build();
Response dropboxApiResponse = this.httpClient.newCall(dropboxApiRequest).execute();
return dropboxApiResponse.body().string();
}
}
A better answer would explain why I cannot simply use a @Bean definition in a configuration class as the only good reason I can see for Spring not being able to load a String in is that it cannot know which string to pass in when it tries to autowiring.一个更好的答案可以解释为什么我不能简单地在配置 class 中使用 @Bean 定义,这是我看到 Spring 无法加载字符串的唯一充分理由是它无法知道在尝试加载字符串时要传入哪个字符串自动装配。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.