註解是一種元數據, 能夠添加到java代碼中. 類、方法、變量、參數、包均可以被註解,註解對註解的代碼沒有直接影響.前端
定義註解用的關鍵字是 @interfacejava
在Annotation以前,XML被普遍的應用於描述元數據。可是XML是鬆耦合的並且維護比較麻煩。 有時使用一些和代碼緊耦合的東西更加合適(好比一些服務),Annotation應運而生,並且它更加方便維護。git
目前,許多框架將XML和Annotation兩種方式結合使用,平衡二者之間的利弊。例如ButterKnife, EventBus, Retrofit, Dagger等github
Annotations僅僅是元數據,和業務邏輯無關。也就是說Annotations只是指定了業務邏輯,它的用戶來 完成其業務邏輯,JVM即是它的用戶,它工做在字節碼層面.api
固然,前端編譯生成字節碼階段,編譯器針對註釋作了處理,若是有註解錯誤等,沒法正常編譯成字節碼.只有成功編譯生成字節碼後.在運行期JVM就能夠進行業務邏輯處理.bash
java內置的註解有Override, Deprecated, SuppressWarnings等, 做用相信你們都知道.
元註解就是用來定義註解的註解.其做用就是定義註解的做用範圍, 使用在什麼元素上等等框架
JDK5.0版本開始提供註解支持: @Documented、@Retention、@Target、@Inheriteddom
@Documented : 是否會保存到 Javadoc 文檔中。ide
@Retention : 定義該註解的生命週期。 它有三個枚舉類型: RetentionPolicy.SOURCE(只在源碼中可用)、 RetentionPolicy.CLASS(在源碼和字節碼中可用,註解默認使用這種方式)、 RetentionPolicy.RUNTIME(在源碼,字節碼,運行時都可用,咱們自定義的註解一般使用這種方式) Tips : RetentionPolicy.SOURCE – 在編譯階段丟棄。這些註解在編譯結束以後就再也不有任何意義,因此它們不會寫入字節碼。 @Override, @SuppressWarnings都屬於這類註解測試
@Target : 表示該註解用於什麼地方。若是不明確指出,該註解能夠放在任何地方。如下是一些可用的參數。 Tips : 屬性的註解是兼容的,你能夠添加多個屬性。 ElementType.TYPE:用於描述類、接口或enum聲明 ElementType.FIELD:用於描述實例變量 ElementType.METHOD:方法 ElementType.PARAMETER參數 ElementType.CONSTRUCTOR構造器 ElementType.LOCAL_VARIABLE本地變量 ElementType.ANNOTATION_TYPE 另外一個註釋 ElementType.PACKAGE 用於記錄java文件的package信息
@Inherited : 是否能夠被繼承,默認爲false
如下代碼所有經過Idea開發
建立一個註解類
@Retention(RetentionPolicy.RUNTIME)
public @interface SingleAnno {
String value() default "shy";
}
複製代碼
引用它
public class MyClass {
@SingleAnno("single")
public void run(){ }
}
複製代碼
經過反射獲取值
public class TestDemo {
@Test
public void test(){
Class<MyClass> myClass = MyClass.class;
for (Method method : myClass.getDeclaredMethods()){
SingleAnno anno = method.getAnnotation(SingleAnno.class);
if(anno != null){
System.out.println(method.getName());//打印方法名
System.out.println(anno.value());//打印註解值
}
}
}
}
複製代碼
控制檯能夠看到,輸出的是single
run
single
複製代碼
Annotations只支持基本類型、String及枚舉類型。註釋中全部的屬性被定義成方法,並容許提供默認值。
①定義註解類型(稱爲A),最好給A加上運行Retention的RUNTIME註解.默認應該是SOURCE類型. ②定義屬性,實際上是方法表示.提供默認值. ③在其餘類方法(稱爲M)等添加A註解,並給A指定屬性值. ④能夠在其餘地方獲取M方法,而後獲取M的註解,並獲取註解值等.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiAnno {
enum Priority{HIGH,MID,LOW}
enum Status {START,PAUSE,STOP}
String name() default "TheShy";
Priority priority() default Priority.HIGH;
Status status() default Status.START;
}
複製代碼
核心: 經過聚合來實現,讓代理類持有委託類的引用便可.
一個小例子: 咱們用一個隨機睡眠時間模擬火車運行的時間。若是我要計算運行時間,而且這個類沒法改動.
public interface Runnable {
void running();
}
public class Train implements Runnable {
public void running() {
System.out.println("Train is running......");
int ranTime = new Random().nextInt(1000);
try {
Thread.sleep(ranTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
複製代碼
這裏有不少解決方案: 例如在調用方法地方的先後記錄,繼承(繼承Train調用父類方法),聚合(新建Train2,構造方法傳入Train對象,而後調用running).
可是若是再增長需求:在running方法先後打印日誌,並控制執行順序,固然是用繼承仍是能夠實現,可是要繼續建立新的子類,致使無限擴展......
這時候修改聚合,使其成爲靜態代理就能夠完美解決這個問題: 將構造方法改傳入Runnable接口:
//代理-在方法先後打印日誌
public class TrainLogProxy implements Runnable {
private Runnable runnable;
public TrainLogProxy(Runnable runnable) {
this.runnable = runnable;
}
public void running() {
System.out.println("Train running start...");
runnable.running();
System.out.println("Train running end...");
}
}
複製代碼
//代理-計算執行時間
public class TrainTimeProxy implements Runnable {
private Runnable runnable;
public TrainTimeProxy(Runnable runnable) {
this.runnable = runnable;
}
public void running() {
long start = System.currentTimeMillis();
runnable.running();
long end = System.currentTimeMillis();
System.out.println("run time = " + (end - start));
}
}
複製代碼
接下來:
Train train = new Train();
//想先計算執行時間,後打印log
// TrainTimeProxy trainTimeProxy = new TrainTimeProxy(train);
// TrainLogProxy trainLogProxy = new TrainLogProxy(trainTimeProxy);
// trainLogProxy.running();
//想先打印log,後計算執行時間
TrainLogProxy trainLogProxy = new TrainLogProxy(train);
TrainTimeProxy trainTimeProxy = new TrainTimeProxy(trainLogProxy);
trainTimeProxy.running();
複製代碼
繼承和聚合的區別:
接下來,觀察上面的類TimeProxy,在它的fly方法中咱們直接調用了Runable->run()方法。換而言之,TrainTimeProxy其實代理了傳入的Runnable對象,這就是典型的靜態代理實現。 從表面上看,靜態代理已經完美解決了咱們的問題。但是,試想一下,若是咱們須要計算SDK中100個方法的運行時間,一樣的代碼至少須要重複100次,而且建立至少100個代理類。往小了說,若是Train類有多個方法,咱們須要知道其餘方法的運行時間,一樣的代碼也至少須要重複屢次。所以,靜態代理至少有如下兩個侷限性問題:
那麼,咱們是否可使用同一個代理類來代理任意對象呢?咱們以獲取方法運行時間爲例,是否可使用同一個類(例如:TrainProxy)來計算任意對象的任一方法的執行時間呢?甚至再大膽一點,代理的邏輯也能夠本身指定。好比,獲取方法的執行時間,打印日誌,這類邏輯均可以本身指定。
核心原理 :
首先經過Proxy.newProxyInstance方法獲取代理類實例,然後能夠經過這個代理類實例調用代理類的方法,對代理類的方法的調用實際上都會調用中介類(調用處理器)的invoke方法,在invoke方法中咱們調用委託類的相應方法,而且能夠添加本身的處理邏輯。
Runnable runnable = (Runnable) Proxy.newProxyInstance(Runnable.class.getClassLoader(), new Class[]{Runnable.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object invoke = method.invoke(new Train(), args);
System.out.println("after");
return invoke;
}
});
runnable.running();
複製代碼
以上咱們就成功的經過不修改Train類在執行running()先後打印了日誌.
代理模式最大的特色就是代理類和實際業務類實現同一個接口(或繼承同一父類),代理對象持有一個實際對象的引用,外部調用時操做的是代理對象,而在代理對象的內部實現中又會去調用實際對象的操做
Java動態代理其實內部也是經過Java反射機制來實現的,即已知的一個對象,而後在運行時動態調用其方法,這樣在調用先後做一些相應的處理
經過動態代理+註解,完成相似retrofit效果,在InvocationHandler的Invoke方法處獲取方法、方法註解、方法參數註解、方法參數等信息,根據狀況設置adapter,完成業務邏輯. (固然,這裏省略了adapter的動做)
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Get {
public String value();
}
複製代碼
public interface ServerAPI {
@Get("https://www.baidu.com/")
public String getBaiduHome(@Query("type") String type);
@Post("https://www.baidu.com/update")
public String getBaiduUser(@Field("name") String name, @Field("age") String age);
}
複製代碼
public class APICreater {
public static ServerAPI create(Class<ServerAPI> api){
ServerAPI serverAPI = (ServerAPI) Proxy.newProxyInstance(api.getClassLoader(), new Class[]{api}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
getMethodMsg(method, args);
if ("getBaiduHome".equals(method.getName())) {
return "I am getBaiduHome return by proxy";
}
if ("getBaiduUser".equals(method.getName())) {
return "I am getBaiduUser return by proxy";
}
ServerAPI obj = getAPI();
return method.invoke(obj, args);
}
});
return serverAPI;
}
private static ServerAPI getAPI() {
return new ServerAPI() {
@Override
public String getBaiduHome(String type) {
return null;
}
@Override
public String getBaiduUser(String name, String age) {
return null;
}
};
}
// 獲取了註解信息和參數信息,結合起來就能夠實現本身的自定義方法.
private static void getMethodMsg(Method method, Object[] args) {
AnnoBean bean = new AnnoBean();
bean.setMethodName(method.getName());
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Get) {
Get getAnni = (Get) annotation;
String value = getAnni.value();
bean.setMethodAnniType("Get");
bean.setMethodAnniValue(value);
}
if (annotation instanceof Post) {
Post getAnni = (Post) annotation;
String value = getAnni.value();
bean.setMethodAnniType("Post");
bean.setMethodAnniValue(value);
}
}
bean.setMethodArgs(Arrays.asList(args));
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (Annotation[] annotation : parameterAnnotations) {
for (Annotation annotation1 : annotation) {
if (annotation1 instanceof Field) {
List<String> list = bean.getParamAnniList();
if (list == null) {
list = new ArrayList<String>();
}
list.add("paramAnniType: field " + " value: " + ((Field) annotation1).value());
bean.setParamAnniList(list);
}
if (annotation1 instanceof Query) {
List<String> list = bean.getParamAnniList();
if (list == null) {
list = new ArrayList<String>();
}
list.add("paramAnniType: query " + " value: " + ((Query) annotation1).value());
bean.setParamAnniList(list);
}
}
}
System.out.println(bean.toString());
}
}
複製代碼
public class TestRetrofitDemo {
@Test
public void testRetrofit(){
ServerAPI serverAPI = APICreater.create(ServerAPI.class);
String homeeeeee = serverAPI.getBaiduHome("Homeeeeee");
System.out.println("-----" + homeeeeee);
}
}
複製代碼
最後測試一下輸出結果:
AnniBean{methodName='getBaiduHome', methodArgs=[Homeeeeee], methodAnniType='Get', methodAnniValue='https://www.baidu.com/', paramAnniList=[paramAnniType: query value: type]}
-----I am getBaiduHome return by proxy
複製代碼
github地址 : github.com/saurylip/An…