2015-01-17 21 views
11

Tôi muốn tạo lại (đối tượng mới) một bean cụ thể tại thời gian chạy (không khởi động lại máy chủ) khi một số thay đổi DB. Đây là cách nó trông -Java Spring Tạo lại Bean cụ thể

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

Mã này không chạy như myShop đối tượng được tạo ra bởi mùa xuân như Singleton & bối cảnh của nó không được làm mới trừ khi server được khởi động lại. Làm thế nào để làm mới (tạo một đối tượng mới) myShop?

Một cách tồi tệ mà tôi có thể nghĩ là tạo đối tượng myShop mới bên trong restartListeners() nhưng điều đó dường như không phù hợp với tôi.

+0

thuộc tính bean được tải trong giai đoạn xử lý nhà máy Bean, và tôi không nghĩ rằng chúng tôi có thể làm được nhiều điều sau đó về tải lại đặc biệt là trong đơn, bạn sẽ phải xây dựng lại bối cảnh ứng dụng mà hạt được nạp vào, đây là một ví dụ thú vị, http://stackoverflow.com/questions/4084890/spring-replacing-the-bean-property-values-with-new-property-file-values – mariubog

Trả lời

4

Trong DefaultListableBeanFactory bạn có phương thức public destroySingleton ("beanName") để bạn có thể chơi với nó, nhưng bạn phải lưu ý rằng nếu autowired bean của bạn nó sẽ giữ cùng một thể hiện của đối tượng đã được autowired trong nơi đầu tiên, bạn có thể thử một cái gì đó như thế này:

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

    } 

Nó không phải là đẹp, nhưng cho thấy làm thế nào bạn có thể tiếp cận nó. Nếu bạn đang đối phó với một bộ điều khiển chứ không phải là một lớp thành phần, bạn có thể có một tiêm trong đối số phương pháp và nó cũng sẽ làm việc, bởi vì Bean sẽ không được tái tạo cho đến khi cần thiết bên trong phương pháp, ít nhất đó là những gì nó trông giống như. Câu hỏi thú vị sẽ là những người khác đã tham khảo Bean cũ bên cạnh đối tượng nó đã được autowired vào nơi đầu tiên, bởi vì nó đã được gỡ bỏ khỏi bối cảnh, tôi tự hỏi nếu nó vẫn còn tồn tại hoặc là rác colected nếu phát hành nó trong bộ điều khiển ở trên, nếu một số đối tượng khác trong ngữ cảnh có tham chiếu đến nó, ở trên sẽ gây ra vấn đề.

1

Chúng tôi có cùng một trường hợp sử dụng. Như đã đề cập một trong những vấn đề chính với việc tạo lại một bean trong suốt thời gian chạy là làm thế nào để cập nhật các tài liệu tham khảo đã được tiêm. Đây là thách thức chính.

Để giải quyết vấn đề này, tôi đã sử dụng lớp AtomicReference < của Java. Thay vì tiêm trực tiếp hạt đậu, tôi đã bọc nó như một AtomicReference và sau đó tiêm nó. Bởi vì đối tượng được bao bọc bởi AtomicReference có thể được thiết lập lại một cách an toàn, tôi có thể sử dụng điều này để thay đổi đối tượng bên dưới khi phát hiện thấy thay đổi cơ sở dữ liệu. Dưới đây là ví dụ về cấu hình/cách sử dụng mẫu này:

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

Sau đó, tôi cho đậu vào nơi cần thiết, ví dụ:

public MyClass1 { 

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

public MyClass2 { 

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

Trong một lớp riêng biệt, tôi chạy một chuỗi lịch được bắt đầu khi ngữ cảnh ứng dụng được bắt đầu. Lớp học trông giống như thế này:

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 { 

     } 
    } 
} 

Tôi vẫn đang ở hàng rào nếu đây là điều tôi sẽ ủng hộ vì nó có mùi nhẹ. Nhưng trong sử dụng hạn chế và cẩn thận, nó cung cấp một cách tiếp cận thay thế cho trường hợp sử dụng đã nêu. Xin lưu ý rằng từ quan điểm của Kafka, ví dụ mã này sẽ để nhà sản xuất cũ mở. Trong thực tế, người ta cần phải thực hiện một cuộc gọi tuôn ra() trên nhà sản xuất cũ để đóng nó. Nhưng đó không phải là ví dụ minh họa.

Các vấn đề liên quan