Kết hợp Java Reflection và Java Annotations

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();
    }
 
}
&#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:

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.