Table of Contents
Java Reflection có thể nhận biết được những thứ (Class, field, method, ..) được chú thích bởi một Annotation nào đó. Và tất nhiên nó chỉ nhận biết được các Annotation có @Retention(RetentionPolicy.RUNTIME).
Reflection và Annotation là 2 tính năng rất mạnh mẽ của Java, nếu bạn hiểu rõ và vận dụng tốt nó sẽ giúp cho bạn tiết kiệm rất nhiều thời gian và công sức khi phải ngồi làm đi làm lại chỉ một công việc nhàm chán. Chẳng hạn như, bạn cần tính năng chuyển từ một đối tượng (object) sang xml. Nếu không có Reflection và Annotation thì ở mỗi đối tượng bạn đều phải viết code để chuyển đổi từ object sang xml.
Trong bài này tôi sẽ giới thiệu với các bạn cách kết hợp Java Reflection và Annotation để tạo chức năng chuyển một object sang xml.
Trong ví dụ này tôi sử dụng các class sau:
- XmlRootElement: là một Annotation, sử dụng để đánh dấu phần tử gốc (root element) của một tài liệu xml.
- XmlElementWrapper: là một Annotation sử dụng để đánh dấu đây là một phần cha và giá trị của nó là một Collection (có chứa tài liệu xml con).
- XmlElement: là một Annotation, sử dụng để đánh dấu đây là một phần tử của tài liệu xml.
- XmlAttribute: là một Annotation, sử dụng để đánh dấu đây là một thuộc tính của phần tử.
- Book: lớp chứa thông tin sách.
- BookStore: lớp chứa thông tin cửa hàng sách.
- ObjectToXmlHelper: lớp sử dụng Reflection để đọc thông tin các Annotation được đánh dấu và chuyển sang tài liệu xml.
- ObjectToXmlExample: chương trình minh họa sử dụng kết hợp Annotation với Reflection
Chi tiết code xử lý như sau:
1. XmlRootElement.java
package com.maixuanviet.combine; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Target(ElementType.TYPE) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface XmlRootElement { String name(); String namespace() default ""; }
2. XmlElementWrapper.java
package com.maixuanviet.combine; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Target({ ElementType.FIELD }) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface XmlElementWrapper { String name(); }
3. XmlElement.java
package com.maixuanviet.combine; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Target({ ElementType.FIELD }) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface XmlElement { String name(); }
4. XmlAttribute.java
package com.maixuanviet.combine; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Target({ ElementType.FIELD }) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface XmlAttribute { String name(); }
Book.java
package com.maixuanviet.combine; @XmlRootElement(name = "book") public class Book { @XmlElement(name = "name") private String name; @XmlElement(name = "author") private String author; @XmlElement(name = "publisher") private String publisher; @XmlAttribute(name = "isbn") private String isbn; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getPublisher() { return publisher; } public void setPublisher(String publisher) { this.publisher = publisher; } public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } @Override public String toString() { return "Book [name=" + name + ", author=" + author + ", publisher=" + publisher + ", isbn=" + isbn + "]"; } }
5. BookStore.java
package com.maixuanviet.combine; import java.util.List; @XmlRootElement(name = "bookStore", namespace = "com.maixuanviet.combine.annoation.reflection") public class BookStore { @XmlElement(name = "name") private String name; @XmlElement(name = "location") private String location; @XmlElementWrapper(name = "bookList") private List<Book> bookList; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public List<Book> getBookList() { return bookList; } public void setBookList(List<Book> bookList) { this.bookList = bookList; } @Override public String toString() { return "BookStore [name=" + name + ", location=" + location + "]"; } }
6. ObjectToXmlHelper.java
package com.maixuanviet.combine; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; public class ObjectToXmlHelper { /* * Chuyển đối tượng sang chuỗi xml */ public static <T> String convertToXml(T obj) { StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); sb.append(convertToXml(obj, 0)); return sb.toString(); } /* * Chuyển đối tượng sang chuỗi xml */ private static <T> String convertToXml(T obj, int numOfTab) { StringBuilder sb = new StringBuilder(); // Get Class of obj Class<?> clazz = obj.getClass(); // Kiểm tra xem lớp này có được chú thích XmlRootElement hay không. boolean isXmlDoc = clazz.isAnnotationPresent(XmlRootElement.class); List<Field> fields; String name; String value; if (isXmlDoc) { // Lấy ra đối tượng XmlRootElement, chú thích trên lớp này. XmlRootElement rootNode = clazz.getAnnotation(XmlRootElement.class); sb.append(getTab(numOfTab)); // Add tab sb.append("<" + rootNode.name()); // Root element if (isNotEmpty(rootNode.namespace())) { sb.append(" xmlns=\"" + rootNode.namespace() + "\""); } fields = getFields(clazz, XmlAttribute.class); if (!fields.isEmpty()) { for (Field field : fields) { field.setAccessible(true); XmlAttribute ann = field.getAnnotation(XmlAttribute.class); name = ann.name(); value = getValueOfField(field, obj); sb.append(" " + name + "=\"" + value + "\""); // Add attribute } } sb.append("><span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span>"); sb.append("\n"); // Add new line fields = getFields(clazz, null); if (!fields.isEmpty()) { // Create xml elements for (Field field : fields) { field.setAccessible(true); if (field.isAnnotationPresent(XmlElementWrapper.class)) { sb.append(createXmlWrapper(field, obj, numOfTab + 1)); } else if (field.isAnnotationPresent(XmlElement.class)) { sb.append(createXmlElement(field, obj, numOfTab + 1)); } } } sb.append(getTab(numOfTab)); sb.append("</" + rootNode.name() + ">"); // End root element } return sb.toString(); } private static String createXmlWrapper(Field field, Object obj, int numOfTab) { StringBuilder sb = new StringBuilder(); XmlElementWrapper ann = field.getAnnotation(XmlElementWrapper.class); String name = ann.name(); // Get Element's name sb.append(getTab(numOfTab)); // Create 1 tab sb.append("<" + name + ">"); // Start Element sb.append("\n"); // Add new line Collection<?> collections = getListValueOfField(field, obj); if (collections != null && !collections.isEmpty()) { // Create xml sub elements for (Object collection : collections) { sb.append(convertToXml(collection, numOfTab + 1)); // Increase tab sb.append("\n"); // Add new line } } sb.append(getTab(numOfTab)); // Create 1 tab sb.append("</" + name + ">"); // End Element sb.append("\n"); // Add new line return sb.toString(); } private static String createXmlElement(Field field, Object obj, int numOfTab) { StringBuilder sb = new StringBuilder(); XmlElement ann = field.getAnnotation(XmlElement.class); String name = ann.name(); // Get Element's name String value = getValueOfField(field, obj); // Get value of field sb.append(getTab(numOfTab)); // Create tab sb.append("<" + name + ">"); // Start Element sb.append(value); // Element's content sb.append("</" + name + ">"); // End Element sb.append("\n"); // Add new line return sb.toString(); } // Kiểm tra chuỗi không rỗng private static boolean isNotEmpty(String str) { return str != null && !str.isEmpty(); } // Lấy danh sách field có sử dụng Annotation ann private static List<Field> getFields(Class<?> clazz, Class<? extends Annotation> ann) { List<Field> fieldAttributes = new ArrayList<>(); Field[] fields = clazz.getDeclaredFields(); if (ann == null) { fieldAttributes.addAll(Arrays.asList(fields)); } else { for (Field field : fields) { if (field.isAnnotationPresent(ann)) { fieldAttributes.add(field); } } } return fieldAttributes; } // Lấy giá trị kiểu chuỗi private static String getValueOfField(Field field, Object obj) { String value = ""; try { value = String.valueOf(field.get(obj)); } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } return value; } // Lấy giá trị kiểu Collection (List, ArrayList, Set, ...) private static Collection<?> getListValueOfField(Field field, Object obj) { Collection<?> collection = null; try { Object objValue = field.get(obj); if (objValue instanceof Collection) { collection = (Collection<?>) objValue; } } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } return collection; } // Lấy số dấu tab private static String getTab(int numOfTab) { StringBuilder sb = new StringBuilder(); for (int i = 1; i <= numOfTab; i++) { sb.append("\t"); // Thêm dấu tab. } return sb.toString(); } } [/code] <!-- /wp:shortcode --> <!-- wp:heading --> <h2>7. ObjectToXmlExample.java</h2> <!-- /wp:heading --> <!-- wp:shortcode --> package com.maixuanviet.combine; import java.util.ArrayList; import java.util.List; public class ObjectToXmlExample { public static void main(String[] args) { // create list List<Book> bookList = new ArrayList<Book>(); // create books Book book1 = new Book(); book1.setIsbn("978-0-13-708189-9"); book1.setName("Core Java Volume I"); book1.setAuthor("Horstmann, Cay S. and Cornell"); book1.setPublisher("Oracle"); bookList.add(book1); Book book2 = new Book(); book2.setIsbn("978-3832180577"); book2.setName("Spring MVC Beginner’s Guide"); book2.setAuthor("Amuthan G"); book2.setPublisher("Packt Pub"); bookList.add(book2); Book book3 = new Book(); book3.setIsbn("999-1234567890"); book3.setName("Java 8 in Action"); book3.setAuthor("Raoul-Gabriel Urma"); book3.setPublisher("Manning Publications Co."); bookList.add(book3); // create bookstore, assigning book BookStore bookStore = new BookStore(); bookStore.setName("Fraport Bookstore"); bookStore.setLocation("Frankfurt Airport"); bookStore.setBookList(bookList); System.out.println(bookStore); System.out.println("bookList: "); for (Book book : bookList) { System.out.println("+ " + book); } System.out.println("\nConvert to xml: \n"); String xml = ObjectToXmlHelper.convertToXml(bookStore); System.out.println(xml); } }
Kết quả thực thi chương trình trên:
BookStore [name=Fraport Bookstore, location=Frankfurt Airport] bookList: + Book [name=Core Java Volume I, author=Horstmann, Cay S. & Cornell, publisher=Oracle, isbn=978-0-13-708189-9] + Book [name=Spring MVC Beginner’s Guide, author=Amuthan G, publisher=Packt Pub, isbn=978-3832180577] + Book [name=Java 8 in Action, author=Raoul-Gabriel Urma, publisher=Manning Publications Co., isbn=999-1234567890] Convert to xml: <?xml version="1.0" encoding="UTF-8" ?> <bookStore xmlns="com.maixuanviet.combine.annoation.reflection"> <name>Fraport Bookstore</name> <location>Frankfurt Airport</location> <bookList> <book isbn="978-0-13-708189-9"> <name>Core Java Volume I</name> <author>Horstmann, Cay S. and Cornell</author> <publisher>Oracle</publisher> </book> <book isbn="978-3832180577"> <name>Spring MVC Beginner’s Guide</name> <author>Amuthan G</author> <publisher>Packt Pub</publisher> </book> <book isbn="999-1234567890"> <name>Java 8 in Action</name> <author>Raoul-Gabriel Urma</author> <publisher>Manning Publications Co.</publisher> </book> </bookList> </bookStore>
Trên đây là ví dụ đơn giản minh họa việc kết hợp Reflection và Annotation trong các ứng dụng Java. Hy vọng giúp ích cho các bạn, hẹn gặp lại ở các bài viết tiếp theo.