런타임에 Spring bean 정의를 대체할 수 있습니까?
다음 시나리오를 생각해 봅니다.수 있어야 Spring . 생각해 .을할수야는이는링션다가다가션링i는nhtkeaagecn을 .DataSource아니면MailSender빈으로 되며, 은 의 됩니다 으로 됩니다 으로 의 은 configuration.
이제 관리자가 전자 메일 주소나 데이터베이스 URL과 같은 구성 값을 변경할 수 있으며, 실행 시 구성된 빈을 다시 초기화하려고 합니다.
위의 구성 가능한 빈의 속성을 단순히 수정할 수는 없다고 가정합니다(예: 에 의해 생성됨).FactoryBean또는 시공자 주입)을 사용하지만 콩 자체를 재생성해야 합니다.
이것을 어떻게 달성할 것인지에 대한 생각이 있습니까?전체 구성도 어떻게 구성해야 하는지 조언을 받게 되어 기쁩니다.정해진게 없습니다. :-)
편집
좀 더 명확하게 설명하자면:구성을 업데이트하는 방법이나 정적 구성 값을 주입하는 방법을 묻는 것이 아닙니다.예를 들어 보겠습니다.
<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>
콩이 constructorInjectedBean건설자 주입을 사용하는 것입니다. 것이 사항이 .DataSource.
될 마다 ( 가 은 이 될 마다 을 을 )를 통해)configurationService콩,알constructorInjectedBean재생성되어 애플리케이션 컨텍스트와 의존 콩에 재투입되고 있습니다.
우리는 안전하게 추정할 수 있습니다.constructorInjectedBean인터페이스를 사용하고 있기 때문에 프록시 매직은 정말 옵션입니다.
저는 질문을 조금 더 명확하게 했으면 좋겠습니다.
이전에 수행한 방법은 다음과 같습니다. 구성에 따라 변경할 수 있는 서비스를 신속하게 실행하면 라이프사이클 인터페이스가 구현됩니다.새로 고침 가능:
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 항목(구성 개체의 이름 제공)으로 브로드캐스트되는 구성 요소를 수정할 수 있는 컨트롤러(또는 서비스).메시지 구동 빈은 IR Refreshable을 구현하는 모든 빈에서 IR Refreshable 인터페이스 계약을 호출합니다.
스프링의 좋은 점은 새로 고침이 필요한 애플리케이션 컨텍스트에서 모든 서비스를 자동으로 감지할 수 있으므로 이러한 서비스를 명시적으로 구성할 필요가 없다는 것입니다.
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 항목으로 브로드캐스트할 수 있습니다.
저는 '홀더빈' 방식(본질은 데코레이터)을 생각할 수 있는데, 홀더빈은 홀더빈에게 위임하고, 다른 원두에 종속적으로 주입하는 홀더빈입니다.소지자 말고는 아무도 소지자를 언급하지 않습니다.이제 홀더 빈의 구성이 변경되면 이 새로운 구성으로 홀더를 재생성하고 홀더 빈에 위임하기 시작합니다.
JMX를 한 번 살펴보셔야 하는데, 봄도 이를 뒷받침해줍니다.
스크립트로 작성된 빈을 다루도록 추가 업데이트된 답변
스프링 2.5.x+에서 지원되는 또 다른 접근 방식은 스크립트 빈입니다.스크립트에 다양한 언어를 사용할 수 있습니다. BeanShell은 Java와 동일한 구문을 사용한다는 점을 고려할 때 가장 직관적일 수 있지만 일부 외부 의존성이 필요합니다.하지만 그 예는 그루비에 있습니다.
Spring Documentation의 섹션 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 컨텍스트 내의 값을 업데이트하고 꺼집니다.
문서에는 이 기법이 건설자 주입에 효과가 없다고 나와 있지만, 아마도 당신은 이를 해결할 수 있을 것입니다.
동적 속성 변경을 포함하도록 답변 업데이트
전체 소스 코드를 제공하는 이 기사에서 인용하는 한 가지 접근 방식은 다음과 같습니다.
* a factory bean that detects file system changes * an observer pattern for Properties, so that file system changes can be propagated * a property placeholder configurer that remembers where which placeholders were used, and updates singleton beans’ properties * a timer that triggers the regular check for changed files관찰자 패턴은 인터페이스와 클래스 ReloadableProperties, ReloadablePropertiesListener, PropertiesReloadedEvent, ReloadablePropertiesBase로 구현됩니다.그들 중 어떤 것도 특별히 흥미로운 것은 없고, 그저 평범한 청취자의 대처일 뿐입니다.DelegatingProperties 클래스는 속성이 업데이트될 때 현재 속성을 투명하게 교환하는 역할을 합니다.전체 속성 맵을 한 번에 업데이트하기만 하면 애플리케이션이 일관되지 않은 중간 상태를 방지할 수 있습니다(자세한 내용은 나중에 확인).
이제 PropertiesFactoryBean을 작성하여 PropertiesBean처럼 Properties 인스턴스 대신 Reloadable Properties 인스턴스를 만들 수 있습니다.메시지가 나타나면 RPFB는 파일 수정 시간을 확인하고 필요한 경우 Reloadable Properties를 업데이트합니다.관찰자 패턴 기계를 트리거합니다.
당사의 경우 수신기는 Property Placeholder Configurer뿐입니다.플레이스홀더의 모든 사용을 추적한다는 점을 제외하고는 표준 스프링 PropertyPlaceholderConfigurer과 동일하게 동작합니다.이제 속성을 다시 로드하면 각 수정된 속성의 모든 용도가 발견되고 싱글톤 콩의 속성이 다시 할당됩니다.
정적 속성 변경을 다루는 아래의 원래 답변:
당신은 당신의 봄 맥락에 외부적인 특성을 주입하고 싶어하는 것처럼 들립니다.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 파일은 관리자 및 응용 프로그램을 실행하는 사용자만 액세스할 수 있는지 확인합니다.
application.properties 예제:
password=Aardvark
또는 이와 유사한 질문에서 나온 접근 방식을 사용할 수도 있고, 따라서 제 해결책도 사용할 수 있습니다.
접근 방식은 속성 파일을 통해 구성된 콩을 갖는 것이며 해결책은 다음 중 하나입니다.
- 속성이 변경된 경우 전체 applicationContext(예약된 작업을 자동으로 사용하거나 JMX를 수동으로 사용)를 새로 고칩니다.
- 모든 속성에 액세스하려면 전용 속성 제공자 개체를 사용합니다.이 속성 공급자는 수정을 위해 속성 파일을 계속 확인합니다.프로토타입 기반 속성 조회가 불가능한 원두의 경우 속성 공급자가 업데이트된 속성 파일을 찾을 때 발생하는 사용자 지정 이벤트를 등록합니다.복잡한 생명주기를 가진 콩들은 그 행사에 귀를 기울이고 그들 자신을 새롭게 해야 할 필요가 할 것입니다.
ApplicationContext 이라는 "reconfigurable" 사용자 정의 있습니다 범위를 만들 수 에 있습니다 수 만들 applic 범위를 사용자 정의 이라는 " " re ation con이 범위의 모든 콩의 인스턴스를 생성하고 캐싱합니다.구성 변경 시 캐시를 지우고 새 구성으로 처음 액세스할 때 빈을 다시 만듭니다.하고 Spring-EL을. map continue는이을면성든를의위한로고다여야성에을prta다야에ed성여pouo,ef이nsksoeltgpsyhdapsgd-d:을g config하고 ApplicationContext와 같은 .#{ config['key'] }.
이것은 제가 시도한 것이 아니라, 조언을 제공하려고 하는 것입니다.
응용프로그램 컨텍스트가 AbstractRefreshable ApplicationContext의 하위 클래스라고 가정합니다(예: XmlWebApplicationContext, ClassPathXmlApplicationContext).AbstractRefreshableApplicationContext.getBeanFactory()는 ConfigurableListableBeanFactory의 인스턴스를 제공합니다.BeanDefinition Registry의 인스턴스인지 확인합니다.그렇다면 'register BeanDefinition' 메서드를 호출할 수 있습니다.이 접근 방식은 스프링 구현과 긴밀하게 결합될 것입니다.
AbstractRefreshable ApplicationContext 및 DefaultListableBeanFactory 코드 확인('AbstractRefreshableApplicationContext getBeanFactory()'를 호출하면 나타나는 구현입니다)
옵션 1:
- 를 합니다.
configurable다에DataSource아니면MailSender. 구성 빈에서 항상 구성 가능한 값을 가져와야 합니다. - 안에
configurable외부에서 구성 가능한 속성(파일 등)을 주기적으로 읽기 위해 스레드를 실행합니다.이런 식으로.configurable관리자가 속성을 변경한 후에 콩은 그 자체로 새로워질 것입니다. 그래서DataSource는 업데이트된 값을 자동으로 가져옵니다.- "시스템"을 실제로 구현할 필요는 없습니다. http://commons.apache.org/configuration/userguide/howto_filebased.html#Automatic_Reloading
옵션 2(불량일 수도 있지만 아닐 수도 있음 - 사용 사례에 따라 다름):
- 항상 새로운 종류의 콩을 만들기.
DataSource/MailSender- 사용하는prototype스코프. 콩의 시작 부분에서 속성을 새로 읽습니다.
옵션 3 : JMX 사용에 대한 @mR_fr0g 제안은 나쁘지 않을 것 같습니다.할 수 있는 일은 다음과 같습니다.
- 구성 bean을 MBean으로 표시(http://static.springsource.org/spring/docs/2.5.x/reference/jmx.html) 읽기)
- 관리자에게 MBean의 구성 속성을 변경하도록 요청(또는 원본에서 속성 업데이트를 트리거할 수 있는 인터페이스를 제공)
- 이 MBean(새 자바 코드를 작성해야 함)은 Beans(변경된 속성을 변경/주입하려는 것)의 참조를 유지해야 합니다.이는 간단해야 합니다(빈 이름/클래스의 setter 주입 또는 런타임 가져오기를 통해).
- MBean의 속성이 변경(또는 트리거)되면, MBean은 각각의 bean에서 해당 설정자를 호출해야 합니다.그러면 기존 코드가 변경되지 않고 런타임 속성 변경을 관리할 수 있습니다.
HTH!
런타임에 Spring 기반 응용프로그램에 프로그램 방식으로 액세스할 수 있는 플러그형 구성요소를 Spring Inspector에서 확인할 수 있습니다.Javascript를 사용하여 구성을 변경하거나 런타임에 응용프로그램 동작을 관리할 수 있습니다.
다음은 속성의 사용을 추적하고 구성 변경이 발생할 때마다 변경하는 플레이스홀더 구성자를 직접 작성하는 좋은 아이디어입니다.이는 두 가지 단점이 있습니다.
- 특성 값의 생성자 주입에서는 작동하지 않습니다.
- 재구성된 bean이 일부 작업을 처리하는 동안 변경된 구성을 수신하면 레이스 조건을 얻을 수 있습니다.
제 해결책은 원래의 물체를 복사하는 것이었습니다.피스트는 인터페이스를 만들었습니다.
/**
* 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가 조금이나마 도움이 될 수 있도록 합니다.그러면 같은 기능을 사용하는 copy constructor를 만들 수 있습니다.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이며, 이 값은의된을다질이다nh질을s의된이ede, .newSettingsData이 을 사용하면 높은 할 수 . 이 방법을 사용하면 높은 복잡성 없이 콩을 업데이트할 수 있습니다.
언급URL : https://stackoverflow.com/questions/4041300/can-i-replace-a-spring-bean-definition-at-runtime
'programing' 카테고리의 다른 글
| 데이터가 존재하는 행 수 (0) | 2023.09.20 |
|---|---|
| Python: Excel 2007+ 파일(.xlsx 파일)에 쓰기 (0) | 2023.09.15 |
| UIAparance Proxy를 통해 설정할 수 있는 속성은 무엇입니까? (0) | 2023.09.15 |
| malloc 구현? (0) | 2023.09.15 |
| WooCommerce에서 카트 항목을 동일한 제품 범주에 속하도록 제한 (0) | 2023.09.15 |