JAVA反射
對於java來說,我們常見的功能都是類、屬性、方法,這些在我們常規變成中,起着舉足輕重的作用。但是JAVA之所以強大,反射機制纔是JAVA的重中之重,在之前幾章的博客中曾經提到利用JAVA的反射機制模擬mvc框架,模擬hibernate等數據庫框架。但是在反射過程中,還有一項非常重要的元素,那就是註解。
經常接觸spring的我們應該都瞭解,spring中代碼裏面註解有着極高的出場率,然而在什麼時候我們會需要使用註解,註解都能幫我們幹什麼,相信好多人對註解的理解還是比較模糊的。下面我們詳細瞭解一下註解
註解的定義
先引入一個比較調侃的話,“喬布斯重新定義了手機,羅永浩重新定義了傻逼”PS:這句話出自老羅的一個微博,本人無貶低老羅的意思。
首先讓我們來看看官方對註解的定義:
Java 註解用於爲 Java 代碼提供元數據。作爲元數據,註解不直接影響你的代碼執行,但也有一些類型的註解實際上可以用於這一目的。Java 註解是從 Java5 開始添加到 Java 的。
從過來人的角度上理解這句話,其實很簡單,但是從初學者的角度上來看,應該會是一頭霧水,那麼按照我的理解,註解應該這樣定義:
當你無法對已經定義好的類,屬性,方法進行改變;但是需求偏偏非要改變。那麼不能改變代碼,我們就去改變他的運行方式。
舉例:
比如數據庫定義好一個字段爲日期類型,框架約束好,日期的格式爲YYYY-MM-DD,但是偏偏有一個業務,必須要求日期要按照YYYY-MM-DD HH:MM:SS顯示。
在比如,我們定義好的對象中,有三個屬性,分別是name,age,sex。我們需要通過xml和其他系統進行交互,在我們自己使用的系統中,我們需要這三個字段,但是在和其他系統交互的時候,sex字段是隱私,我們不能給別人,但是我們還要使用這個對象。那麼在同其他系統交互過程中,轉爲xml的時候,我們就要忽略某個字段。
應用:
由於舉例中的第一個例子是SPRING MVC返回reponsebody中的常見問題,網上已經有很多解決方案,我們使用第二個例子進行解決
1、創建一個自定義註解類
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreXmlElement {
}
這是一個最基本的註解創建方式。當然我們註解裏面還有各種屬性,例如我們增加一個name的屬性,可以
String name() default "##default";
因爲第二個例子本身不需要太複雜的功能,我們暫時就不增加屬性了。
2、在我們需要轉換XML的類的某個屬性上增加上該註解
@IgnoreXmlElement
private String sex;
3、在類轉換xml的時候,根據註解對這個屬性特殊處理
public String toString() {
String xml = null;
try {
Class<T> c = getEntityClass();
JAXBContext jc = JAXBContext.newInstance(c);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_ENCODING, Encode.UTF8);
marshaller.setListener(new MarshallerListener());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
marshaller.marshal(this, bos);
xml = new String(Base64.encodeBase64(bos.toByteArray()), Encode.UTF8);
} catch (Exception e) {
throw new RuntimeException("解析爲xml時出錯:" + e.getMessage());
}
return xml;
}
在類中,我們重寫了類的toString方法,方便返回一個xml
marshaller.setListener(new MarshallerListener());
對xml的轉化我們這裏使用的java自帶的jaxbcontext,我們對齊增加一個監聽。
package com.easternie.sys.util;
import javax.xml.bind.Marshaller;
import com.easternie.his.webservice.model.data.IgnoreXmlElement;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
/**
* 對java對象轉xml增加一個監聽,如果節點爲null空返回空節點
*
* @author ZhangQiang
*
*/
public class MarshallerListener extends Marshaller.Listener {
public static final String BLANK_CHAR = "";
@Override
public void beforeMarshal(Object source) {
super.beforeMarshal(source);
Field[] fields = source.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
Annotation an = f.getAnnotation(IgnoreXmlElement.class);
if(an != null) {
try {
f.set(source, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}else {
try {
if (f.getType() == String.class && f.get(source) == null) {
f.set(source, BLANK_CHAR);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
這個監聽實現瞭如下兩個功能,
功能一:如果我們通過反射獲取的field的註解不爲空,那麼說明這個字段是我們轉XML時候要忽略的,我們直接把他設置爲NULL,這裏要說明,在使用JAXBCONTEXT的過程中,我們可以給元素設置爲NULL讓其不產生XML節點。
功能二:但是如果是NULL就不顯示節點了,貌似又違背了我們的需求,我們主動設置爲NULL的應該不正常顯示,但是如果這個字段本身就爲NULL,我們應該給用戶返回一個空節點。所有又有了else中的代碼,我們增加一個空格,保證返回給用戶的是一個空節點,而非連節點都不顯示。
總結:
寫在最後,註解一般情況下都是結合反射使用,用來處理非常規流程的問題。所以在我們開發過程中遇到此類問題的時候。我們應該可以想到使用註解或者自定義註解,當然上面僅僅是舉例在屬性上增加註解,實際在類,在方法上增加註解獲取註解和上面代碼也大同小異,不再過多描述。