JAVA類載入器、註解和動態代理
一.類載入器
1.什麼是類載入器,作用是什麼
類載入器就載入位元組碼檔案(.class)
2.類載入器的種類
類載入器有三種,不同載入器載入不同
- BootStrap:引導類載入器:載入都是最基礎的檔案
- ExtClassLoader:擴充套件類載入器:載入都是基礎的檔案
- AppClassLoader:應用類載入器:三方jar包和自己編寫java檔案
- 載入順序:BootStrap -> ExtClassLoader -> AppClassLoader
怎麼獲得類載入器(重點)
- 獲得位元組碼物件的三種方式
- Class.forName(“…”);
- 物件.getClass()
- 類名.class
- 位元組碼物件.getClassLoad()獲得ClassLoader型別(即類載入器)的物件
- 由於src下的檔案都會被編譯,故通過類載入器的物件可以獲得classes(即編譯後的src)下的任何資源[注意是由於在classes內,故都是對應java檔案的位元組碼檔案]
- 例項程式碼:
package demo;
import java.net.URL;
public class Demo {
public static void main(String[]args) {
//獲得Demo位元組碼檔案的類載入器
Class clazz = Demo.class;//獲得Demo的位元組碼物件
ClassLoader classLoader = clazz.getClassLoader();//獲得Demo的類載入器
//getResource的引數路徑相對classes(src)
URL jdbc_properties_url = classLoader.getResource("demo/jdbc.properties");//獲得classes(src)下的任何資源地址
String path = jdbc_properties_url.getPath();
System.out.println(path);
}
}
//列印結果/Users/wangzhe/Documents/workspace/WEB25/build/classes/demo/jdbc.properties
二.註解 @xxx
1.什麼是註解,註解的作用
註解就是符合一定格式的語法 @xxxx
註解作用:
- 註釋:在閱讀程式時清楚(給程式設計師看的)
- 註解:給jvm看的(給機器看的)
註解在目前而言最主流的應用:代替配置檔案
如在建立web專案時選項Dynamic web module version3.0以上沒有web.xml檔案,是因為被註解取代,如下:
package demo; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/demo") public class DemoServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().append("Served at: ").append(request.getContextPath()); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
此時@WebServlet就代替在web.xml中配置url-pattern等相關配置。
關於配置檔案與註解開發的優缺點:
- 註解優點:開發效率高,成本低
- 註解缺點:耦合性大,並且不利於後期維護(如果是在web.xml中配置則直接修改配置資訊即可)
- 故企業開發中,我們通常把不變的內容定義成註解,變化的內容在xml中配置
2.jdk5提供的註解
@Override:告知編譯器此方法是覆蓋父類的(可以在方法上用),幫助檢查覆蓋父類的方法是否正確
@Deprecated:標註過時
@SuppressWarnings:壓制警告(可以在方法上用,也可以在類上用,還可以在屬性上用)
發現的問題:
不同的註解只能在不同的位置使用(方法上、欄位上、類上)
例項程式碼:
package annotation; import java.util.ArrayList; import java.util.List; public class AnnoDemo { public static void main(String[] args) { @SuppressWarnings(value = { "rawtypes", "unused" })//rawtypes壓制泛型的警告、unused壓制未使用的警告,all壓制所有警告 List list = new ArrayList(); show(); } @Deprecated public static void show() {} public static void show(String xx) {} @Override public String toString() { return "AnnoDemo []"; } }
3.自定義註解(瞭解)
思路
- 怎樣去編寫一個自定義的註解
- 怎樣去使用註解
- 怎樣去解析註解—–使用反射知識
編寫一個註解
定義註解關鍵字:@interface
註解的屬性
語法:返回值 名稱();
注意:如果屬性的名字是value,並且註解的屬性值有一個那麼在使用註解時可以省略value
如定義名為value的字串型別屬性,使用時如下:
//定義如下 public @interface MyAnno { String value(); } //使用如下 public class MyAnnoTest { @MyAnno("xxx") public void show(){} }
- 如定義名為value的字串陣列型別屬性,使用時如下:
//定義如下 public @interface MyAnno { String[] value(); } //使用如下 public class MyAnnoTest { @MyAnno({"xxx","bbb"}) public void show(){} }
- 如定義其他名稱的對應型別屬性,使用時如下:
//定義如下 public @interface MyAnno { //註解的屬性 String name(); int age() default 28; } //使用如下 public class MyAnnoTest { @MyAnno(name="aaa",age=10) public void show(){} }
註解屬性型別只是以下幾種
基本型別
- String
- 列舉型別
- 註解型別
- Class型別
- 以上型別的陣列型別
使用註解
在類、方法、欄位上面使用@XXX
解析使用了註解的類
- 介入一個概念:元註解:代表修飾註解的註解,作用:限制定義的註解的特性
- @Target代表註解修飾的範圍:類上使用,方法上使用,欄位上使用
- ElementType.FIELD:欄位上可用此註解
- ElementType.METHOD:方法上可以用此註解
- ElementType.TYPE:類/介面上可以使用此註解
- @Retention
- RetentionPolicy.SOURCE: 註解在原始碼級別可見(預設)
- RetentionPolicy.CLASS:註解在位元組碼檔案級別可見
- RetentionPolicy.RUNTIME:註解在整個執行階段都可見
注意:
- 要想解析使用了註解的類,那麼該註解的Retention必須設定成Runtime
- 關於註解解析的實質:從註解中解析出屬性值
- 位元組碼物件存在於獲得註解相關的方法
isAnnotationParsent<Class <?extends Annotation> annotationClass>
:判斷該位元組碼物件身上是否使用該註解了getAnnotation(Class <A> annotationClass)
:獲得該位元組碼物件身上的註解物件
例項程式碼:
- 定義註解:可以修飾類和方法;註解在整個執行階段都可見(才能保證通過位元組碼物件可以取出對應註解);定義註解的屬性
package annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD,ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnno { //註解的屬性 String name(); int age() default 28; }
- 定義使用了註解的類
package annotation; public class MyAnnoTest { @MyAnno(name="aaa",age=10) public void show(String str){ System.out.println("show running......"+str); } }
- 解析使用了註解的類並輸出註解的屬性內容
package annotation; import java.lang.reflect.Method; public class MyAnnoParser { public static void main(String[] args) throws NoSuchMethodException, SecurityException { //解析show方法上面的@MyAnno //直接目的是:獲得show方法上MyAnno中的引數 //獲得show方法的位元組碼物件 Class clazz = MyAnnoTest.class; Method method = clazz.getMethod("show",String.class); //獲得show方法上的@MyAnno MyAnno annotation = method.getAnnotation(MyAnno.class); //獲得@MyAnno上的屬性值 System.out.println(annotation.name()); System.out.println(annotation.age()); } }
4.註解案例:模擬單元測試
編寫註解MyTest用於修飾方法,且註解在整個階段都可見。此註解的作用僅做修飾,用於被註解修飾的方法需要執行某些內容。
package case1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyTest { //不需要屬性 }
編寫使用該註解的類。
package case1; import org.junit.Test; public class TestDemo { //程式設計師開發中測試用的程式碼 @Test public void test1() { System.out.println("test1 running..."); } @MyTest public void test2() { System.out.println("test2 running..."); } @MyTest public void test3() { System.out.println("test3 running..."); } }
編寫執行對應解析內容的類,先通過位元組碼物件找到對應的類,獲取所有的方法,再通過方法篩選被@MyTest註解修飾的方法,最後執行對應內容(本demo執行的是對應方法體)
package case1; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class MyTestParster { public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { //獲得TestDemo Class clazz = TestDemo.class; //獲得所有的方法 Method[] methods = clazz.getMethods(); if(methods!=null) { //獲得註解使用了@MyTest的方法 for(Method method : methods) { //判斷該方法是否使用了@MyTest註解 boolean annotationPresent = method.isAnnotationPresent(MyTest.class); if(annotationPresent) { //該方法使用MyTest註解 method.invoke(clazz.newInstance(), null); } } } } }
輸出結果
test3 running... test2 running...
三.動態代理
1.什麼是代理(中介)
- 目標物件/被代理物件(房主:真正的租房的方法)
- 代理物件(黑中介:有租房子的方法(呼叫房主的租房的方法))
- 執行代理物件方法的物件(租房的人 )
- 流程:我們要租房——>中介(租房的方法)——>房主(租房的方法)
- 抽象:呼叫物件——>代理物件——>目標物件
2.動態代理
動態代理:不用手動編寫一個代理物件,不需要一一編寫與目標物件相同的方法,這個過程,在執行時 的記憶體中動態生成代理物件。(位元組碼物件級別的代理物件 )
動態代理的API
在jdk的API中存在一個Proxy中存在一個生成動態代理的的方法newProxyInstance
返回值型別 | 方法 |
---|---|
static Object | newProxyInstance(ClassLoader loader,Class[]interfaces,InvoationHandlerh) |
* 返回值:Object就是代理物件
* 引數1:loader代表與目標物件相同的類載入器(目標物件.getClass().getClassLoader())
* 引數2:interfaces代表與目標物件實現的所有的介面位元組碼物件陣列
* 引數3:h具體的代理的操作,InvacationHandler介面
注意:JDK的Proxy方式實現的動態代理,目標物件必須有介面,沒有介面不能實現jdk版動態代理
代理物件與介面的關係
例項程式碼(利用動態代理的內容為目標物件進行動態代理)
- 編寫介面內容
package proxy; public interface TargetInterface { public void method1(); public String method2(); public int method3(int x); }
- 編寫目標實現類
package proxy; public class Target implements TargetInterface{ @Override public void method1() { System.out.println("method1 running..."); } @Override public String method2() { System.out.println("method2 running..."); return "method2"; } @Override public int method3(int x) { return x; } }
- 編寫第一版代理內容,建立代理物件,利用匿名內部類重寫invoke方法實現對目標物件的目標方法進行相關處理,並利用代理物件呼叫對應方法進行結果檢視。(請參見注釋)
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.junit.Test; public class ProxyTest { @Test public void test1() { //獲得動態的代理物件(在執行時,內容中動態的為Target建立一個虛擬的代理物件) //objProxy是代理物件 根據引數確定到底是誰的代理物件 /* * 引數一ClassLoader loader:與目標物件相同的類載入器 * 引數二Class<?>[] interfaces:介面的位元組碼物件陣列 * 引數三InvocationHandler h:具體的代理的操作,InvocationHandler介面 */ TargetInterface objProxy = (TargetInterface)Proxy.newProxyInstance(Target.class.getClassLoader(), new Class[]{TargetInterface.class}, new InvocationHandler() { //invoke()代表的是執行代理物件的方法 @Override /* * 引數二Method method:代表目標物件的方法位元組碼物件 * 引數三Object[] args:代表目標物件相應方法的引數 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目標方法前的邏輯"); //執行目標物件的方法 Object invoke = method.invoke(new Target(), args); System.out.println("目標方法後的邏輯"); return invoke; } }); objProxy.method1(); String method2 = objProxy.method2(); System.out.println(method2); } }
執行結果:
目標方法前的邏輯 method1 running... 目標方法後的邏輯 目標方法前的邏輯 method2 running... 目標方法後的邏輯 method2
- 編寫第二版內容,對相關方法內容進行詳細闡述。(請參見注釋)
package proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyTest2 { public static void main(String[] args) { final Target target = new Target(); //動態建立代理物件 TargetInterface proxy = (TargetInterface)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces() , new InvocationHandler() { @Override //invoke方法被執行幾次?看代理物件呼叫方法幾次 //代理物件呼叫介面相應的方法,都是呼叫invoke() /* * 引數一Object proxy:指的就是代理物件 * 引數二Method method:指的是目標方法的位元組碼物件 * 引數三Object[] args:指的是呼叫目標方法時的引數 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //執行相關處理操作... //利用反射的知識點 Object invoke = method.invoke(target, args);//目標物件的相應方法 //執行相關處理操作... return invoke;//return返回的值給代理物件 } }); proxy.method1();//呼叫invoke,對應引數二method就是目標物件的method1,args是null,返回值是null String method2 = proxy.method2();//呼叫invoke,對應引數二method就是目標物件的method2,args是null,返回值是”method2“ int method3 = proxy.method3(100);//呼叫invoke,對應引數二method就是目標物件的method3,args是Object[]{100},返回值是100 System.out.println(method2); System.out.println(method3); } }
執行結果:
method1 running... method2 running... method2 100
案例DEMO:使用動態代理完成全域性編碼
- 例項程式碼
package filter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; public class EncodingFilter2 implements Filter{ @Override public void destroy() {} @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)arg0; //使用動態代理完成全域性編碼 HttpServletRequest enhanceRequest = (HttpServletRequest)Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //對getParameter方法進行增強 String name = method.getName();//這是目標物件的方法名稱 if("getParameter".equals(name)) { String invoke = (String)method.invoke(request,args); //轉碼 invoke = new String(invoke.getBytes("ISO8859-1"),"UTF-8"); return invoke; } return method.invoke(request, args); } }); arg2.doFilter(enhanceRequest, arg1); } @Override public void init(FilterConfig arg0) throws ServletException {} }
總結
- 具體應用時,裝飾者模式一般用於內容增強
- 動態代理一般用於攔截
相關推薦
JAVA類載入器、註解和動態代理
一.類載入器 1.什麼是類載入器,作用是什麼 類載入器就載入位元組碼檔案(.class) 2.類載入器的種類 類載入器有三種,不同載入器載入不同 BootStrap:引導類載入器:載入都是最基礎的檔案 ExtClassLoader:擴充套
java基礎第十八篇之單元測試、註解和動態代理
1:單元測試 1)JUnit是一個Java語言的單元測試框架,這裡的單元指的就是方法 2)單元測試用來替換以前的main方法 1.1 Junit測試的步驟 1:在方法的上面加上 @Test 2:將junit庫新增到工程的構建路徑 3:選中方法--->右鍵--->JunitTest
反射、註解和動態代理
反射是指計算機程式在執行時訪問、檢測和修改它本身狀態或行為的一種能力,是一種超程式設計語言特性,有很多語言都提供了對反射機制的支援,它使程式能夠編寫程式。Java的反射機制使得Java能夠動態的獲取類的資訊和呼叫物件的方法。 一、Java反射機制及基本用法 在Java中,Class(類型別)是反射程式設計的起
java類載入器和動態代理
import java.lang.reflect.*; import java.util.ArrayList; import java.util.Collection; public class ProxyTest { public static void main(String[] args) throw
Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例
【正文】Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例 本小節目錄 9.1. 父載入器不能訪問子載入器的類 9.2. 一個寵物工廠介面 9.3. 一個寵物工廠管理類 9.4 APPClassLoader不能訪問子載入器中的類 9.5. 執行緒上下文
Java類載入器( CLassLoader ) 死磕8: 使用ASM,和類載入器實現AOP
【正文】Java類載入器( CLassLoader ) 死磕8: 使用ASM,和類載入器實現AOP 本小節目錄 8.1. ASM位元組碼操作框架簡介 8.2. ASM和訪問者模式 8.3. 用於增強位元組碼的事務類 8.4 通過ASM訪問註解 8.5. 通過ASM注入AOP事務程式
Java類載入器( CLassLoader ) 死磕 1、2: 匯入 & 分類
JAVA類載入器 死磕系列 目錄 by 瘋狂創客圈 1.匯入1.1. 從class檔案的載入開始1.2. 什麼是類載入器2. JAVA類載入器分類2.1. 作業系統的環境變數2.2. Bootstrap ClassLoader(啟動類載入器)2.3. Extention ClassL
Java原始碼分析——Class類、ClassLoader類解析(三) 類載入器、實現自定義類載入器
在這個系列的第一篇章就講解了Class類的獲取以及載入過程,但是並沒有提及具體的載入過程,在java中,載入一個類是通過ClassLoader類來執行的,也就是類載入器完成。java中所有的類,都必須載入進jvm中才能執行,這個載入的意思是
Java類載入器和雙親委派模型.md
0.類載入過程 一般來說,類載入分為3個過程,載入,連結和初始化。 1.載入階段,是Java將位元組碼資料從不同資料來源讀取到JVM中,並對映為JVM認可的Class物件,這裡的資料來源可能有Jar包,class檔案,甚至網路資料來源等。如果輸入資料不是ClassFile結構,則會丟
類載入器、反射,反射的應用例項(泛型擦除和配置檔案)
類載入器 1.1類的載入 當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會通過載入,連線,初始化三步來實現對這個類進行初始化。 1.1.1載入 指將class檔案讀入記憶體,併為之建立一個Class物件。 任何類被使用時系統都會建立一個Class物件(位元組碼檔案物件,建
十、JAVA多執行緒:JVM類載入器(自動類載入器、雙親委託機制、類載入器名稱空間、執行時包、類的解除安裝等)
Jvm提供了三大內建的類載入器,不同的類載入器負責將不同的類載入到記憶體之中 根載入器(Bootstrap ClassLoader) 是最頂層的載入器,是由C++編寫的,主要負責虛擬機器核心類庫的載入,如整個java.lang包,根載入器是獲取不到引用的,因此
java類載入器和雙親委派載入機制
java類載入器分類詳解 1、Bootstrap ClassLoader:啟動類載入器,也叫根類載入器,負責載入java的核心類庫,例如(%JAVA_HOME%/lib)目錄下的rt.jar(包含Sy
Java類載入器和雙親委派機制
前言 之前詳細介紹了Java類的整個載入過程(類載入機制詳解)。雖然,篇幅較長,但是也不要被內容嚇到了,其實每個階段都可以用一句話來概括。 1)載入:查詢並載入類的二進位制位元組流資料。 2)驗證:保證被載入的類的正確性。 3)準備:為類的靜態變數分配記憶體,並設定預設初始值。 4)解析:把類中的符號引用轉換
java類載入器——ClassLoader
web rac rgb 好的 全盤負責機制 安全 trac 字節 如何 Java的設計初衷是主要面向嵌入式領域,對於自己定義的一些類,考慮使用依需求載入原則。即在程序使用到時才載入類,節省內存消耗,這時就可以通過類載入器來動態載入。 假設你平時僅僅是做web開發,那應該
Java類載入器 ClassLoader的解析
index html dir obj ble body 6.4 odin 普通 //參考 : http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 類載入器基本概念 類載
JAVA類載入器詳解
Java類載入器的作用就是在執行時載入類。Java類載入器基於三個機制:委託、可見性和單一性。委託機制是指將載入一個類的請求交給父類載入器,如果這個父類載入器不能夠找到或者載入這個類,那麼再載入它。可見性的原理是子類的載入器可以看見所有的父類載入器載入的類,而父類載入器看不到子類載入器載入的
1.java類載入器?
Java類載入器ClassLoader總結 JAVA類裝載方式,有兩種: 1.隱式裝載, 程式在執行過程中當碰到通過new 等方式生成物件時,隱式呼叫類裝載器載入對應的類到jvm中。 2.顯式裝載, 通過class.forname()等方法,顯式載入需要的類 類載
Java類載入器(死磕5)
Java類載入器( CLassLoader ) 死磕5: 自定義一個檔案系統classLoader 本小節目錄 5.1. 自定義類載入器的基本流程 5.2. 入門案例:自定義檔案系統類載入器 5.3. 案例的環境配置 5.4 FileClassLoader
Java類載入器( 死磕9)
【正文】Java類載入器( CLassLoader ) 死磕9: 上下文載入器原理和案例 本小節目錄 9.1. 父載入器不能訪問子載入器的類 9.2. 一個寵物工廠介面 9.3. 一個寵物工廠管理類 9.4 APPClassLoader不能訪問子載入器中的類 9.5. 執行緒上下文
Java類載入器( 死磕7)
【正文】Java類載入器( CLassLoader )死磕7: 基於加密的自定義網路載入器 本小節目錄 7.1. 加密傳輸Server端的原始碼 7.2. 加密傳輸Client端的原始碼 7.3. 使用亦或實現簡單加密和解密演算法 7. 網路加密SafeClassLoader的原始