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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | 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(); } } &# 91 ;/code&# 93 ; <!-- /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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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.