如何在Spring中提供参数化组件?
How to provide a parameterized component in Spring?
目前,我有一个实现 CommandLineRunner
并使用 Commons CLI 解析命令行参数的组件。
java -jar app.jar --host 123.123.222 --port 8080
还有另一个组件 Requester
,它取决于这些参数(的子集)。
@Component
public class Requester
{
// host and port need to be configured once
private final String host;
private final int port;
public Requester(String host, int port)
{
this.host = host;
this.port = port;
}
public boolean doRequest(String name) throws Exception
{
String url = "http://" + host + ":" + port + "/?command=" + name;
URL obj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) obj.openConnection();
int responseCode = connection.getResponseCode();
return 200 == responseCode;
}
}
如何将已配置的 Requester
自动装配到未来的组件中? Spring 创建参数化单例 bean 的方法是什么?
一个解决方案是让每个对程序参数有任何依赖性的组件实现 CommandLineRunner
。这样它就可以解析程序参数本身,但这是一种高度冗余的方法。一定有更好的解决办法。
你检查注释了吗@Value?它允许您在 运行 时间从属性文件中注入值。每次需要从资源中注入一些外部价值时,您都应该使用它。例如你的代码可以是:
@Component
public class Requester
{
@Value("${host}")
private final String host;
@Value("${port}")
private final int port;
public boolean doRequest(String name) throws Exception
{
String url = "http://" + host + ":" + port + "/?command=" + name;
URL obj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) obj.openConnection();
int responseCode = connection.getResponseCode();
return 200 == responseCode;
}
}
在您的 application.properties
文件中:
...
host = myhost
port = 1234
...
如果您想将参数作为命令行参数传递,您只需像以前那样调用命令即可:
java -jar app.jar --host 123.123.222 --port 8080
这些参数将覆盖 属性 文件中的参数,因为它们具有更高的优先级,如 documentation.
中所示
使用post来处理。
让我们假设这是您的 CommandLineRunner
实现:
@Component
public class CLIArgs implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// Parse the args, and for each arg set system property
System.setProperty("ArgName", "value");
}
}
然后为您想要的所有参数创建 Getters,Requester
将如下所示:
@Component
public class Requester {
@Autowired
private Environment env; // This is a spring component
// host and port need to be configured once
private String host;
private int port;
@PostConstruct
public void init () {
this.host = env.getProperty("host");
this.port = Integer.parseInt(env.getProperty("port"));
}
public boolean doRequest(String name) throws Exception {
String url = "http://" + host + ":" + port + "/?command=" + name;
URL obj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) obj.openConnection();
int responseCode = connection.getResponseCode();
return 200 == responseCode;
}
}
将在创建组件时发生
受 JB Nizet 评论的启发,我现在在启动 Spring 应用程序之前解析命令行参数并手动注册 bean。
我确定了两种方法:提供 c'tor 参数并让 Spring 创建 bean 或向 Spring 提供供应商函数。
现在可以在应用程序的其他部分声明 @Autowire private Requester requester;
。
为了使此解决方案起作用,需要从 Requester
中删除 @Component
注释,否则当 Spring 无法提供必要的构造函数参数时可能会出现问题。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// parse args here
String host = getHost();
int port = getPort();
SpringApplication application = new SpringApplication(Application.class);
// provide c'tor arguments and let Spring create the instance
BeanDefinitionCustomizer bdc = bd -> {
// working with bd.getPropertyValues() uses setter instead of c'tor
ConstructorArgumentValues values = bd.getConstructorArgumentValues();
values.addIndexedArgumentValue(0, host);
values.addIndexedArgumentValue(1, port);
};
application.addInitializers((GenericApplicationContext ctx) -> ctx.registerBean(Requester.class, bdc));
// or create the instance yourself
Requester requester = new Requester(host, port);
application.addInitializers((GenericApplicationContext ctx) -> ctx.registerBean(Requester.class, () -> requester));
application.run(args);
}
}
目前,我有一个实现 CommandLineRunner
并使用 Commons CLI 解析命令行参数的组件。
java -jar app.jar --host 123.123.222 --port 8080
还有另一个组件 Requester
,它取决于这些参数(的子集)。
@Component
public class Requester
{
// host and port need to be configured once
private final String host;
private final int port;
public Requester(String host, int port)
{
this.host = host;
this.port = port;
}
public boolean doRequest(String name) throws Exception
{
String url = "http://" + host + ":" + port + "/?command=" + name;
URL obj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) obj.openConnection();
int responseCode = connection.getResponseCode();
return 200 == responseCode;
}
}
如何将已配置的 Requester
自动装配到未来的组件中? Spring 创建参数化单例 bean 的方法是什么?
一个解决方案是让每个对程序参数有任何依赖性的组件实现 CommandLineRunner
。这样它就可以解析程序参数本身,但这是一种高度冗余的方法。一定有更好的解决办法。
你检查注释了吗@Value?它允许您在 运行 时间从属性文件中注入值。每次需要从资源中注入一些外部价值时,您都应该使用它。例如你的代码可以是:
@Component
public class Requester
{
@Value("${host}")
private final String host;
@Value("${port}")
private final int port;
public boolean doRequest(String name) throws Exception
{
String url = "http://" + host + ":" + port + "/?command=" + name;
URL obj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) obj.openConnection();
int responseCode = connection.getResponseCode();
return 200 == responseCode;
}
}
在您的 application.properties
文件中:
...
host = myhost
port = 1234
...
如果您想将参数作为命令行参数传递,您只需像以前那样调用命令即可:
java -jar app.jar --host 123.123.222 --port 8080
这些参数将覆盖 属性 文件中的参数,因为它们具有更高的优先级,如 documentation.
中所示使用post
让我们假设这是您的 CommandLineRunner
实现:
@Component
public class CLIArgs implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// Parse the args, and for each arg set system property
System.setProperty("ArgName", "value");
}
}
然后为您想要的所有参数创建 Getters,Requester
将如下所示:
@Component
public class Requester {
@Autowired
private Environment env; // This is a spring component
// host and port need to be configured once
private String host;
private int port;
@PostConstruct
public void init () {
this.host = env.getProperty("host");
this.port = Integer.parseInt(env.getProperty("port"));
}
public boolean doRequest(String name) throws Exception {
String url = "http://" + host + ":" + port + "/?command=" + name;
URL obj = new URL(url);
HttpURLConnection connection = (HttpURLConnection) obj.openConnection();
int responseCode = connection.getResponseCode();
return 200 == responseCode;
}
}
受 JB Nizet 评论的启发,我现在在启动 Spring 应用程序之前解析命令行参数并手动注册 bean。
我确定了两种方法:提供 c'tor 参数并让 Spring 创建 bean 或向 Spring 提供供应商函数。
现在可以在应用程序的其他部分声明 @Autowire private Requester requester;
。
为了使此解决方案起作用,需要从 Requester
中删除 @Component
注释,否则当 Spring 无法提供必要的构造函数参数时可能会出现问题。
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// parse args here
String host = getHost();
int port = getPort();
SpringApplication application = new SpringApplication(Application.class);
// provide c'tor arguments and let Spring create the instance
BeanDefinitionCustomizer bdc = bd -> {
// working with bd.getPropertyValues() uses setter instead of c'tor
ConstructorArgumentValues values = bd.getConstructorArgumentValues();
values.addIndexedArgumentValue(0, host);
values.addIndexedArgumentValue(1, port);
};
application.addInitializers((GenericApplicationContext ctx) -> ctx.registerBean(Requester.class, bdc));
// or create the instance yourself
Requester requester = new Requester(host, port);
application.addInitializers((GenericApplicationContext ctx) -> ctx.registerBean(Requester.class, () -> requester));
application.run(args);
}
}