javascript
JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架...
1、類加載器
·簡(jiǎn)要介紹什么是類加載器,和類加載器的作用
·Java虛擬機(jī)中可以安裝多個(gè)類加載器,系統(tǒng)默認(rèn)三個(gè)主要類加載器,每個(gè)類負(fù)責(zé)加載特定位置的類:BootStrap,ExtClassLoader,AppClassLoader
·類加載器也是Java類,因?yàn)槠渌荍ava的類加載器本身也要被類加載器加載,顯然必須有第一個(gè)加載器不是Java類,這正是BootStrap
·Java虛擬機(jī)中的所有類加載器采用具有樹形結(jié)構(gòu)進(jìn)行組織,在實(shí)例化每個(gè)類裝載器對(duì)象時(shí),需要為其指定一個(gè)父類級(jí)的裝載器對(duì)像或者采用系統(tǒng)默認(rèn)類裝載器為其父類加載
·類加載器之間的父子關(guān)系和管轄范圍:
?
注意點(diǎn):用Eclipse的打包工具將ClassLoaderTest輸出成jre/lib/ext目錄下的shanhw.jar包,在用Eclipse中運(yùn)行這個(gè)類,運(yùn)行結(jié)果顯示為ExeClassLoader。此時(shí)環(huán)境狀態(tài)是classpath目錄有ClassLoaderTest.class,ext/shanhw.jar包中也有ClassLoarderTest.class,這時(shí)候,我們就需要了解類加載的具體過程和原理了
類加載器的委托機(jī)制:
·當(dāng)Java虛擬機(jī)要加載一個(gè)類時(shí),到底派出那個(gè)類加載器去加載呢?1、首先在當(dāng)前線程的類加載器去在加載線程中的第一個(gè)類 2、如果類中A引用了類B,Java虛擬機(jī)將使用裝載A的裝載器來裝載B 3、還有可以直接調(diào)用ClassLoader.loadClass();方法來指定某個(gè)類加載器去加載某個(gè)類
· 每個(gè)類加載器加載時(shí),優(yōu)先委托給其上級(jí)類加載器;
2、編寫自己的類加載器
自定義類加載器,例子:寫了三個(gè)類,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("運(yùn)行時(shí)異常"); 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概念
·生活中的代理:武漢人從武漢代理商手中買聯(lián)想電腦和直接跑到北京聯(lián)想總部買電腦,你覺得最終業(yè)務(wù)的主體業(yè)務(wù)目標(biāo)有什么區(qū)別嗎?基本上一樣,解決了核心問題,從代理商那里買,是不是更方便一些?
·程序中的代理:1、要為已經(jīng)存在的多個(gè)具有相同接口的目標(biāo)類的各個(gè)方法增加一些功能,例如:異常處理、日志、計(jì)算方法的運(yùn)行時(shí)間、事物管理等等,你準(zhǔn)備如何做?2、編寫一個(gè)目標(biāo)類具有相同接口的代理類,代理類的每個(gè)方法調(diào)用目標(biāo)類的相同方法,并在調(diào)用方法時(shí)加上系統(tǒng)功能代碼、如圖:
3、如果采用工廠模式和配置文件的方式進(jìn)行管理,則不需要修改客戶端程序,在配置文件中配置是使用目標(biāo)類、還是代理類,這樣以后很容易切換,譬如,想要日志功能時(shí),就配置代理類,否則配置目標(biāo)類,這樣,增加系統(tǒng)功能很容易,以后運(yùn)行一段時(shí)間后,又想去掉系統(tǒng)功能也很容易
·AOP
1、系統(tǒng)中存在交叉業(yè)務(wù),一個(gè)交叉業(yè)務(wù)就是要切入到系統(tǒng)的一個(gè)方面,(安全、事物、日志等要貫穿到好多的模塊中,所以它們就是交叉業(yè)務(wù))如下所示:
2、用具體的程序代碼描述交叉業(yè)務(wù)
3、交叉業(yè)務(wù)的編碼問題即為面向方面的編程(Aspect Oriented Program 簡(jiǎn)稱AOP),AOP的目標(biāo)就是使用交叉業(yè)務(wù)模塊兒化。可以采用將切面代碼移動(dòng)到原始方法的周圍,這與直接在方法中編寫切面代碼運(yùn)行效果是一樣的,如下所示:
4、使用代理技術(shù)正好可以解決這種問題,代理是實(shí)現(xiàn)AOP功能的核心和關(guān)鍵技術(shù)
·動(dòng)態(tài)代理技術(shù)
1、要為系統(tǒng)中的各種接口的類增加代理功能,那將需要太多的代理類,全部采用靜態(tài)代理的方式,將是一件非常麻煩的事情,寫成成百上千的代理類,是不是太累了
2、JVM可以在運(yùn)行期動(dòng)態(tài)生成類的字節(jié)碼,這種動(dòng)態(tài)生成的類,往往被用作代理類,即動(dòng)態(tài)代理
3、JVM生成動(dòng)態(tài)類必須實(shí)現(xiàn)一個(gè)或者多個(gè)接口,所以JVM生成的動(dòng)態(tài)類只能用作具有相同接口的目標(biāo)類的代理
4、CGLIB庫可以動(dòng)態(tài)生成一個(gè)類的子類,一個(gè)類的子類也可以用作該類的代理,所以,如果要為一個(gè)沒有實(shí)現(xiàn)接口的類生成動(dòng)態(tài)代理,那么可以使用CGLIB庫
5、代理類的各個(gè)方法中通常除了要調(diào)用目標(biāo)的相應(yīng)方法和對(duì)外返回目標(biāo)返回的結(jié)果外,還可以在代理方法中的如下四個(gè)位置加上系統(tǒng)功能代碼:
******1、在調(diào)用目標(biāo)方法之前 2、在調(diào)用目標(biāo)方法之后 3、在調(diào)用目標(biāo)方法前后 4、在處理目標(biāo)方法異常的catch塊中
·分析JVM動(dòng)態(tài)生成的類
·1、創(chuàng)建實(shí)現(xiàn)了Collection接口的動(dòng)態(tài)類和查看其名稱,分析Proxy.getProxyClass方法的各個(gè)參數(shù)
·2、編碼列出動(dòng)態(tài)類中的所有構(gòu)造方法和參數(shù)簽名
·3、編碼列出動(dòng)態(tài)類中的所有方法和參數(shù)簽名
·4、創(chuàng)建動(dòng)態(tài)類的實(shí)例對(duì)象
1、用反射獲得動(dòng)態(tài)方法
2、編寫一個(gè)簡(jiǎn)單的InvocationHandle類
3、調(diào)用構(gòu)造方法創(chuàng)建動(dòng)態(tài)類的實(shí)例對(duì)象,并將編寫的InvocationHandler類的實(shí)例對(duì)象傳進(jìn)去
4、打印創(chuàng)建的對(duì)象和調(diào)用的對(duì)象的沒有返回值和getClass方法,演示調(diào)用其他返回值的方法報(bào)告了異常
5、將創(chuàng)建動(dòng)態(tài)類的實(shí)例對(duì)象的代理改成匿名內(nèi)部類的形式編寫,鍛煉習(xí)慣匿名內(nèi)部類
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("----------創(chuàng)建實(shí)例對(duì)象:----------"); 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 }?內(nèi)部的原理:猜想分析動(dòng)態(tài)生成的類的內(nèi)部代碼?
·動(dòng)態(tài)生成的類實(shí)現(xiàn)了Collection接口(可以實(shí)現(xiàn)若干接口),生成的類有Collection接口中的所有方法和一個(gè)如下接受InvocationHandler參數(shù)的構(gòu)造方法
·構(gòu)造方法接受一個(gè)InvocationHandler對(duì)象,接受對(duì)象干什么了呢?該方法內(nèi)部類的代碼會(huì)是怎么樣的呢?
·實(shí)現(xiàn)的Collection接口中的各個(gè)方法的代碼又是怎么樣的呢?InvocationHandler接口中定義的invoke方法接受的三個(gè)參數(shù)是什么意思呢?
·讓動(dòng)態(tài)生成的類,成為目標(biāo)類的代理
1、分析動(dòng)態(tài)代理的工作原理圖:
2、怎樣將目標(biāo)類傳進(jìn)去?
****1、在InvocationHandler實(shí)現(xiàn)類中創(chuàng)建目標(biāo)類的實(shí)例對(duì)象,可以看運(yùn)行效果和加入日志代碼,但是沒有實(shí)際意義
****2、為InvocationHandler實(shí)現(xiàn)類注入目標(biāo)類的實(shí)例對(duì)象,不能采用匿名內(nèi)部類的形式了
****3、讓匿名的InvocationHandler實(shí)現(xiàn)訪問外面方法中的目標(biāo)類實(shí)例對(duì)象的final類型的引用變量
3、將創(chuàng)建代理的過程改為一種更優(yōu)雅的方式,eclipse重構(gòu)出一個(gè)getProxy方法綁定接受目標(biāo)同時(shí)返回代理對(duì)象,讓調(diào)用者更懶惰,更方便,調(diào)用者甚至不用接觸任何代理的API
4、把系統(tǒng)功能代碼模塊化,即將切面代碼也改為通過參數(shù)形式提供,怎樣把要執(zhí)行的系統(tǒng)功能代碼從參數(shù)形式提供?
****1、把要執(zhí)行的代碼裝到一個(gè)對(duì)象的某個(gè)方法里,然后把這個(gè)對(duì)象作為參數(shù)傳遞,接收者只要調(diào)用這個(gè)對(duì)象的方法,即等于執(zhí)行外界提供的代碼。
****2、為bind方法增加一個(gè)Advice參數(shù)。
·實(shí)現(xiàn)類似Spring的可配置的AOP框架
(實(shí)現(xiàn)思路):1、工廠類BeanFactory負(fù)責(zé)創(chuàng)建目標(biāo)類或代理類的實(shí)例對(duì)象,并通過配置文件實(shí)現(xiàn)切換,其getBean方法根據(jù)參數(shù)字符串返回一個(gè)相應(yīng)的實(shí)例對(duì)象,如果參數(shù)字符串在配置文件中對(duì)應(yīng)的類名不是ProxyFactoryBean,則直接返回該類的實(shí)例對(duì)象,否則,返回該類實(shí)例對(duì)象的getProxy方法返回的對(duì)象
2、BeanFactory的構(gòu)造方法接收代表配置文件的輸入流對(duì)象,配置文件格式如下:xxx = java.util.ArrayList #xxx = cn.shanhw.ProxyFactoryBean xxx.target=java.util.ArrayList xxx = advice.shanhw.MyAdvice
·ProxyFactoryBean充當(dāng)封裝生成動(dòng)態(tài)代理的工廠,需要為工廠類提供哪些配置參數(shù)信息:1、目標(biāo) 2、通知
·編寫客戶端應(yīng)用 1、編寫實(shí)現(xiàn)Advice接口的類和配置文件中進(jìn)行配置 2、調(diào)用BeanFactory獲取對(duì)象
轉(zhuǎn)載于:https://www.cnblogs.com/shanhouwang/archive/2013/03/24/2979737.html
總結(jié)
以上是生活随笔為你收集整理的JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 运行管理员线程和用户线程小练习
- 下一篇: ActiveXObject函数详解