javascript
JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架...
1、類加載器
·簡要介紹什么是類加載器,和類加載器的作用
·Java虛擬機中可以安裝多個類加載器,系統默認三個主要類加載器,每個類負責加載特定位置的類:BootStrap,ExtClassLoader,AppClassLoader
·類加載器也是Java類,因為其他是Java的類加載器本身也要被類加載器加載,顯然必須有第一個加載器不是Java類,這正是BootStrap
·Java虛擬機中的所有類加載器采用具有樹形結構進行組織,在實例化每個類裝載器對象時,需要為其指定一個父類級的裝載器對像或者采用系統默認類裝載器為其父類加載
·類加載器之間的父子關系和管轄范圍:
?
注意點:用Eclipse的打包工具將ClassLoaderTest輸出成jre/lib/ext目錄下的shanhw.jar包,在用Eclipse中運行這個類,運行結果顯示為ExeClassLoader。此時環境狀態是classpath目錄有ClassLoaderTest.class,ext/shanhw.jar包中也有ClassLoarderTest.class,這時候,我們就需要了解類加載的具體過程和原理了
類加載器的委托機制:
·當Java虛擬機要加載一個類時,到底派出那個類加載器去加載呢?1、首先在當前線程的類加載器去在加載線程中的第一個類 2、如果類中A引用了類B,Java虛擬機將使用裝載A的裝載器來裝載B 3、還有可以直接調用ClassLoader.loadClass();方法來指定某個類加載器去加載某個類
· 每個類加載器加載時,優先委托給其上級類加載器;
2、編寫自己的類加載器
自定義類加載器,例子:寫了三個類,1、MyClassLoaderTest.java 2、MyClassLoader.java 3、ClassLoaderAttachment.java
MyClassLoaderTest.java
1 package com.shanhw.javaEnhance.thirteenthDay; 2 3 import java.util.Date; 4 5 6 /** 7 * 自定義類加載器 8 */ 9 public class MyClassLoaderTest { 10 11 public static void main(String[] args) throws Exception { 12 String path = "E:\\Kingsoft Cloud\\Workspaces\\TestExample\\shanhwlib\\ClassLoaderAttachment.class"; 13 Class<?> clzz = new MyClassLoader(path).loadClass("com.shanhw.javaEnhance.thirteenthDay.ClassLoaderAttachment"); 14 Date d = (Date)clzz.newInstance(); 15 System.out.println(d); 16 } 17 18 }MyClassLoader.java
1 package com.shanhw.javaEnhance.thirteenthDay; 2 3 import java.io.ByteArrayOutputStream; 4 import java.io.FileInputStream; 5 import java.io.InputStream; 6 import java.io.OutputStream; 7 8 public class MyClassLoader extends ClassLoader{ 9 10 private static void cypher(InputStream is,OutputStream os) throws Exception{ 11 int i = 0; 12 while((i = is.read()) != -1){ 13 os.write(i ^ 0xff); 14 } 15 } 16 17 private String classDir; 18 19 public MyClassLoader(String classDir){ 20 this.classDir = classDir; 21 } 22 23 @SuppressWarnings("deprecation") 24 @Override 25 protected Class<?> findClass(String name) throws ClassNotFoundException { 26 try { 27 28 FileInputStream fis = new FileInputStream(classDir); 29 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 30 cypher(fis,baos); 31 fis.close(); 32 byte[] bytes = baos.toByteArray(); 33 return defineClass(bytes, 0,bytes.length); 34 } catch (Exception e) { 35 throw new RuntimeException("運行時異常"); 36 } 37 } 38 }ClassLoaderAttachment.java
1 package com.shanhw.javaEnhance.thirteenthDay; 2 3 import java.util.Date; 4 5 public class ClassLoaderAttachment extends Date { 6 /** 7 * 8 */ 9 private static final long serialVersionUID = 1L; 10 11 public String toString(){ 12 return "Hello Houwang Shan!"; 13 } 14 }3、分析代理類的作用與原理及AOP概念
·生活中的代理:武漢人從武漢代理商手中買聯想電腦和直接跑到北京聯想總部買電腦,你覺得最終業務的主體業務目標有什么區別嗎?基本上一樣,解決了核心問題,從代理商那里買,是不是更方便一些?
·程序中的代理:1、要為已經存在的多個具有相同接口的目標類的各個方法增加一些功能,例如:異常處理、日志、計算方法的運行時間、事物管理等等,你準備如何做?2、編寫一個目標類具有相同接口的代理類,代理類的每個方法調用目標類的相同方法,并在調用方法時加上系統功能代碼、如圖:
3、如果采用工廠模式和配置文件的方式進行管理,則不需要修改客戶端程序,在配置文件中配置是使用目標類、還是代理類,這樣以后很容易切換,譬如,想要日志功能時,就配置代理類,否則配置目標類,這樣,增加系統功能很容易,以后運行一段時間后,又想去掉系統功能也很容易
·AOP
1、系統中存在交叉業務,一個交叉業務就是要切入到系統的一個方面,(安全、事物、日志等要貫穿到好多的模塊中,所以它們就是交叉業務)如下所示:
2、用具體的程序代碼描述交叉業務
3、交叉業務的編碼問題即為面向方面的編程(Aspect Oriented Program 簡稱AOP),AOP的目標就是使用交叉業務模塊兒化。可以采用將切面代碼移動到原始方法的周圍,這與直接在方法中編寫切面代碼運行效果是一樣的,如下所示:
4、使用代理技術正好可以解決這種問題,代理是實現AOP功能的核心和關鍵技術
·動態代理技術
1、要為系統中的各種接口的類增加代理功能,那將需要太多的代理類,全部采用靜態代理的方式,將是一件非常麻煩的事情,寫成成百上千的代理類,是不是太累了
2、JVM可以在運行期動態生成類的字節碼,這種動態生成的類,往往被用作代理類,即動態代理
3、JVM生成動態類必須實現一個或者多個接口,所以JVM生成的動態類只能用作具有相同接口的目標類的代理
4、CGLIB庫可以動態生成一個類的子類,一個類的子類也可以用作該類的代理,所以,如果要為一個沒有實現接口的類生成動態代理,那么可以使用CGLIB庫
5、代理類的各個方法中通常除了要調用目標的相應方法和對外返回目標返回的結果外,還可以在代理方法中的如下四個位置加上系統功能代碼:
******1、在調用目標方法之前 2、在調用目標方法之后 3、在調用目標方法前后 4、在處理目標方法異常的catch塊中
·分析JVM動態生成的類
·1、創建實現了Collection接口的動態類和查看其名稱,分析Proxy.getProxyClass方法的各個參數
·2、編碼列出動態類中的所有構造方法和參數簽名
·3、編碼列出動態類中的所有方法和參數簽名
·4、創建動態類的實例對象
1、用反射獲得動態方法
2、編寫一個簡單的InvocationHandle類
3、調用構造方法創建動態類的實例對象,并將編寫的InvocationHandler類的實例對象傳進去
4、打印創建的對象和調用的對象的沒有返回值和getClass方法,演示調用其他返回值的方法報告了異常
5、將創建動態類的實例對象的代理改成匿名內部類的形式編寫,鍛煉習慣匿名內部類
1、例子:ProxyTest.java
1 package com.shanhw.javaEnhance.thirteenthDay; 2 3 import java.lang.reflect.Constructor; 4 import java.lang.reflect.InvocationHandler; 5 import java.lang.reflect.Method; 6 import java.lang.reflect.Proxy; 7 import java.util.ArrayList; 8 import java.util.Collection; 9 10 public class ProxyTest { 11 12 /** 13 * 14 */ 15 public static void main(String[] args) throws Exception{ 16 Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); 17 System.out.println("----------Constructors:----------"); 18 Constructor[] constructors = clazzProxy.getConstructors(); 19 for(Constructor constructor:constructors){ 20 String consName = constructor.getName(); 21 StringBuilder str = new StringBuilder(consName); 22 str.append('('); 23 Class[] clazzParas = constructor.getParameterTypes(); 24 for(Class clazzPara : clazzParas){ 25 str.append(clazzPara.getName()).append(','); 26 } 27 if(clazzParas.length != 0){ 28 str.deleteCharAt(str.length()-1); 29 } 30 str.append(')'); 31 System.out.println(str); 32 } 33 System.out.println("----------Methods:----------"); 34 Method[] methods = clazzProxy.getMethods(); 35 for(Method method:methods){ 36 String consName = method.getName(); 37 StringBuilder str = new StringBuilder(consName); 38 str.append('('); 39 Class[] clazzParas = method.getParameterTypes(); 40 for(Class clazzPara : clazzParas){ 41 str.append(clazzPara.getName()).append(','); 42 } 43 if(clazzParas.length != 0){ 44 str.deleteCharAt(str.length()-1); 45 } 46 str.append(')'); 47 System.out.println(str); 48 } 49 System.out.println("----------創建實例對象:----------"); 50 Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class); 51 52 Collection collection = (Collection)constructor.newInstance(new InvocationHandler(){ 53 54 @Override 55 public Object invoke(Object proxy, Method method, Object[] args) 56 throws Throwable { 57 return null; 58 }}); 59 Collection proxy = (Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(), 60 new Class[]{Collection.class}, 61 new InvocationHandler(){ 62 ArrayList arrayList = new ArrayList(); 63 @Override 64 public Object invoke(Object proxy, Method method, 65 Object[] args) throws Throwable { 66 return method.invoke(arrayList, args); 67 } 68 }); 69 proxy.add("a"); 70 proxy.add("a"); 71 proxy.add("a"); 72 System.out.println(proxy.size()); 73 } 74 }?內部的原理:猜想分析動態生成的類的內部代碼?
·動態生成的類實現了Collection接口(可以實現若干接口),生成的類有Collection接口中的所有方法和一個如下接受InvocationHandler參數的構造方法
·構造方法接受一個InvocationHandler對象,接受對象干什么了呢?該方法內部類的代碼會是怎么樣的呢?
·實現的Collection接口中的各個方法的代碼又是怎么樣的呢?InvocationHandler接口中定義的invoke方法接受的三個參數是什么意思呢?
·讓動態生成的類,成為目標類的代理
1、分析動態代理的工作原理圖:
2、怎樣將目標類傳進去?
****1、在InvocationHandler實現類中創建目標類的實例對象,可以看運行效果和加入日志代碼,但是沒有實際意義
****2、為InvocationHandler實現類注入目標類的實例對象,不能采用匿名內部類的形式了
****3、讓匿名的InvocationHandler實現訪問外面方法中的目標類實例對象的final類型的引用變量
3、將創建代理的過程改為一種更優雅的方式,eclipse重構出一個getProxy方法綁定接受目標同時返回代理對象,讓調用者更懶惰,更方便,調用者甚至不用接觸任何代理的API
4、把系統功能代碼模塊化,即將切面代碼也改為通過參數形式提供,怎樣把要執行的系統功能代碼從參數形式提供?
****1、把要執行的代碼裝到一個對象的某個方法里,然后把這個對象作為參數傳遞,接收者只要調用這個對象的方法,即等于執行外界提供的代碼。
****2、為bind方法增加一個Advice參數。
·實現類似Spring的可配置的AOP框架
(實現思路):1、工廠類BeanFactory負責創建目標類或代理類的實例對象,并通過配置文件實現切換,其getBean方法根據參數字符串返回一個相應的實例對象,如果參數字符串在配置文件中對應的類名不是ProxyFactoryBean,則直接返回該類的實例對象,否則,返回該類實例對象的getProxy方法返回的對象
2、BeanFactory的構造方法接收代表配置文件的輸入流對象,配置文件格式如下:xxx = java.util.ArrayList #xxx = cn.shanhw.ProxyFactoryBean xxx.target=java.util.ArrayList xxx = advice.shanhw.MyAdvice
·ProxyFactoryBean充當封裝生成動態代理的工廠,需要為工廠類提供哪些配置參數信息:1、目標 2、通知
·編寫客戶端應用 1、編寫實現Advice接口的類和配置文件中進行配置 2、調用BeanFactory獲取對象
轉載于:https://www.cnblogs.com/shanhouwang/archive/2013/03/24/2979737.html
總結
以上是生活随笔為你收集整理的JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 运行管理员线程和用户线程小练习
- 下一篇: ActiveXObject函数详解