Java動態代理與反射詳解


首先我得先請大家不要誤會,博客園說轉載的文章放在文章分類里,原創的文章用隨筆寫,我開先還以為隨筆是拿來寫抒情文的(滑稽),后來才發現不是這樣的,但是自己所有的文章都在文章分類里了,又懶得搬運,所以我就用js重定向了一下。所以現在標題欄里進來的都是文章分類哦,大部分都是自己原創的,轉載會注明轉載的url。

廢話不多說,今天我想來聊一下java里的反射和動態代理的問題,因為這兩個東西實在撩人,而且動態代理百度幾乎都是千篇一律,今天我寫這篇博文希望能幫助大家,順便也是為了鞏固自己,畢竟自己也折騰了好久。

先來看看反射。
java里的class文件加載分為兩種情況,一種就是類型是編譯器已知的,這種文件的.class文件在編譯的時候,編譯器會把.class文件打開檢查,但是注意不是加載哦,第二種就是我們可能是從別的地方獲取到了一個引用,然后動態的把這個未知類型的引用的對象的.class文件加載進jvm虛擬機里。

那么我們稱前者為RTTI,即Run- Time Type Identification 運行時類型識別,有的人把RTTI翻譯成 Run - Time Type Information ,我個人認為是不對的,因為我覺得它概括的不夠全面,所以我建議大家把I 翻譯成Identification更容易理解。

我們稱后者為“反射”,這對於正在學習JAVA的人來說可是一個新的名詞,但反射也是作為一個java的核心技術的存在。下面就來看看反射究竟有多重要吧。

反射

在java里提供了一個叫做reflect的庫,這個庫里封裝了Method,Constructor,field,Proxy,InvocationHandler 等類,這些類的API在我們學習反射會很有幫助。

反射最大的作用就在於我們可以不在編譯時知道某個對象的類型,而在運行時得到。同時我們只需要得到我們想得到的類的名字即可(如果不在一個包,必須寫完整的名字包括包名)。

package com.bike;

import java.lang.reflect.*;

public class Main {
	public static void main(String[] args) throws Exception{
		//返回A的構造方法
		Constructor c = A.class.getConstructor();
		//返回A類的所有為public 聲明的構造方法
		Constructor[] cons = A.class.getConstructors();
		//返回A類所有的構造方法,包括private
		Constructor[] cons2 = A.class.getDeclaredConstructors();
		//返回A類的第一個public 方法
		Method m = A.class.getMethod("say");
		//執行
		m.invoke(A.class.newInstance(), null);
		//返回A類所有的public 方法
		Method[] ms = A.class.getMethods();
		//返回A類所有的方法,包括private
		Method[] allMs = A.class.getDeclaredMethods();
		//返回A類的public字段
		Field field = A.class.getField("i");
		System.out.println(field.get(A.class.newInstance()));
		//返回A類的static 字段
		System.out.println(field.get(null));
	}
}

class A{
	public int i = 1;
	public static int b = 2;
	public A(){
		System.out.println("無參構造");
	}
	private A(String s){
		System.out.println("有參構造"+s);
	}
	
	public void say(){
		System.out.println("say");
	}
}

這里我只是簡單的把API羅列了一下,大家可以自己動手試試,我這里就不再去描述了。
通過上面的例子我們可以看出我們只用知道一個類的名字便可以得知它內部方法和字段,那么這里已經強烈的體現到了反射的作用。只是我這里做例子的時候把A作為了自己內部包的一個類,而在實際開發中,你可能是跨包的,所以你必須要寫上全名才行。

關於.class類字面常量的知識請參照我的上一篇博文:http://www.cnblogs.com/haodawang/articles/5954368.html

代理

接下來我們來看一下代理。
代理可以幫助我們進行很好的封裝,使底層的代碼能夠有效的隱藏起來。
為了區別,我們先來看看靜態代理吧。


public class Main2 {
	//這里傳入的是接口類型的對象,方便向上轉型,實現多態
	public static void consumer(ProxyInterface pi){
		pi.say();
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		consumer(new ProxyObject());
	}
}

//代理接口
interface ProxyInterface{
	public void say();
}


//被代理者
class RealObject implements ProxyInterface{
	//實現接口方法
	@Override
	public void say() {
		// TODO Auto-generated method stub
		System.out.println("say");
	}
	
}


//代理者
class ProxyObject implements ProxyInterface{

	@Override
	public void say() {
		// TODO Auto-generated method stub
		//dosomething for example
		System.out.println("hello proxy");
		new RealObject().say();
		System.out.println("this is method end");
	}
	
}
output:
hello proxy
say
this is method end

這就是靜態代理,理解這個應該不難。
下面我們再來看看動態代理

import java.lang.reflect.*;

public class Main {
	static void customer(ProxyInterface pi){
		pi.say();
	}
	public static void main(String[] args){
		RealObject real = new RealObject();
		ProxyInterface proxy = (ProxyInterface)Proxy.newProxyInstance(ProxyInterface.class.getClassLoader(),new Class[]{ProxyInterface.class}, new ProxyObject(real));
		customer(proxy);
	}
}


interface ProxyInterface{
	void say();
}

//被代理類
class RealObject implements ProxyInterface{
	public void say(){
		System.out.println("i'm talking");
	}
}

//代理類,實現InvocationHandler 接口
class ProxyObject implements InvocationHandler{
	private Object proxied = null;
	public ProxyObject(){
		
	}
	public ProxyObject(Object proxied){
		this.proxied  = proxied;
	}
	public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
		System.out.println("hello");
		return arg1.invoke(proxied, arg2);
	};
}


可以看到動態代理的代理類是實現了一個InvocationHandler的接口,我們通過reflect.Proxy的類的newProxyInstance方法就可以得到這個接口的實例,然后再來作為參數傳遞進去,這里每一個在代理類上處理的東西也會被重定向到調用處理器上。

至於動態代理和靜態代理的區別,即動態代理是動態的創建代理和動態的處理方法的,這也是反射的一個重要體現之處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM