복붙노트

[SPRING] Spring IoC를 사용하여 enum 값 설정하기

SPRING

Spring IoC를 사용하여 enum 값 설정하기

생성시 Spring IoC를 통해 이러한 enum 값을 설정하는 방법이 있습니까?

내가 뭘하고 싶은데, 클래스 로딩 시간에 아래 코드 스 니펫에 하드 코딩 된 값을 삽입하는 것입니다.

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;
        }

}

응용 프로그램을 Nanos가 "거의 무료"인 독일이나 Ferraris가 "Unaffordable"인 인도에 배포해야한다고 가정 해 봅시다. 두 나라 모두 3 개의 자동차 (결정론 집합)가 존재하며 더 이상 열거가 없으므로 열거 형이지만 내부 값은 다를 수 있습니다. 그래서 이것은 무형의 문맥 초기화의 경우입니다.

해결법

  1. ==============================

    1.enum 자체를 설정한다는 의미입니까?

    enum 자체를 설정한다는 의미입니까?

    나는 그것이 가능하다고 생각하지 않는다. 열거 형은 정적 특성을 가지고 있기 때문에 인스턴스화 할 수 없습니다. 그래서 스프링 IoC는 열거 형을 만들 수 없다고 생각한다.

    다른 한편, 열거 형을 사용하여 초기화 할 필요가있는 경우 Spring IoC 장을 확인하십시오. (열거 형 검색) 사용할 수있는 간단한 예제가 있습니다.

  2. ==============================

    2.나는 이것이 Spring의 ApplicationContext 설정으로 할 수 있다고 생각하지 않는다. 그러나 Spring에 의해 실제로 필요합니까, 아니면 ResourceBundle을 사용하여 간단한 외부화를 해결할 수 있습니까? 이렇게 :

    나는 이것이 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(...);
    
    }
    

    특성 파일에서 각 특정 로케일에 대해 하나씩, 가능한 내부 enum 값을 설명하는 키를 입력하십시오.

    Car.NANO.cost=Very cheap
    Car.NANO.madeIn=India
    Car.MERCEDES.cost=Expensive
    ...
    

    이 접근법의 유일한 단점은 Java 코드에서 문자열로 열거 형 필드 (cost, madeIn)의 이름을 반복해야한다는 것입니다. 편집 : 그리고 더하기 측면에서 모든 열거 형의 모든 속성을 언어 / 로캘 당 하나의 속성 파일로 스택 할 수 있습니다.

  3. ==============================

    3.좋아, 아주 까다 롭지 만 할 수있다.

    좋아, 아주 까다 롭지 만 할 수있다.

    Spring이 enum을 인스턴스화 할 수 없다는 것은 사실입니다. 하지만 그건 문제가 아닙니다. Spring은 또한 factory 메소드를 사용할 수 있습니다.

    이것이 핵심 구성 요소입니다.

    public class EnumAutowiringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
        private final List<Class<? extends Enum>> enumClasses = new ArrayList<>();
    
        public EnumAutowiringBeanFactoryPostProcessor(Class<? extends Enum>... enumClasses) {
            Collections.addAll(this.enumClasses, enumClasses);
        }
    
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
            for (Class<? extends Enum> enumClass : enumClasses) {
                for (Enum enumVal : enumClass.getEnumConstants()) {
                    BeanDefinition def = new AnnotatedGenericBeanDefinition(enumClass);
                    def.setBeanClassName(enumClass.getName());
                    def.setFactoryMethodName("valueOf");
                    def.getConstructorArgumentValues().addGenericArgumentValue(enumVal.name());
                    ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(enumClass.getName() + "." + enumVal.name(), def);
                }
            }
        }
    }
    

    그런 다음 다음 테스트 클래스는 작동하는지 보여줍니다.

    @Test
    public class AutowiringEnumTest {
    
        public void shouldAutowireEnum() {
            new AnnotationConfigApplicationContext(MyConig.class);
    
            assertEquals(AutowiredEnum.ONE.myClass.field, "fooBar");
            assertEquals(AutowiredEnum.TWO.myClass.field, "fooBar");
            assertEquals(AutowiredEnum.THREE.myClass.field, "fooBar");
        }
    
        @Configuration
        public static class MyConig {
    
            @Bean
            public MyClass myObject() {
                return new MyClass("fooBar");
            }
    
            @Bean
            public BeanFactoryPostProcessor postProcessor() {
                return new EnumAutowiringBeanFactoryPostProcessor(AutowiredEnum.class);
            }
        }
    
        public enum AutowiredEnum {
            ONE,
            TWO,
            THREE;
    
            @Resource
            private MyClass myClass;
    
        }
    
        public static class MyClass {
    
            private final String field;
    
            public MyClass(String field) {
                this.field = field;
            }
       }
    
    }
    
  4. ==============================

    4.Spring을 통해 새로운 enum 값을 생성 할 수 없으며 클래스에서 선언해야합니다. 그러나 열거 형 값은 어쨌든 (JVM에 의해 생성 된) 싱글 톤이 될 것이므로, 설정해야하는 모든 구성 또는 주입 할 서비스는 enum 클래스에서 정적 메서드를 호출하여 수행 할 수 있습니다.

    Spring을 통해 새로운 enum 값을 생성 할 수 없으며 클래스에서 선언해야합니다. 그러나 열거 형 값은 어쨌든 (JVM에 의해 생성 된) 싱글 톤이 될 것이므로, 설정해야하는 모든 구성 또는 주입 할 서비스는 enum 클래스에서 정적 메서드를 호출하여 수행 할 수 있습니다.

    http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/beans/factory/config/MethodInvokingFactoryBean.html

  5. ==============================

    5.문자열을 취하는 setter (또는 생성자 인수)를 제공하지 말고 단순히 Enum.valueOf (String s)를 호출하여 String에서 enum으로 변환하십시오. 이것이 실패하고 Spring 초기화가 빠지면 ​​예외가 던져 질 것이다.

    문자열을 취하는 setter (또는 생성자 인수)를 제공하지 말고 단순히 Enum.valueOf (String s)를 호출하여 String에서 enum으로 변환하십시오. 이것이 실패하고 Spring 초기화가 빠지면 ​​예외가 던져 질 것이다.

  6. ==============================

    6.나는 다음과 같이 그것을했다 :

    나는 다음과 같이 그것을했다 :

    @Component
    public class MessageSourceHelper {
        @Inject
        public MessageSource injectedMessageSource;
    
        public static MessageSource messageSource;
    
        public static String getMessage(String messageKey, Object[] arguments, Locale locale) {
            return messageSource.getMessage(messageKey, arguments, locale);
        }
    
        @PostConstruct
        public void postConstruct() {
            messageSource = injectedMessageSource;
        }
    

    }

    그렇게하면 다음과 같은 방법으로 열거 형에서 메시지를 쉽게 사용할 수 있습니다.

    MessageSourceHelper.getMessage(key, arguments, locale);
    
  7. ==============================

    7.무엇을 설치해야합니까? 클래스가로드 될 때 값이 생성되고 열거 형이므로 소스에 값을 추가하고 다시 컴파일하지 않는 한 다른 값을 만들 수 없습니다.

    무엇을 설치해야합니까? 클래스가로드 될 때 값이 생성되고 열거 형이므로 소스에 값을 추가하고 다시 컴파일하지 않는 한 다른 값을 만들 수 없습니다.

    명시 적 범위의 불변 값으로 유형을 제한 할 수 있도록하는 것이 바로 enum입니다. 이제 코드의 어느 위치에서든 Car 또는 그 값인 Car.NANO, Car.MERCEDES 등을 참조 할 수 있습니다.

    반면에 명시적인 범위가 아닌 값 집합이 있고이 유형의 임의 개체를 만들려면 게시물과 동일한 ctor를 사용하지만 일반 클래스, enum 클래스가 아닙니다. 그런 다음 Spring은 다양한 소스 (XML 파일, 설정 파일 등)의 값을 읽고 해당 유형의 목록을 만드는 다양한 헬퍼 클래스를 제공합니다.

  8. ==============================

    8.

    <bean id="car" class="Foo">
        <property name="carString" value="NANO" />
    </bean>
    

    그리고 Foo 클래스에서이 세터를 가질 수 있습니다.

    public void setCar(String carString) {
        this.carString = Car.valueOf(carString);
    }
    
  9. ==============================

    9.열거 형을 돌연변이하려는 시도는 어리석은 짓이며 설계 목표와 완전히 상반됩니다. 정의에 의한 열거 형은 그룹 내의 고유 한 값을 나타냅니다. 값이 더 많거나 적게 필요하면 소스를 업데이트해야합니다. setter를 추가하여 열거 형 상태를 변경할 수 있지만 (결국은 객체 일뿐입니다) 시스템을 해킹 할 수 있습니다.

    열거 형을 돌연변이하려는 시도는 어리석은 짓이며 설계 목표와 완전히 상반됩니다. 정의에 의한 열거 형은 그룹 내의 고유 한 값을 나타냅니다. 값이 더 많거나 적게 필요하면 소스를 업데이트해야합니다. setter를 추가하여 열거 형 상태를 변경할 수 있지만 (결국은 객체 일뿐입니다) 시스템을 해킹 할 수 있습니다.

  10. ==============================

    10.좋아, 이건 좀 복잡해.하지만 당신은 그것을 통합하는 방법을 찾을 수 있습니다. 열거 형은 런타임시 변경되지 않으므로 반성 해킹입니다. 죄송 합니다만 Spring 구현 부분이 없지만 enum 클래스 나 객체를 가져올 빈과 새로운 값이나 값이 될 다른 필드를 만들 수 있습니다.

    좋아, 이건 좀 복잡해.하지만 당신은 그것을 통합하는 방법을 찾을 수 있습니다. 열거 형은 런타임시 변경되지 않으므로 반성 해킹입니다. 죄송 합니다만 Spring 구현 부분이 없지만 enum 클래스 나 객체를 가져올 빈과 새로운 값이나 값이 될 다른 필드를 만들 수 있습니다.

    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());
    

    이것은이 사이트에서 가져온 것입니다.

  11. ==============================

    11.여기에 내가 온 솔루션 (Javashlook 덕분에 답장을 받았다)이다. 그것은 작동하지만, 아마도 그것을 생산 수준의 방법이 아닙니다.

    여기에 내가 온 솔루션 (Javashlook 덕분에 답장을 받았다)이다. 그것은 작동하지만, 아마도 그것을 생산 수준의 방법이 아닙니다.

    하지만 천 단어가 넘는 코드는 여기 있습니다. 나는 스스로 판단 할 수있게 할 것입니다.

    개정 된 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();
        }
    }
    

    그리고 여기에 "배관공"수업이 있습니다 :

    //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;
        }
    }
    

    마지막으로 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>
    

    CarEnumerationInitializer는 Car enumeration에 대한 참조가 만들어지기 전에 인스턴스화되어야합니다. 그렇지 않으면 CarProperties가 null입니다. Car가로드 될 때 Car 속성을 설정할 수 없음을 의미합니다 (따라서 IllegalStateException이 발생했습니다. 예측 가능하고 문서화 된 방식으로 충돌 시키십시오.) carInitializer 빈의 lazy-init 속성은 가능한 한 빨리로드 할 필요성에 중점을두고 명시 적 false로 설정됩니다. 간단한 응용 프로그램에서 유용 할 수 있습니다. 자동차에 대한 첫 번째 호출이 어디에서 발생 하는지를 easely 추측 할 수 있습니다. 더 큰 경우, 아마도 당신이 그것을 사용하도록 권장하지 않은 혼란 것입니다.

    희망이 도움말, 의견과 투표 (위아래로) 매우 환영 :) 나는 당신이 반응하게 하자이 답변을 만들기 위해 며칠을 기다릴께.

  12. ==============================

    12.Enum 클래스를 팩토리 빈으로 사용할 수 있습니다. 예 : enum 값으로 serializationInclusion 필드 설정 :

    Enum 클래스를 팩토리 빈으로 사용할 수 있습니다. 예 : enum 값으로 serializationInclusion 필드 설정 :

                <property name="serializationInclusion">
                    <bean class="org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion" factory-method="valueOf">
                        <constructor-arg>   
                            <value>NON_NULL</value>
                        </constructor-arg>
                    </bean>
                </property>
    

    그러나 실제로 (Spring 3.1) 더 간단한 솔루션이 작동합니다 : 단지 enum 값을 쓰고 Spring은 무엇을 해야할지 인식합니다 :

    <property name="serializationInclusion" value="NON_NULL"/>
    
  13. from https://stackoverflow.com/questions/710392/using-spring-ioc-to-set-up-enum-values by cc-by-sa and MIT license