2015-01-17 9 views
11

일부 DB 변경시 Runtime (서버를 다시 시작하지 않음)에서 특정 bean을 다시 작성 (새 Object)하고 싶습니다. myShop 객체가 서버를 다시 시작하지 않는 한 컨텍스트를 새로하지 않는 싱글 &으로 스프링에 의해 생성 될 때이 코드는 실행되지 않습니다Java Spring 특정 Bean 다시 만들기

@Component 
public class TestClass { 

    @Autowired 
    private MyShop myShop; //to be refreshed at runtime bean 

    @PostConstruct //DB listeners 
    public void initializeListener() throws Exception { 
     //... 
     // code to get listeners config 
     //... 

     myShop.setListenersConfig(listenersConfig); 
     myShop.initialize(); 
    } 

    public void restartListeners() { 
     myShop.shutdownListeners(); 
     initializeListener(); 
    } 
} 

-이 보이는 방법이다. 새로 고침 (새 개체 만들기) myShop?

내가 생각할 수있는 한 가지 나쁜 방법은 restartListeners() 안에 새로운 myShop 개체를 만드는 것입니다.하지만 그건 나에게 맞는 것 같지 않습니다.

+0

beans 속성은 Bean Factory 사후 처리 단계에서로드됩니다. 특히 싱글 톤에서 다시로드하는 측면에서 그다지 많은 작업을 수행 할 수 없다고 생각하면 콩을로드 할 응용 프로그램 컨텍스트를 다시 작성해야합니다. 흥미로운 예가 있습니다. http://stackoverflow.com/questions/4084890/spring-replacing-the-bean-property-values-with-new-property-file-values – mariubog

답변

4

DefaultListableBeanFactory에서는 공개 메소드 destroySingleton ("beanName")을 사용할 수있게되어 있지만, 자동으로 실행되는 bean의 경우 autowired 된 객체의 동일한 인스턴스를 유지한다는 점에 유의해야합니다. 먼저, 당신은 이런 식으로 뭔가를 시도 할 수 있습니다 :

@RestController 
public class MyRestController { 

     @Autowired 
     SampleBean sampleBean; 

     @Autowired 
     ApplicationContext context; 
     @Autowired 
     DefaultListableBeanFactory beanFactory; 

     @RequestMapping(value = "/ ") 
     @ResponseBody 
     public String showBean() throws Exception { 

      SampleBean contextBean = (SampleBean) context.getBean("sampleBean"); 

      beanFactory.destroySingleton("sampleBean"); 

      return "Compare beans " + sampleBean + "==" 

    + contextBean; 

    //while sampleBean stays the same contextBean gets recreated in the context 
      } 

    } 

그것은 꽤 아니지만 당신이 그것을 접근하는 방법을 보여줍니다. 컴포넌트 클래스가 아닌 컨트롤러를 다루는 경우, 메소드 인자에 주사가있을 수 있으며, Bean은 메소드 내에서 필요할 때까지 다시 작성되지 않기 때문에 작동 할 것입니다. 적어도 그것이 어떻게 생겼는지는 알 수 있습니다. 흥미로운 질문은 콘텍스트에서 제거 되었기 때문에 원래 빈에 추가 된 객체 외에 다른 객체에 대한 참조를 가진 다른 객체가있을 것입니다. 콘트롤러에서 객체가 여전히 존재하거나 쓰레기 다듬을 수 있는지 궁금합니다. 위의 경우, 컨텍스트의 일부 다른 객체가 참조를 가진 경우 문제가 발생합니다.

1

동일한 유스 케이스가 있습니다. 이미 언급했듯이 런타임 중에 빈을 다시 만드는 주요한 문제 중 하나는 이미 삽입 된 참조를 업데이트하는 방법입니다. 이것은 주요 도전 과제를 제시합니다.

이 문제를 해결하기 위해 Java의 AtomicReference <> 클래스를 사용했습니다. 콩을 직접 주입하는 대신 AtomicReference로 포장 한 다음 주입했습니다. AtomicReference에 의해 래핑 된 객체는 스레드 안전 방식으로 재설정 될 수 있으므로 데이터베이스 변경이 감지 될 때 기본 객체를 변경하는 데이 객체를 사용할 수 있습니다. 다음은이 패턴의 설정/사용 예입니다.

@Configuration 
public class KafkaConfiguration { 

    private static final String KAFKA_SERVER_LIST = "kafka.server.list"; 
    private static AtomicReference<String> serverList; 

    @Resource 
    MyService myService; 

    @PostConstruct 
    public void init() { 
     serverList = new AtomicReference<>(myService.getPropertyValue(KAFKA_SERVER_LIST)); 
    } 

    // Just a helper method to check if the value for the server list has changed 
    // Not a big fan of the static usage but needed a way to compare the old/new values 
    public static boolean isRefreshNeeded() { 

     MyService service = Registry.getApplicationContext().getBean("myService", MyService.class);  
     String newServerList = service.getPropertyValue(KAFKA_SERVER_LIST); 

     // Arguably serverList does not need to be Atomic for this usage as this is executed 
     // on a single thread 
     if (!StringUtils.equals(serverList.get(), newServerList)) { 
      serverList.set(newServerList); 
      return true; 
     } 

     return false; 
    } 

    public ProducerFactory<String, String> kafkaProducerFactory() { 

     Map<String, Object> configProps = new HashMap<>(); 
     configProps.put(ProducerConfig.CLIENT_ID_CONFIG, "..."); 

     // Here we are pulling the value for the serverList that has been set 
     // see the init() and isRefreshNeeded() methods above 
     configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, serverList.get()); 

     configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 
     configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); 
     return new DefaultKafkaProducerFactory<>(configProps); 
    } 

    @Bean 
    @Lazy 
    public AtomicReference<KafkaTemplate<String, String>> kafkaTemplate() { 

     KafkaTemplate<String, String> template = new KafkaTemplate<>(kafkaProducerFactory()); 
     AtomicReference<KafkaTemplate<String, String>> ref = new AtomicReference<>(template); 
     return ref; 
    } 
} 

그런 다음 필요에 따라 빈을 주입합니다.

public MyClass1 { 

    @Resource 
    AtomicReference<KafkaTemplate<String, String>> kafkaTemplate; 
    ... 
} 

public MyClass2 { 

    @Resource 
    AtomicReference<KafkaTemplate<String, String>> kafkaTemplate; 
    ... 
} 

별도의 클래스에서 응용 프로그램 컨텍스트가 시작될 때 시작되는 스케줄러 스레드를 실행합니다.

class Manager implements Runnable { 

    private ScheduledExecutorService scheduler; 

    public void start() { 
     scheduler = Executors.newSingleThreadScheduledExecutor(); 
     scheduler.scheduleAtFixedRate(this, 0, 120, TimeUnit.SECONDS); 
    } 

    public void stop() { 
     scheduler.shutdownNow(); 
    } 

    @Override 
    public void run() { 

     try { 
      if (KafkaConfiguration.isRefreshNeeded()) { 

       AtomicReference<KafkaTemplate<String, String>> kafkaTemplate = 
        (AtomicReference<KafkaTemplate<String, String>>) Registry.getApplicationContext().getBean("kafkaTemplate"); 

       // Get new instance here. This will have the new value for the server list 
       // that was "refreshed" 
       KafkaConfiguration config = new KafkaConfiguration(); 

       // The set here replaces the wrapped objet in a thread safe manner with the new bean 
       // and thus all injected instances now use the newly created object 
       kafkaTemplate.set(config.kafkaTemplate().get()); 
      } 

     } catch (Exception e){ 

     } finally { 

     } 
    } 
} 

이 그것으로 약간의 냄새가 않기 때문에 내가하는 일을 옹호 할 일이 있다면 내가 울타리에 여전히 이니 클래스는 다음과 같이 보인다. 그러나 제한적이고주의 깊은 사용에서 명시된 유스 케이스에 대한 대체 접근법을 제공합니다. Kafka 관점에서 볼 때이 코드 예제는 이전 제작자를 열어 둡니다. 실제로는 그것을 닫으려면 오래된 제작자에게 flush() 호출을 적절하게 수행해야합니다. 그러나 이것이 그 예가 보여주는 의미는 아닙니다.