Java 使用 Spring IoC 设置枚举值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/710392/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Spring IoC to set up enum values
提问by Olivier
Is there a way to set up such enum values via Spring IoC at construction time?
有没有办法在构建时通过 Spring IoC 设置这样的枚举值?
What I would like to do is to inject, at class load time, values that are hard-coded in the code snippet below:
我想做的是在类加载时注入以下代码片段中硬编码的值:
public enum Car
{
NANO ("Very Cheap", "India"),
MERCEDES ("Expensive", "Germany"),
FERRARI ("Very Expensive", "Italy");
public final String cost;
public final String madeIn;
Car(String cost, String madeIn)
{
this.cost= cost;
this.madeIn= madeIn;
}
}
Let's say that the application must be deployed in Germany, where Nanos are "Nearly free", or in India where Ferraris are "Unaffordable". In both countries, there are only three cars (deterministic set), no more no less, hence an enum, but their "inner" values may differ. So, this is a case of contextual initializationof immutables.
假设应用程序必须部署在德国,在那里 Nanos“几乎免费”,或者在法拉利“负担不起”的印度。在这两个国家,只有三辆车(确定性集合),不多也不少,因此是枚举,但它们的“内部”值可能不同。所以,这是一个不可变的上下文初始化的情况。
采纳答案by bruno conde
Do you mean setting up the enum
itself?
你的意思是设置enum
本身?
I don't think that's possible. You cannot instantiate enums because they have a static
nature. So I think that Spring IoC can't createenums
as well.
我不认为那是可能的。您不能实例化枚举,因为它们具有static
性质。所以我认为 Spring IoC 也不能创建enums
。
On the other hand, if you need to set initialize something with a enum
please check out the Spring IoC chapter. (search for enum) There's a simple example that you can use.
另一方面,如果您需要使用 a 设置初始化内容,enum
请查看Spring IoC 章节。(搜索枚举)有一个可以使用的简单示例。
回答by tpdi
What do you need to set up? The values are created when the class loads, and as it's an enum, no other values can be created (unless you add them to the source and recompile).
你需要设置什么?这些值是在类加载时创建的,因为它是一个枚举,所以不能创建其他值(除非您将它们添加到源代码并重新编译)。
That's the point of an enum, to be able to give limit a type to an explicit range of constant, immutable values. Now, anywhere in your code, you can refer to a type Car, or its values, Car.NANO, Car.MERCEDES, etc.
这就是枚举的要点,能够将类型限制为常量、不可变值的显式范围。现在,您可以在代码中的任何位置引用 Car 类型或其值、Car.NANO、Car.MERCEDES 等。
If, on the other hand, you have a set of values that isn't an explicit range, and you want to be able to create arbitrary objects of this type, you'd use the same ctor as in your post, but as a regular, not enum class. Then Spring provides various helper clases to read values from some source (XML file, config file, whatever) and create Lists of that type.
另一方面,如果您有一组不是明确范围的值,并且您希望能够创建这种类型的任意对象,您将使用与您的帖子中相同的 ctor,但作为常规,而不是枚举类。然后 Spring 提供了各种辅助类来从某些源(XML 文件、配置文件等)读取值并创建该类型的列表。
回答by Brian Agnew
Why not provide a setter (or constructor argument) that takes a String, and simply call Enum.valueOf(String s)to convert from a String to an enum. Note an exception will get thrown if this fails, and your Spring initialisation will bail out.
为什么不提供一个接受 String 的 setter(或构造函数参数),并简单地调用Enum.valueOf(String s)将 String 转换为 enum。请注意,如果失败,则会引发异常,并且您的 Spring 初始化将退出。
回答by Greg Noe
<bean id="car" class="Foo">
<property name="carString" value="NANO" />
</bean>
And then in your class Foo, you would have this setter:
然后在你的 Foo 类中,你会有这个二传手:
public void setCar(String carString) {
this.carString = Car.valueOf(carString);
}
回答by Greg Noe
All right, this is a bit complex but you may find a way to integrate it. Enums are not meant to change at runtime, so this is a reflection hack. Sorry I don't have the Spring implementation part, but you could just build a bean to take in the enum class or object, and another field that would be the new value or values.
好吧,这有点复杂,但您可能会找到一种方法来集成它。枚举并不意味着在运行时改变,所以这是一个反射黑客。抱歉,我没有 Spring 实现部分,但是您可以构建一个 bean 来接收枚举类或对象,以及另一个将是新值或值的字段。
Constructor con = MyEnum.class.getDeclaredConstructors()[0];
Method[] methods = con.getClass().getDeclaredMethods();
for (Method m : methods) {
if (m.getName().equals("acquireConstructorAccessor")) {
m.setAccessible(true);
m.invoke(con, new Object[0]);
}
}
Field[] fields = con.getClass().getDeclaredFields();
Object ca = null;
for (Field f : fields) {
if (f.getName().equals("constructorAccessor")) {
f.setAccessible(true);
ca = f.get(con);
}
}
Method m = ca.getClass().getMethod(
"newInstance", new Class[] { Object[].class });
m.setAccessible(true);
MyEnum v = (MyEnum) m.invoke(ca, new Object[] {
new Object[] { "MY_NEW_ENUM_VALUE", Integer.MAX_VALUE } });
System.out.println(v.getClass() + ":" + v.name() + ":" + v.ordinal());
This is taken from this site.
这是从这个网站上截取的。
回答by javashlook
I don't think it can be done from Spring's ApplicationContext
configuration. But, do you really need it done by Spring, or can you settle for simple externalization using ResourceBundle; like this:
我不认为它可以从 Spring 的ApplicationContext
配置中完成。但是,您是否真的需要 Spring 来完成它,或者您可以使用ResourceBundle进行简单的外部化吗?像这样:
public enum Car
{
NANO,
MERCEDES,
FERRARI;
public final String cost;
public final String madeIn;
Car()
{
this.cost = BUNDLE.getString("Car." + name() + ".cost");
this.madeIn = BUNDLE.getString("Car." + name() + ".madeIn");
}
private static final ResourceBundle BUNDLE = ResourceBundle.getBundle(...);
}
In the properties file, one for each specific locale, enter the keys describing the possible internal enum values:
在属性文件中,每个特定区域设置一个,输入描述可能的内部枚举值的键:
Car.NANO.cost=Very cheap
Car.NANO.madeIn=India
Car.MERCEDES.cost=Expensive
...
The only drawback of this approach is having to repeat the name of enum fields (cost, madeIn) in Java code as strings. Edit: And on the plus side, you can stack all properties of all enums into one properties file per language/locale.
这种方法的唯一缺点是必须在 Java 代码中将枚举字段的名称(cost、madeIn)作为字符串重复。编辑:从好的方面来说,您可以将所有枚举的所有属性堆叠到每个语言/区域设置的一个属性文件中。
回答by Olivier
Here is the solution I came to (thanks to Javashlook whose answer put me on track). It works, but it's most probably not a production-grade way of doing it.
这是我找到的解决方案(感谢 Javashlook,他的回答让我走上了正轨)。它有效,但很可能不是生产级的方法。
But better than a thousand words, here is the code, I'll let you judge by yourself.
但胜过一千字,这里是代码,我让你自己判断。
Let's take a look at the revised Car
enum :
我们来看看修改后的Car
enum :
public enum Car {
NANO(CarEnumerationInitializer.getNANO()), MERCEDES(
CarEnumerationInitializer.getMERCEDES()), FERRARI(
CarEnumerationInitializer.getFERRARI());
public final String cost;
public final String madeIn;
Car(ICarProperties properties) {
this.cost = properties.getCost();
this.madeIn = properties.getMadeIn();
}
}
And here are the "plumbling" classes :
这里是“管道”类:
//Car's properties placeholder interface ...
public interface ICarProperties {
public String getMadeIn();
public String getCost();
}
//... and its implementation
public class CarProperties implements ICarProperties {
public final String cost;
public final String madeIn;
public CarProperties(String cost, String madeIn) {
this.cost = cost;
this.madeIn = madeIn;
}
@Override
public String getCost() {
return this.cost;
}
@Override
public String getMadeIn() {
return this.madeIn;
}
}
//Singleton that will be provide Car's properties, that will be defined at applicationContext loading.
public final class CarEnumerationInitializer {
private static CarEnumerationInitializer INSTANCE;
private static ICarProperties NANO;
private static ICarProperties MERCEDES;
private static ICarProperties FERRARI;
private CarEnumerationInitializer(ICarProperties nano,
ICarProperties mercedes, ICarProperties ferrari) {
CarEnumerationInitializer.NANO = nano;
CarEnumerationInitializer.MERCEDES = mercedes;
CarEnumerationInitializer.FERRARI = ferrari;
}
public static void forbidInvocationOnUnsetInitializer() {
if (CarEnumerationInitializer.INSTANCE == null) {
throw new IllegalStateException(CarEnumerationInitializer.class
.getName()
+ " unset.");
}
}
public static CarEnumerationInitializer build(CarProperties nano,
CarProperties mercedes, CarProperties ferrari) {
if (CarEnumerationInitializer.INSTANCE == null) {
CarEnumerationInitializer.INSTANCE = new CarEnumerationInitializer(
nano, mercedes, ferrari);
}
return CarEnumerationInitializer.INSTANCE;
}
public static ICarProperties getNANO() {
forbidInvocationOnUnsetInitializer();
return NANO;
}
public static ICarProperties getMERCEDES() {
forbidInvocationOnUnsetInitializer();
return MERCEDES;
}
public static ICarProperties getFERRARI() {
forbidInvocationOnUnsetInitializer();
return FERRARI;
}
}
Finally, the applicationContext definition :
最后,applicationContext 定义:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="nano" class="be.vinkolat.poc.core.car.CarProperties">
<constructor-arg type="java.lang.String" value="Cheap"></constructor-arg>
<constructor-arg type="java.lang.String" value="India"></constructor-arg>
</bean>
<bean id="mercedes"
class="be.vinkolat.poc.core.car.CarProperties">
<constructor-arg type="java.lang.String" value="Expensive"></constructor-arg>
<constructor-arg type="java.lang.String" value="Germany"></constructor-arg>
</bean>
<bean id="ferrari" class="be.vinkolat.poc.core.car.CarProperties">
<constructor-arg type="java.lang.String"
value="Very Expensive">
</constructor-arg>
<constructor-arg type="java.lang.String" value="Italy"></constructor-arg>
</bean>
<bean id="carInitializer"
class="be.vinkolat.poc.core.car.CarEnumerationInitializer"
factory-method="build" lazy-init="false">
<constructor-arg type="be.vinkolat.poc.core.car.CarProperties"
ref="nano" />
<constructor-arg type="be.vinkolat.poc.core.car.CarProperties"
ref="mercedes" />
<constructor-arg type="be.vinkolat.poc.core.car.CarProperties"
ref="ferrari" />
</bean>
</beans>
It works, but there is one major weakness : CarEnumerationInitializer
MUST be instantiated BEFORE any reference is made to Car
enumeration, otherwise CarProperties are null, meaning that Car's properties can't be set when Car
is loaded (hence the IllegalStateException
thrown, to at least make it crashes in a predictable and documentated way). carInitializer
bean's property lazy-init
set to an explicit false
, to put emphasis on the need to load it as soon as possible.
I would say it may be useful in a simple application, one where you can easely guess where a first call to Car
will be made. For a larger one, it will probably be such a clutter that I didn't encourage you to use it.
它有效,但有一个主要弱点:CarEnumerationInitializer
必须在对Car
枚举进行任何引用之前实例化,否则 CarProperties 为空,这意味着在Car
加载时无法设置 Car 的属性(因此IllegalStateException
抛出,至少使其崩溃一种可预测和记录在案的方式)。carInitializer
bean 的属性lazy-init
设置为一个明确的false
,以强调需要尽快加载它。我想说它在一个简单的应用程序中可能很有用,在一个简单的应用程序中,您可以轻松猜测第一次调用Car
将在哪里进行。对于较大的,它可能会非常混乱,我不鼓励您使用它。
Hope this help, comments and vote (up and down) very welcome :) I'll wait for a few days to make this one the accepted answer, to let you react.
希望这些帮助,评论和投票(上下)非常受欢迎:) 我会等几天让这个成为公认的答案,让你做出反应。
回答by mP.
Attempting to mutate an Enum is well silly and goes completely against their design objectives. An enum by definition represents a distinct value within a group. If you ever need more / less values you will need to update the source. While you can change an enums state by adding setters (after all they are just objects) your hacking the system.
试图改变一个 Enum 是很愚蠢的,完全违背了他们的设计目标。根据定义,枚举表示组内的不同值。如果您需要更多/更少的值,则需要更新源。虽然您可以通过添加 setter(毕竟它们只是对象)来更改枚举状态,但您可以入侵系统。
回答by Jason
You can't create new enum values via Spring, they must be declared in the class. However, since the enum values will be singletons anyway (created by the JVM), any configurations that should be set, or services to be injected, can be done via invoking static methods in the enum class:
您不能通过 Spring 创建新的枚举值,它们必须在类中声明。然而,由于枚举值无论如何都是单例的(由 JVM 创建),任何应该设置的配置或要注入的服务都可以通过调用枚举类中的静态方法来完成:
回答by Danubian Sailor
You can use Enumclass as factory bean. Example: setting serializationInclusionfield with enum value:
您可以使用Enum类作为工厂 bean。示例:使用枚举值设置serializationInclusion字段:
<property name="serializationInclusion">
<bean class="org.codehaus.Hymanson.map.annotate.JsonSerialize.Inclusion" factory-method="valueOf">
<constructor-arg>
<value>NON_NULL</value>
</constructor-arg>
</bean>
</property>
But actually (Spring 3.1) a simpler solution works: you just write the enum value and Spring recognizes what to do:
但实际上(Spring 3.1)有一个更简单的解决方案:您只需编写枚举值,Spring 就会识别要做什么:
<property name="serializationInclusion" value="NON_NULL"/>