Chuyển đổi kiểu Spring MVC: PropertyEditor hoặc Converter?


129

Tôi đang tìm cách dễ nhất và đơn giản nhất để liên kết và chuyển đổi dữ liệu trong Spring MVC. Nếu có thể, không cần thực hiện bất kỳ cấu hình xml.

Cho đến nay tôi đã sử dụng PropertyEditor như vậy:

public class CategoryEditor extends PropertyEditorSupport {

    // Converts a String to a Category (when submitting form)
    @Override
    public void setAsText(String text) {
        Category c = new Category(text);
        this.setValue(c);
    }

    // Converts a Category to a String (when displaying form)
    @Override
    public String getAsText() {
        Category c = (Category) this.getValue();
        return c.getName();
    }

}

...
public class MyController {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(Category.class, new CategoryEditor());
    }

    ...

}

Rất đơn giản: cả hai chuyển đổi đều được định nghĩa trong cùng một lớp và ràng buộc là đơn giản. Nếu tôi muốn thực hiện một ràng buộc chung trên tất cả các bộ điều khiển của mình, tôi vẫn có thể thêm 3 dòng trong cấu hình xml của mình .


Nhưng Spring 3.x đã giới thiệu một cách mới để làm điều đó, bằng cách sử dụng Trình chuyển đổi :

Trong một thùng chứa Spring, hệ thống này có thể được sử dụng thay thế cho PropertyEditor

Vì vậy, hãy nói rằng tôi muốn sử dụng Trình chuyển đổi vì đây là "sự thay thế mới nhất". Tôi sẽ phải tạo hai bộ chuyển đổi:

public class StringToCategory implements Converter<String, Category> {

    @Override
    public Category convert(String source) {
        Category c = new Category(source);
        return c;
    }

}

public class CategoryToString implements Converter<Category, String> {

    @Override
    public String convert(Category source) {
        return source.getName();
    }

}

Hạn chế đầu tiên: tôi phải làm hai lớp. Lợi ích: không cần phải đúc nhờ sự hào phóng.

Sau đó, làm thế nào để tôi đơn giản dữ liệu liên kết các bộ chuyển đổi?

Hạn chế thứ hai: Tôi chưa tìm thấy bất kỳ cách đơn giản nào (chú thích hoặc các phương tiện lập trình khác) để thực hiện điều đó trong bộ điều khiển: không có gì giống như someSpringObject.registerCustomConverter(...);.

Các cách duy nhất tôi tìm thấy sẽ tẻ nhạt, không đơn giản và chỉ về ràng buộc bộ điều khiển chéo chung:

  • Cấu hình XML :

    <bean id="conversionService"
      class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="somepackage.StringToCategory"/>
                <bean class="somepackage.CategoryToString"/>
            </set>
        </property>
    </bean>
  • Cấu hình Java ( chỉ trong Spring 3.1+ ):

    @EnableWebMvc
    @Configuration
    public class WebConfig extends WebMvcConfigurerAdapter {
    
        @Override
        protected void addFormatters(FormatterRegistry registry) {
            registry.addConverter(new StringToCategory());
            registry.addConverter(new CategoryToString());
        }
    
    }

Với tất cả những nhược điểm này, tại sao nên sử dụng Converters? Tui bỏ lỡ điều gì vậy ? Có những thủ thuật khác mà tôi không nhận thức được?

Tôi muốn tiếp tục sử dụng PropertyEditor ... Binding dễ dàng và nhanh chóng hơn nhiều.


Lưu ý (Tôi cũng vấp ngã khi sử dụng Spring 3.2.17): khi sử dụng <mvc: annotation-điều khiển /> cần phải thực sự tham khảo bean chuyển đổi này: <mvc: annotation-direction convert-service = "convertService" />
mauhiz

addFormatters (...) phải được công khai. Cũng kể từ 5.0 WebMvcConfigurerAdOG không được dùng nữa.
Paco Abato

Câu trả lời:


55

Với tất cả những nhược điểm này, tại sao nên sử dụng Converters? Tui bỏ lỡ điều gì vậy ? Có những thủ thuật khác mà tôi không nhận thức được?

Không, tôi nghĩ rằng bạn đã mô tả rất toàn diện cả PropertyEditor và Converter, cách mỗi cái được khai báo và đăng ký.

Trong suy nghĩ của tôi, PropertyEditor bị giới hạn phạm vi - chúng giúp chuyển đổi Chuỗi thành một loại và chuỗi này thường xuất phát từ UI và do đó, việc đăng ký PropertyEditor bằng @InitBinder và sử dụng WebDataBinder có ý nghĩa.

Mặt khác, bộ chuyển đổi là chung chung hơn, nó được dành cho bất kỳ chuyển đổi nào trong hệ thống - không chỉ cho các chuyển đổi liên quan đến giao diện người dùng (Chuỗi thành loại mục tiêu). Ví dụ: Tích hợp Spring sử dụng rộng rãi bộ chuyển đổi để chuyển đổi tải trọng thư sang loại mong muốn.

Tôi nghĩ đối với các luồng liên quan đến UI, PropertyEditor vẫn phù hợp, đặc biệt đối với trường hợp bạn cần thực hiện một số tùy chỉnh cho một thuộc tính lệnh cụ thể. Đối với các trường hợp khác, tôi sẽ lấy đề xuất từ ​​tham chiếu Spring và viết một trình chuyển đổi thay thế (ví dụ: để chuyển đổi từ một Id dài sang một thực thể nói, như một mẫu).


5
Một điều tốt nữa là các trình chuyển đổi là không trạng thái, trong khi các trình soạn thảo thuộc tính có trạng thái và được tạo ra nhiều lần và được thực hiện với nhiều lệnh gọi api, tôi không nghĩ rằng điều này sẽ có tác động lớn đến hiệu suất nhưng trình chuyển đổi chỉ đơn giản và gọn gàng hơn.
Boris Treukhov

1
@Boris cleaner có, nhưng không đơn giản hơn, đặc biệt đối với người mới bắt đầu: bạn phải viết 2 lớp trình chuyển đổi + thêm một vài dòng trong xml config hoặc java config. Tôi đang nói về việc gửi / hiển thị biểu mẫu Spring MVC, với các chuyển đổi chung (không chỉ các thực thể).
Jerome Dalbert

16
  1. Để chuyển đổi sang / từ Chuỗi sử dụng trình định dạng (triển khai org.springframework.format.Formatter ) thay vì bộ chuyển đổi. Nó có các phương thức print (...)parse (...) , vì vậy bạn chỉ cần một lớp thay vì hai. Để đăng ký chúng, sử dụng FormattingConversionServiceFactoryBean , có thể đăng ký cả hai chuyển đổi và trình định dạng, thay vì ConversionServiceFactoryBean .
  2. Công cụ Formatter mới có một số lợi ích bổ sung:
    • Giao diện Formatter cung cấp đối tượng Locale trong các phương thức in (...)parse (...) , do đó, chuyển đổi chuỗi của bạn có thể nhạy cảm với miền địa phương
    • Ngoài các trình định dạng đăng ký trước, FormattingConversionServiceFactoryBean đi kèm với một vài đăng ký trước tiện dụng AnnotationFormatterFactory đối tượng, cho phép bạn xác định các thông số định dạng bổ sung qua chú thích. Ví dụ: @RequestParam@DateTimeFormat (mẫu = "MM-dd-yy")LocalDate baseDate ... Không khó để tạo các lớp AnnotationFormatterFactory của riêng bạn , hãy xem Spring's NumberFormatAnnotationFormatterFactory để biết ví dụ đơn giản. Tôi nghĩ rằng điều này loại bỏ sự cần thiết trong trình định dạng / trình soạn thảo dành riêng cho bộ điều khiển. Sử dụng một Dịch vụ chuyển đổi cho tất cả các bộ điều khiển và tùy chỉnh định dạng thông qua các chú thích.
  3. Tôi đồng ý rằng nếu bạn vẫn cần một số chuyển đổi chuỗi dành riêng cho bộ điều khiển, cách đơn giản nhất vẫn là sử dụng trình soạn thảo thuộc tính tùy chỉnh. (Tôi đã cố gắng gọi ' binder.setConversionService (...) ' trong phương thức @InitBinder của mình , nhưng không thành công, vì đối tượng binder đi kèm với dịch vụ chuyển đổi 'toàn cầu' đã được đặt. Có vẻ như các lớp chuyển đổi trên bộ điều khiển không được khuyến khích Mùa xuân 3).

7

Cách đơn giản nhất (giả sử rằng bạn đang sử dụng khung bền vững), nhưng không phải là cách hoàn hảo là triển khai trình chuyển đổi thực thể chung thông qua ConditionalGenericConvertergiao diện sẽ chuyển đổi các thực thể bằng siêu dữ liệu của chúng.

Ví dụ: nếu bạn đang sử dụng JPA, trình chuyển đổi này có thể xem liệu lớp được chỉ định có @Entitychú thích hay không và sử dụng @Idtrường chú thích để trích xuất thông tin và thực hiện tra cứu tự động bằng cách sử dụng giá trị Chuỗi được cung cấp làm Id để tra cứu.

public interface ConditionalGenericConverter extends GenericConverter {
    boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

ConditionalGenericConverter là một "vũ khí tối thượng" của API hội tụ mùa xuân, nhưng được triển khai một khi nó sẽ có thể xử lý hầu hết các hội tụ thực thể, tiết kiệm thời gian của nhà phát triển - thật tuyệt vời khi bạn chỉ định các lớp thực thể là tham số của trình điều khiển của mình và không bao giờ nghĩ đến việc triển khai một trình chuyển đổi mới (tất nhiên trừ các loại tùy chỉnh và phi thực thể).


Giải pháp tốt đẹp để đối phó với chỉ chuyển đổi thực thể, nhờ vào mẹo. Không đơn giản khi bắt đầu vì bạn phải viết thêm một lớp, nhưng đơn giản và tiết kiệm thời gian trong thời gian dài.
Jerome Dalbert

Btw như vậy có thể được thực hiện cho bất kỳ loại nào tuân thủ một số hợp đồng chung - một ví dụ khác: nếu enum của bạn thực hiện một số giao diện tra cứu ngược phổ biến - thì bạn cũng sẽ có thể thực hiện một trình chuyển đổi chung (nó sẽ tương tự như stackoverflow.com / câu hỏi / 5178622 / đánh )
Boris Treukhov

@JeromeDalbert vâng đó là một chút khó khăn cho người mới bắt đầu để làm một số công cụ cân nặng, nhưng nếu bạn có một đội ngũ các nhà phát triển nó sẽ được đơn giản hơn) PS Và nó sẽ trở nên nhàm chán để đăng ký các biên tập viên bất động sản tương tự mỗi khi vào mẫu ràng buộc anyway)
Boris Treukhov

1

Bạn có thể sắp xếp công việc xung quanh nhu cầu có hai lớp Trình chuyển đổi riêng biệt bằng cách triển khai hai Trình chuyển đổi dưới dạng các lớp bên trong tĩnh.

public class FooConverter {
    public static class BarToBaz implements Converter<Bar, Baz> {
        @Override public Baz convert(Bar bar) { ... }
    }
    public static class BazToBar implements Converter<Baz, Bar> {
        @Override public Bar convert(Baz baz) { ... }
    }
}

Bạn vẫn sẽ cần phải đăng ký riêng cả hai, nhưng ít nhất điều này sẽ cắt giảm số lượng tệp bạn cần sửa đổi nếu bạn thực hiện bất kỳ thay đổi nào.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.