복붙노트

[SPRING] 런타임시 Spring bean 정의를 대체 할 수 있습니까?

SPRING

런타임시 Spring bean 정의를 대체 할 수 있습니까?

다음 시나리오를 고려하십시오. 속성을 구성 할 수있는 bean을 가진 Spring 응용 프로그램 컨텍스트가 있고 DataSource 또는 MailSender라고 생각합니다. 변경 가능한 응용 프로그램 구성은 별도의 bean에 의해 관리되며 구성이라고 부르기로합니다.

관리자는 이메일 주소 나 데이터베이스 URL과 같은 구성 값을 변경할 수 있으며 런타임에 구성된 Bean을 다시 초기화하고 싶습니다.

위의 구성 가능한 bean (예 : FactoryBean 또는 생성자 주입으로 작성)의 특성을 간단히 수정할 수는 없지만 bean 자체를 다시 작성해야한다고 가정하십시오.

이것을 달성하는 방법에 대한 생각? 전체 구성 작업을 구성하는 방법에 대한 조언을 얻게되어 기쁩니다. 아무것도 수정되지 않았습니다. :-)

편집하다

조금 더 명확하게하기 : 설정을 업데이트하는 방법이나 정적 설정 값을 삽입하는 방법을 묻지는 않습니다. 예를 들어 보겠습니다.

<beans>
    <util:map id="configuration">
        <!-- initial configuration -->
    </util:map>

    <bean id="constructorInjectedBean" class="Foo">
        <constructor-arg value="#{configuration['foobar']}" />
    </bean>

    <bean id="configurationService" class="ConfigurationService">
        <property name="configuration" ref="configuration" />
    </bean>
</beans>

그래서 생성자 삽입을 사용하는 bean constructorInjectedBean이 있습니다. 빈의 구성이 매우 비싸서 프로토 타입 범위 나 공장 프록시를 사용할 수 없다고 생각해보십시오. DataSource를 생각해보십시오.

내가하고 싶은 일은 구성이 업데이트 될 때마다 (configurationService를 통해 bean constructorInjectedBean이 재 작성되어 응용 프로그램 컨텍스트와 종속 Bean에 다시 주입된다는 것입니다.

우리는 constructorInjectedBean이 인터페이스를 사용한다고 가정 할 수 있으므로 프록시 마법은 실제로 옵션입니다.

나는이 질문을 조금 더 명확하게하기를 희망한다.

해결법

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

    1.홀더 빈이 위임자에게 위임하는 '홀더 빈'접근법 (본질적으로 데코레이터)을 생각해 볼 수 있습니다. 홀더 빈은 다른 빈에 종속 관계로 주입됩니다. 아무도 holdee 그러나 홀더에 참고가있다. 이제 홀더 빈의 config가 변경되면,이 새 config로 holdee를 다시 만들고 위임을 시작합니다.

    홀더 빈이 위임자에게 위임하는 '홀더 빈'접근법 (본질적으로 데코레이터)을 생각해 볼 수 있습니다. 홀더 빈은 다른 빈에 종속 관계로 주입됩니다. 아무도 holdee 그러나 홀더에 참고가있다. 이제 홀더 빈의 config가 변경되면,이 새 config로 holdee를 다시 만들고 위임을 시작합니다.

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

    2.여기에 내가 과거에 해왔 던 방법이있다 : 즉석에서 변경할 수있는 구성에 의존하는 실행 서비스는 라이프 사이클 인터페이스를 구현한다. IRefreshable :

    여기에 내가 과거에 해왔 던 방법이있다 : 즉석에서 변경할 수있는 구성에 의존하는 실행 서비스는 라이프 사이클 인터페이스를 구현한다. IRefreshable :

    public interface IRefreshable {
      // Refresh the service having it apply its new values.
      public void refresh(String filter);
    
      // The service must decide if it wants a cache refresh based on the refresh message filter.
      public boolean requiresRefresh(String filter);
    }
    

    구성이 변경된 JMS 항목으로 브로드 캐스트하는 구성을 수정할 수있는 컨트롤러 (또는 서비스) (구성 객체의 이름 제공). 메시지 구동 Bean은 IRefreshable을 구현하는 모든 bean에서 IRefreshable 인터페이스 계약을 호출합니다.

    봄철 좋은 점은 응용 프로그램 컨텍스트에서 새로 고쳐야하는 서비스를 자동으로 감지하여 명시 적으로 구성해야하는 필요성을 제거한다는 것입니다.

    public class MyCacheSynchService implements InitializingBean, ApplicationContextAware {
     public void afterPropertiesSet() throws Exception {
      Map<String, ?> refreshableServices = m_appCtx.getBeansOfType(IRefreshable.class);
      for (Map.Entry<String, ?> entry : refreshableServices.entrySet() ) {
       Object beanRef = entry.getValue();
       if (beanRef instanceof IRefreshable) {
        m_refreshableServices.add((IRefreshable)beanRef);
       }
      }
     }
    }
    

    이 접근법은 많은 응용 프로그램 서버 중 하나가 구성을 변경할 수있는 클러스터 된 응용 프로그램에서 특히 잘 작동합니다. 변경 사항을 트리거하는 메커니즘으로 JMX를 사용하려는 경우 JMX Bean은 속성 중 하나가 변경되면 JMS 항목으로 브로드 캐스팅 할 수 있습니다.

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

    3.JMX를 살펴 봐야한다. Spring은 이것을 지원한다.

    JMX를 살펴 봐야한다. Spring은 이것을 지원한다.

  4. ==============================

    4.스크립트 된 빈을 커버하는 추가 답변

    스크립트 된 빈을 커버하는 추가 답변

    Spring 2.5.x +가 지원하는 또 다른 접근법은 스크립트 된 빈의 접근법이다. 스크립트에 다양한 언어를 사용할 수 있습니다. BeanShell은 Java와 동일한 구문을 사용하기 때문에 아마도 가장 직관적 일 수 있지만 외부 종속성이 필요합니다. 그러나 예제는 Groovy에 있습니다.

    Spring 문서의 Section 24.3.1.2에서는 이것을 설정하는 방법을 다루지 만, 상황에 좀 더 적절하게 적용하기 위해 편집 한 접근법을 보여주는 몇 가지 주요 발췌 내용이있다.

    <beans>
    
        <!-- This bean is now 'refreshable' due to the presence of the 'refresh-check-delay' attribute -->
        <lang:groovy id="messenger"
              refresh-check-delay="5000" <!-- switches refreshing on with 5 seconds between checks -->
              script-source="classpath:Messenger.groovy">
            <lang:property name="message" value="defaultMessage" />
        </lang:groovy>
    
        <bean id="service" class="org.example.DefaultService">
            <property name="messenger" ref="messenger" />
        </bean>
    
    </beans>
    

    Groovy 스크립트는 다음과 같이 보입니다 :

    package org.example
    
    class GroovyMessenger implements Messenger {
    
        private String message = "anotherProperty";
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message
        }
    }
    

    시스템 관리자가 변경하기를 원하면 사용자 (또는 사용자)가 스크립트의 내용을 적절하게 편집 할 수 있습니다. 이 스크립트는 배포 된 응용 프로그램의 일부가 아니며 알려진 파일 위치 (또는 시작할 때 표준 PropertyPlaceholderConfigurer를 통해 구성된 위치)를 참조 할 수 있습니다.

    이 예제는 Groovy 클래스를 사용하지만 간단한 속성 파일을 읽는 코드를 실행하는 클래스를 가질 수 있습니다. 그렇게하면 스크립트를 직접 편집하지 않고 단지 그것을 만져서 시간 소인을 변경합니다. 그러면 해당 동작에 의해 다시로드가 트리거되고 (업데이트 된) 속성 파일에서 새로 고치는 속성이 새로 고쳐지면서 마지막으로 Spring 컨텍스트 내의 값이 업데이트되고 사용자가 이동합니다.

    문서에서는 생성자 주입에이 기술이 작동하지 않는다고 지적하지만 어쩌면 해결할 수 있습니다.

    동적 속성 변경에 대한 답변이 업데이트되었습니다.

    전체 소스 코드를 제공하는이 기사에서 인용하는 한 가지 접근 방식은 다음과 같습니다.

    정적 속성 변경을 다루는 원본 답변은 다음과 같습니다.

    스프링 컨텍스트에 외부 속성을 삽입하려는 것처럼 들립니다. PropertyPlaceholderConfigurer는이 목적으로 설계되었습니다.

      <!-- Property configuration (if required) -->
      <bean id="serverProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
          <list>
            <!-- Identical properties in later files overwrite earlier ones in this list -->
            <value>file:/some/admin/location/application.properties</value>
          </list>
        </property>
      </bean>
    

    Ant 문법 플레이스 홀더를 사용하여 외부 속성을 참조한다 (Spring 2.5.5부터 필요하다면 중첩 될 수있다)

      <bean id="example" class="org.example.DataSource">
        <property name="password" value="${password}"/>
      </bean>
    

    그런 다음 application.properties 파일에 admin 사용자와 응용 프로그램을 실행하는 사용자 만 액세스 할 수 있는지 확인하십시오.

    예제 application.properties :

    암호 = Aardvark

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

    5.또는이 비슷한 질문과 그에 따른 해결책을 사용할 수도 있습니다.

    또는이 비슷한 질문과 그에 따른 해결책을 사용할 수도 있습니다.

    접근법은 속성 파일을 통해 구성된 빈을 가지며 솔루션은

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

    6.이것은 내가 시도한 것이 아니라 포인터를 제공하려고 노력하고있다.

    이것은 내가 시도한 것이 아니라 포인터를 제공하려고 노력하고있다.

    귀하의 응용 프로그램 컨텍스트가 AbstractRefreshableApplicationContext (예 : XmlWebApplicationContext, ClassPathXmlApplicationContext)의 하위 클래스라고 가정합니다. AbstractRefreshableApplicationContext.getBeanFactory ()는 ConfigurableListableBeanFactory 인스턴스를 제공합니다. BeanDefinitionRegistry 인스턴스인지 확인하십시오. 그렇다면 'registerBeanDefinition'메소드를 호출 할 수 있습니다. 이 접근법은 Spring 구현과 밀접하게 결합 될 것이며,

    AbstractRefreshableApplicationContext와 DefaultListableBeanFactory의 코드를 확인하십시오 (이것은 'AbstractRefreshableApplicationContext getBeanFactory ()'를 호출 할 때 얻을 수있는 구현입니다).

  7. ==============================

    7."reconfigurable"이라는 사용자 정의 범위를 ApplicationContext에 만들 수 있습니다. 이 범위에있는 모든 bean의 인스턴스를 작성하고 캐시합니다. 구성 변경시, 캐시를 지우고 새 구성으로 처음 액세스 할 때 Bean을 다시 작성합니다. 이것이 작동하려면 재구성 가능한 모든 빈 인스턴스를 AOP 범위 프록시로 랩핑하고 Spring-EL을 사용하여 구성 값에 액세스해야합니다. config라는 맵을 ApplicationContext에 넣고 # {config [ 'key']와 같은 구성에 액세스해야합니다. }.

    "reconfigurable"이라는 사용자 정의 범위를 ApplicationContext에 만들 수 있습니다. 이 범위에있는 모든 bean의 인스턴스를 작성하고 캐시합니다. 구성 변경시, 캐시를 지우고 새 구성으로 처음 액세스 할 때 Bean을 다시 작성합니다. 이것이 작동하려면 재구성 가능한 모든 빈 인스턴스를 AOP 범위 프록시로 랩핑하고 Spring-EL을 사용하여 구성 값에 액세스해야합니다. config라는 맵을 ApplicationContext에 넣고 # {config [ 'key']와 같은 구성에 액세스해야합니다. }.

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

    8.옵션 1 :

    옵션 1 :

    옵션 2 (좋지는 않다고 생각하지만 사용하지 않는 것일 수 있음) :

    옵션 3 : 제 생각에는 @mR_fr0g JMX 사용에 대한 제안은 좋지 않을 수도 있습니다. 당신이 할 수있는 일은 :

    HTH!

  9. ==============================

    9.런타임시 스프링 기반 애플리케이션에 프로그래밍 방식으로 액세스 할 수있는 플러그 인 (plug-gable) 컴포넌트 인 Spring Inspector를 살펴 보길 원할 것입니다. Javascript를 사용하여 런타임에 구성을 변경하거나 응용 프로그램 동작을 관리 할 수 ​​있습니다.

    런타임시 스프링 기반 애플리케이션에 프로그래밍 방식으로 액세스 할 수있는 플러그 인 (plug-gable) 컴포넌트 인 Spring Inspector를 살펴 보길 원할 것입니다. Javascript를 사용하여 런타임에 구성을 변경하거나 응용 프로그램 동작을 관리 할 수 ​​있습니다.

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

    10.속성의 사용법을 추적하고 구성 변경이 발생할 때마다 속성을 변경하는 자신 만의 PlaceholderConfigurer를 작성하는 좋은 아이디어가 있습니다. 하지만 두 가지 단점이 있습니다.

    속성의 사용법을 추적하고 구성 변경이 발생할 때마다 속성을 변경하는 자신 만의 PlaceholderConfigurer를 작성하는 좋은 아이디어가 있습니다. 하지만 두 가지 단점이 있습니다.

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

    11.내 솔루션은 원래 개체를 복사하는 것이 었습니다. 주먹이 인터페이스를 만들었습니다.

    내 솔루션은 원래 개체를 복사하는 것이 었습니다. 주먹이 인터페이스를 만들었습니다.

    /**
     * Allows updating data to some object.
     * Its an alternative to {@link Cloneable} when you cannot 
     * replace the original pointer. Ex.: Beans 
     * @param <T> Type of Object
     */
    public interface Updateable<T>
    {
        /**
         * Import data from another object
         * @param originalObject Object with the original data
         */
        public void copyObject(T originalObject);
    }
    

    함수의 구현을 쉽게하기 위해 주먹은 모든 필드를 가진 생성자를 생성한다. 그래서 IDE는 나를 조금 도와 줄 수있다. 그런 다음 동일한 함수 Updateable # copyObject (T originalObject)를 사용하는 복사 생성자를 만들 수 있습니다. 또한 IDE에서 만든 생성자 코드를 사용하여 구현할 함수를 만들 수 있습니다.

    public class SettingsDTO implements Cloneable, Updateable<SettingsDTO>
    {
        private static final Logger LOG = LoggerFactory.getLogger(SettingsDTO.class);
    
        @Size(min = 3, max = 30)  
        private String id;
    
        @Size(min = 3, max = 30)
        @NotNull 
        private String name;
    
        @Size(min = 3, max = 100)
        @NotNull 
        private String description;
    
        @Max(100)
        @Min(5) 
        @NotNull
        private Integer pageSize;
    
        @NotNull 
        private String dateFormat; 
    
        public SettingsDTO()
        { 
        }   
    
        public SettingsDTO(String id, String name, String description, Integer pageSize, String dateFormat)
        {
            this.id = id;
            this.name = name;
            this.description = description;
            this.pageSize = pageSize;
            this.dateFormat = dateFormat;
        }
    
        public SettingsDTO(SettingsDTO original)
        {
            copyObject(original);
        }
    
        @Override
        public void copyObject(SettingsDTO originalObject)
        {
            this.id = originalObject.id;
            this.name = originalObject.name;
            this.description = originalObject.description;
            this.pageSize = originalObject.pageSize;
            this.dateFormat = originalObject.dateFormat;
        } 
    }
    

    컨트롤러에서 앱의 현재 설정을 업데이트하는 데 사용했습니다.

            if (bindingResult.hasErrors())
            {
                model.addAttribute("settingsData", newSettingsData);
                model.addAttribute(Templates.MSG_ERROR, "The entered data has errors");
            }
            else
            {
                synchronized (settingsData)
                {
                    currentSettingData.copyObject(newSettingsData);
                    redirectAttributes.addFlashAttribute(Templates.MSG_SUCCESS, "The system configuration has been updated successfully");
                    return String.format("redirect:/%s", getDao().getPath());
                }
            }
    

    그래서 응용 프로그램의 구성을 가진 currentSettingsData는 newSettingsData에있는 업데이트 된 값을 가지게됩니다. 이 메소드를 사용하면 복잡성없이 빈을 갱신 할 수 있습니다.

  12. from https://stackoverflow.com/questions/4041300/can-i-replace-a-spring-bean-definition-at-runtime by cc-by-sa and MIT license