---------- android培訓(xùn)、java培訓(xùn)、期待與您交流! ----------
一、“代理概述”及“AOP概念”
(一)代理概述
1、問題:要為已存在的多個具有相同接口的目標(biāo)類的各個方法增加一些系統(tǒng)功能,例如,異常處理、日志、計算方法的運行時間、事務(wù)管理等等,如何去做?
解答:編寫一個與目標(biāo)類具有相同接口的代理類,代理類的每個方法調(diào)用目標(biāo)類的相同方法,并在調(diào)用方法時加上系統(tǒng)功能的代碼。
2、代理原理圖,如下:
3、代理的優(yōu)點
如果采用工廠模式和配置文件的方式進行管理,則不需要修改客戶端程序,在配置文件中配置是使用目標(biāo)類、還是代理類,這樣以后很容易切換。例如,想要日志功能時就配置代理類,否則配置目標(biāo)類,這樣,增加系統(tǒng)功能很容易,以后運行一段時間后,又想去掉系統(tǒng)功能也很容易。
(二)AOP概念
1、問題引入:
(1)系統(tǒng)中存在交叉業(yè)務(wù),一個交叉業(yè)務(wù)就是要切入到系統(tǒng)中的一個方面,如下所示:
安全 事務(wù) 日志
StudentService ———|——————|——————|—————
CourseService ———|——————|——————|—————
MiscService ———|——————|——————|—————
(2)用具體的程序代碼描述交叉業(yè)務(wù):
method1 method2 method3
{ { {
------------------------------------------切面
.... .... ......
------------------------------------------切面
} } }
2、AOP概念
(1)定義:交叉業(yè)務(wù)的編程問題即為面向方面的編程(aspect oriented PRogram ,簡稱AOP),AOP的目標(biāo)就是要使交叉業(yè)務(wù)模塊化。
(2)可以采用將切面代碼移動到原始方法的周圍,這與直接在方法中編寫切面代碼的運行效果是一樣的,如下所示:
----------------------------------------切面
func1 func2 func3
{ { {
.... .... ....
} } }
-----------------------------------------切面
總結(jié):(1)使用代理技術(shù)正好可以解決這種交叉業(yè)務(wù)模塊化的問題,代理是實現(xiàn)AOP功能的核心和關(guān)鍵技術(shù)。(2)安全,事務(wù),日志等功能要貫穿到好多個模塊中,所以,它們就是交叉業(yè)務(wù)。
二、動態(tài)代理技術(shù)
1、手動增加代理類存在的問題?
要為系統(tǒng)中的各種接口的類增加代理功能,則需要太多的代理類,全部采用靜態(tài)代理方式,就要寫成百上千個代理類,將是一件工作量巨大且非常麻煩的事情。
2、如何解決上述存在的問題?
JVM可以在“運行期”動態(tài)生成出類的字節(jié)碼,這種動態(tài)生成的類往往被用作代理類,即動態(tài)代理類。
3、動態(tài)類需注意細節(jié):
JVM生成的動態(tài)類必須實現(xiàn)一個或多個接口,這樣JVM就知道該實現(xiàn)什么方法。所以,JVM生成的動態(tài)類只能用作具有相同接口的目標(biāo)類的代理。
4、如果一個目標(biāo)類自身沒有實現(xiàn)接口,如何讓JVM動態(tài)生成的代理類與目標(biāo)類有相同的方法列表呢?
生成的代理類的方法聲明要不要和目標(biāo)類的方法一樣?要。但目標(biāo)類自身并沒有實現(xiàn)接口,那通過什么樣的方式告訴JVM生成的代理類與目標(biāo)類有相同的方法列表,JVM干不了這件事情,因為沒接口。
這時候有一個第三方CGLIB庫,CGLIB庫可以動態(tài)生成一個類的子類,一個類的子類也可以用作該類的代理,所以,如果要為一個沒有實現(xiàn)接口的類生成動態(tài)代理類,那么可以使用CGLIB庫。
5、在代理方法中什么位置可以插入系統(tǒng)功能代碼?
代理類的各個方法中通常除了要調(diào)用目標(biāo)的相應(yīng)方法和對外返回目標(biāo)返回的結(jié)果外,還可以在代理方法中的如下四個位置加上系統(tǒng)功能代碼:
(1)在調(diào)用目標(biāo)方法之前
(2)在調(diào)用目標(biāo)方法之后
(3)在調(diào)用目標(biāo)方法前后
(4)在處理目標(biāo)方法異常的catch塊中
三、JVM動態(tài)生成的類
(一)創(chuàng)建動態(tài)類及查看其方法列表信息
1、要求:
(1)創(chuàng)建實現(xiàn)了Collection接口的動態(tài)類和查看其名稱,分析Proxy.getProxyClass方法的各個參數(shù)。
(2)編碼列出動態(tài)類中的所有構(gòu)造方法和參數(shù)簽名
(3)編碼列出動態(tài)類中的所有方法和參數(shù)簽名
2、示例代碼:
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 import java.util.Collection; 5 public class ProxyTest { 6 /** 7 * @param args 8 * @throws SecurityException 9 * @throws NoSuchMethodException10 * @throws Exception11 * @throws IllegalArgumentException12 * @throws IllegalaccessException13 * @throws InstantiationException14 */15 public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, Exception {16 Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);17 System.out.println(clazzProxy1.getName());18 19 System.out.println("------------begin constructor list ------------");20 Constructor[] constructors = clazzProxy1.getConstructors();21 for(Constructor constructor : constructors){22 String name = constructor.getName();23 StringBuilder sb = new StringBuilder(name);24 sb.append("(");25 Class[] clazzParams = constructor.getParameterTypes();26 for(Class clazzParam : clazzParams ){27 sb.append(clazzParam.getName()).append(",");28 }29 if( clazzParams!=null && clazzParams.length!=0)30 sb.deleteCharAt(sb.length()-1);31 sb.append(")");32 System.out.println(sb);33 }34 35 System.out.println("------------begin method list -------------");36 Method[] methods = clazzProxy1.getMethods();37 for(Method method : methods){38 String name = method.getName();39 StringBuilder sb = new StringBuilder(name);40 sb.append("(");41 Class[] clazzParams = method.getParameterTypes();42 for(Class clazzParam : clazzParams ){43 sb.append(clazzParam.getName()).append(",");44 }45 if( clazzParams!=null && clazzParams.length!=0)46 sb.deleteCharAt(sb.length()-1);47 sb.append(")");48 System.out.println(sb);49 }50 }51 }
(二)創(chuàng)建動態(tài)類的實例對象及調(diào)用其方法
1、創(chuàng)建動態(tài)類的實例對象有三種方式:
(1)首先通過Proxy類的getProxyClass(ClassLoader loader, Class<?>... interfaces)方法,獲取代理類的對象;然后通過反射獲得構(gòu)造方法;最后通過接口InvocationHandler的子類創(chuàng)建對象;
(2)首先通過Proxy類的getProxyClass(ClassLoader loader, Class<?>... interfaces)方法,獲取代理類的對象;通過反射獲得構(gòu)造方法;通過給構(gòu)造方法的newInstance()傳入InvocationHandler的匿名內(nèi)部類來創(chuàng)建對象;
(3)直接通過Proxy類的newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法,創(chuàng)建對象。
2、創(chuàng)建動態(tài)類的實例對象的代碼實現(xiàn)
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 import java.lang.reflect.Proxy; 5 import java.util.ArrayList; 6 import java.util.Collection; 7 public class ProxyTest { 8 /** 9 * @param args10 * @throws SecurityException11 * @throws NoSuchMethodException12 * @throws Exception13 * @throws IllegalArgumentException14 * @throws IllegalAccessException15 * @throws InstantiationException16 */17 public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, Exception {18 Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);19 Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);20 21 System.out.println("------------begin create instance list --------------");22 //System.out.println("------------方式一:創(chuàng)建動態(tài)類的實例對象 ----------");23 class MyInvocationHandler1 implements InvocationHandler{24 @Override25 public Object invoke(Object proxy, Method method, Object[] args)26 throws Throwable {27 // TODO Auto-generated method stub28 return null;29 }30 }31 Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHandler1());32 System.out.println(proxy1);33 34 // System.out.println("------------方式二:創(chuàng)建動態(tài)類的實例對象 -----------");35 Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler(){36 @Override37 public Object invoke(Object proxy, Method method, Object[] args)38 throws Throwable {39 // TODO Auto-generated method stub40 return null;41 }42 });43 44 // System.out.println("------------方式三:創(chuàng)建動態(tài)類的實例對象 ------------");45 Collection proxy3 = (Collection)Proxy.newProxyInstance(46 Collection.class.getClassLoader(),47 new Class[]{Collection.class},48 new InvocationHandler(){49 ArrayList target = new ArrayList();50 @Override51 public Object invoke(Object proxy, Method method,52 Object[] args) throws Throwable {53 long beginTime = System.currentTimeMillis();54 Object retVal = method.invoke(target, args);55 long endTime = System.currentTimeMillis();56 System.out.println(method.getName()+"run time"+(endTime-beginTime));57 return retVal;58 }59 }60 );61 proxy3.add("zxx");62 proxy3.add("flx");63 proxy3.add("lhm");64 proxy3.add("bxd");65 proxy3.add("yzz"); 66 System.out.println(proxy3.size());67 }68 }
(三)總結(jié)思考
問題:讓JVM創(chuàng)建動態(tài)類及其實例對象,需要給它提供哪些信息?
解答:主要包括三個方面的信息:
(1)生成的類中有哪些方法,通過讓其實現(xiàn)哪些接口的方式進行告知;
(2)產(chǎn)生的類字節(jié)碼必須有個一個關(guān)聯(lián)的類加載器對象;
(3)生成的類中的方法的代碼是怎樣的,也得由我們提供。把我們的代碼寫在一個約定好了接口對象的方法中,把對象傳給它,它調(diào)用我的方法,即相當(dāng)于插入了我的代碼。提供執(zhí)行代碼的對象就是那個InvocationHandler對象,它是在創(chuàng)建動態(tài)類的實例對象的構(gòu)造方法時傳遞進去的。在上面的InvocationHandler對象的invoke方法中加一點代碼,就可以看到這些代碼被調(diào)用運行了。
* 用Proxy.newInstance方法直接一步就創(chuàng)建出代理對象。
四、動態(tài)生成的類的內(nèi)部代碼分析
在上面“創(chuàng)建動態(tài)類的實例對象”的代碼中,動態(tài)生成的類實現(xiàn)了Collection接口(可以實現(xiàn)若干接口),生成的類有Collection接口中的所有方法和一個如下接受InvocationHandler參數(shù)的構(gòu)造方法。
1、問題:構(gòu)造方法接受一個InvocationHandler對象,接收這個對象要干什么用呢?該方法內(nèi)部的代碼是怎樣的呢?
(1)構(gòu)造方法接收一個參數(shù),為了記住這個參數(shù),以后運用它。
(2)內(nèi)部代碼:
1 $Proxy0 implements Collection{2 InvocationHandler handler;3 public $Proxy0(InvocationHandler handler){4 this.handler = handler;5 }6 }
2、問題:實現(xiàn)Collection接口的動態(tài)類中的各個方法的代碼又是怎樣的呢? InvocationHandler接口中定義的invoke方法接收的三個參數(shù)又是什么意思?
1 (1)$Proxy0 implements Collection{ 2 InvocationHandler handler; 3 public $Proxy0(InvocationHandler handler){ 4 this.handler = handler; 5 } 6 //生成的Collection接口中的方法的運行原理 7 int size(){ 8 return handler.invoke(this, this.getClass().getMethod("size"), null); 9 }10 void clear(){11 handler.invoke(this, this.getClass().getMethod("clear"), null);12 }13 boolean add(Object obj){14 handler.invoke(this, this.getClass().getMethod("add"), obj);15 }16 }
(2)InvocationHandler接口中定義的invoke方法接收的三個參數(shù)意義,如下圖說明:
說明:客戶端調(diào)用了代理對象objProxy,調(diào)用了代理對象的add()方法,為該方法傳遞了字符串參數(shù)"abc"。
3、為什么動態(tài)類的實例對象的getClass()方法返回了正確結(jié)果呢?
調(diào)用調(diào)用代理對象的從Object類繼承的hashCode, equals, 或toString這幾個方法時,代理對象將調(diào)用請求轉(zhuǎn)發(fā)給InvocationHandler對象,對于其他方法,則不轉(zhuǎn)發(fā)調(diào)用請求。
五、動態(tài)生成的類成為目標(biāo)類的代理
1、動態(tài)代理的工作原理圖
2、eclipse重構(gòu)出一個getProxy方法綁定接收目標(biāo)同時返回代理對象,怎樣將目標(biāo)類作為參數(shù)傳進去?
(1)直接在InvocationHandler實現(xiàn)類中創(chuàng)建目標(biāo)類的實例對象,可以看運行效果和加入日志代碼,但沒有實際意義。
(2)為InvocationHandler實現(xiàn)類注入目標(biāo)類的實例對象,不能采用匿名內(nèi)部類的形式了。
(3)讓匿名的InvocationHandler實現(xiàn)類訪問外面方法中的目標(biāo)類實例對象的final類型的引用變量。
3、在上面將目標(biāo)類作為參數(shù)傳入之后,將系統(tǒng)功能代碼模塊化,即將切面代碼也改為通過參數(shù)形式提供,怎樣把要執(zhí)行的系統(tǒng)功能代碼以參數(shù)形式提供?
(1)把要執(zhí)行的代碼裝到一個對象的某個方法里,然后把這個對象作為參數(shù)傳遞,接收者只要調(diào)用這個對象的方法,即等于執(zhí)行了外界提供的代碼!
(2)為bind方法增加一個Advice參數(shù)。
4、將目標(biāo)類和系統(tǒng)功能作為參數(shù)傳遞給getProxy()方法,實現(xiàn)示例代碼如下:
(1)創(chuàng)建ProxyTest類
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 import java.lang.reflect.Proxy; 5 import java.util.ArrayList; 6 import java.util.Collection; 7 public class ProxyTest { 8 /** @param args 9 * @throws SecurityException10 * @throws NoSuchMethodException11 * @throws Exception 12 * @throws IllegalArgumentException13 * @throws IllegalAccessException14 * @throws InstantiationException15 */16 public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, Exception {17 final ArrayList target = new ArrayList();//將目標(biāo)抽取出來,方法里面的內(nèi)部類要訪問局部變量必須添加final關(guān)鍵字 18 Collection proxy3 = (Collection)getProxy(target,new MyAdvice());//抽取出來的方法19 proxy3.add("zxx");20 proxy3.add("flx");21 proxy3.add("lhm"); 22 System.out.println(proxy3.size());23 }24 private static Object getProxy(final Object target,final Advice advice) { /*做成通用的方法,返回Object*/25 Object proxy3 = Proxy.newProxyInstance(26 /*Collection.class.getClassLoader(), //第一個參數(shù)*/27 target.getClass().getClassLoader(), // 代理類的類加載器與目標(biāo)類的類加載器相同,與目標(biāo)類有關(guān)。28 29 /*new Class[]{Collection.class}, //第二個參數(shù)*/30 target.getClass().getInterfaces(), //與target實現(xiàn)相同的接口,代理類要實現(xiàn)的接口也是目標(biāo)類實現(xiàn)的接口,與目標(biāo)類有關(guān)31 32 new InvocationHandler(){ //第三個參數(shù),33 @Override34 public Object invoke(Object proxy, Method method,35 Object[] args) throws Throwable {36 /*37 long beginTime = System.currentTimeMillis(); //將系統(tǒng)功能抽取為一個對象38 Object retVal = method.invoke(target, args);39 long endTime = System.currentTimeMillis(); //將系統(tǒng)功能抽取為一個對象 40 System.out.println(method.getName()+"run time"+(endTime-beginTime));41 return retVal;42 */43 advice.beforeMethod(method);44 Object retVal = method.invoke(target, args);45 advice.afterMethod(method); 46 return retVal;47 }48 }49 );50 return proxy3;51 }52 }
(2)創(chuàng)建Advice接口
import java.lang.reflect.Method;public interface Advice { //一般來說,這個建議的接口應(yīng)該有四個方法,這四個方法可以分別插入: /* 代理類的各個方法中通常除了要調(diào)用目標(biāo)的相應(yīng)方法和對外返回目標(biāo)返回的結(jié)果外, * 還可以在代理方法中的如下四個位置加上系統(tǒng)功能代碼: * 1.在調(diào)用目標(biāo)方法之前 * 2.在調(diào)用目標(biāo)方法之后 * 3.在調(diào)用目標(biāo)方法前后 * 4.在處理目標(biāo)方法異常的catch塊中 * */ void beforeMethod(Method method); void afterMethod(Method method); }
(3)創(chuàng)建Advice接口的子類MyAdvice
1 import java.lang.reflect.Method; 2 public class MyAdvice implements Advice { 3 long beginTime = 0; 4 public void beforeMethod(Method method) { 5 // TODO Auto-generated method stub 6 System.out.println("到黑馬程序員訓(xùn)練營來學(xué)習(xí)了!"); 7 beginTime = System.currentTimeMillis(); //將系統(tǒng)功能抽取為一個對象 8 } 9 10 public void afterMethod(Method method) {11 // TODO Auto-generated method stub12 System.out.println("從黑馬程序員訓(xùn)練營畢業(yè)工作了!");13 long endTime = System.currentTimeMillis(); //將系統(tǒng)功能抽取為一個對象 14 System.out.println(method.getName()+" method run of time "+(endTime-beginTime));15 System.out.print(System.lineSeparator() );16 }17 }
六、實現(xiàn)類似spring的可配置的AOP框架
(一)工廠類BeanFactory
1、工廠類BeanFactory:負責(zé)創(chuàng)建目標(biāo)類或代理類的實例對象,并通過配置文件實現(xiàn)切換。
2、getBean方法:根據(jù)參數(shù)字符串返回一個相應(yīng)的實例對象。如果參數(shù)字符串在配置文件中對應(yīng)的類名不是ProxyFactoryBean,則直接返回該類的實例對象,否則返回該類示例對象的getProxy方法返回的對象。
3、BeanFactory的構(gòu)造方法:接收代表配置文件的輸入流對象的配置文件。
4、ProxyFactoryBean為BeanFactory提供配置參數(shù)信息:(1)目標(biāo)(target)(2)通告(advice)
5、BeanFactory和ProxyFactoryBean:
(1)BeanFactory是一個純粹的bean工程,就是創(chuàng)建bean即相應(yīng)的對象的工廠。
(2)ProxyfactoryBean是BeanFactory中的一個特殊的Bean,是創(chuàng)建代理的工廠。
(二)實現(xiàn)類似spring的可配置的AOP框架的思路:
1、創(chuàng)建BeanFactory類:
(1)構(gòu)造方法:接受一個配置文件,通過Properties對象加載InputStream流對象。
(2)創(chuàng)建getBean(String name)方法:根據(jù)類名name,拿到對應(yīng)的類名className。
(3)根據(jù)類名獲取其字節(jié)碼對象,并創(chuàng)建實例對象bean。
(4)判斷bean是否是特殊的Bean即ProxyFactoryBean。
① 如果是,就要創(chuàng)建代理類,并設(shè)置目標(biāo)(target)和通告(advice),分別得到各自的實例對象,并返回代理類實例對象。
② 如果不是在返回Bean對象自己。
2、創(chuàng)建ProxyFactoryBean類,定義target和advice;定義getProxy()方法,用于創(chuàng)建代理類對象。
3、創(chuàng)建配置文件config.properties,對配置文件進行配置,配置內(nèi)容如下
xxx=java.util.ArrayList
#xxx=cn.itheima.day3.aopframework.ProxyFactoryBean
xxx.advice=cn.itheima.day3.MyAdvice
xxx.target=java.util.ArrayList
注: #表示注釋當(dāng)前行。
4、作一個測試類:AopFrameworkTest進行測試。
(三)完整代碼示例
1、創(chuàng)建BeanFactory類:
package cn.itheima.day3.aopframework;import java.io.IOException;import java.io.InputStream;import java.util.Properties;import cn.itheima.day3.Advice;public class BeanFactory { Properties props = new Properties(); public BeanFactory(InputStream ips){ try { props.load(ips); } catch (IOException e) { e.printStackTrace(); } } public Object getBean(String name){ String className = props.getProperty(name);//根據(jù)類名name,拿到對應(yīng)的類名。 Object bean = null; try { Class clazz = Class.forName(className); bean = clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } if(bean instanceof ProxyFactoryBean){ Object proxy = null; ProxyFactoryBean ProxyFactoryBean = (ProxyFactoryBean)bean; try { Advice advice = (Advice)Class.forName(props.getProperty(name+".advice")).newInstance(); Object target = Class.forName(props.getProperty(name+".target")).newInstance(); ProxyFactoryBean.setAdvice(advice); ProxyFactoryBean.setTarget(target); proxy = ProxyFactoryBean.getProxy(); } catch (Exception e) { e.printStackTrace(); } return proxy; } return bean; }}
2、創(chuàng)建ProxyFactoryBean類:
1 package cn.itheima.day3.aopframework; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 import cn.itheima.day3.Advice; 7 public class ProxyFactoryBean { 8 private Advice advice; 9 private Object target;10 public Advice getAdvice() {11 return advice;12 }13 public void setAdvice(Advice advice) {14 this.advice = advice;15 }16 public Object getTarget() {17 return target;18 }19 public void setTarget(Object target) {20 this.target = target;21 }22 public Object getProxy() {23 Object proxy3 = Proxy.newProxyInstance( 24 target.getClass().getClassLoader(), // 代理類的類加載器與目標(biāo)類的類加載器相同,與目標(biāo)類有關(guān)。25 target.getClass().getInterfaces(), //與target實現(xiàn)相同的接口,代理類要實現(xiàn)的接口也是目標(biāo)類實現(xiàn)的接口,與目標(biāo)類有關(guān)26 27 new InvocationHandler(){ //第三個參數(shù)28 @Override29 public Object invoke(Object proxy, Method method,30 Object[] args) throws Throwable {31 advice.beforeMethod(method);32 Object retVal = method.invoke(target, args);33 advice.afterMethod(method);34 return retVal;35 }36 }37 );38 return proxy3;39 }40 }
3、創(chuàng)建配置文件config.properties。
xxx=java.util.ArrayList#xxx=cn.itheima.day3.aopframework.ProxyFactoryBeanxxx.advice=cn.itheima.day3.MyAdvicexxx.target=java.util.ArrayList
4、創(chuàng)建AopFrameworkTest測試類,進行測試:
1 package cn.itheima.day3.aopframework; 2 3 import java.io.InputStream; 4 public class AopFrameworkTest { 5 /** 6 * @param args 7 */ 8 public static void main(String[] args) { 9 // TODO Auto-generated method stub10 InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");11 Object bean = new BeanFactory(ips).getBean("xxx");12 System.out.println(bean.getClass().getName());13 }14 }
5、設(shè)計到的接口Advice及其子類MyAdvice
(1)Advice接口:
1 package cn.itheima.day3;2 import java.lang.reflect.Method;3 public interface Advice { 4 void beforeMethod(Method method);5 void afterMethod(Method method);6 }
(2)MyAdvice類:
1 package cn.itheima.day3; 2 import java.lang.reflect.Method; 3 public class MyAdvice implements Advice { 4 long beginTime = 0; 5 public void beforeMethod(Method method) { 6 // TODO Auto-generated method stub 7 System.out.println("到黑馬程序員訓(xùn)練營來學(xué)習(xí)了!"); 8 beginTime = System.currentTimeMillis(); //將系統(tǒng)功能抽取為一個對象 9 }10 11 public void afterMethod(Method method) {12 // TODO Auto-generated method stub13 System.out.println("從黑馬程序員訓(xùn)練營畢業(yè)工作了!");14 long endTime = System.currentTimeMillis(); //將系統(tǒng)功能抽取為一個對象 15 System.out.println(method.getName()+" method run of time "+(endTime-beginTime));16 System.out.print(System.lineSeparator() );17 }18 }
---------- android培訓(xùn)、java培訓(xùn)、期待與您交流! ----------
新聞熱點
疑難解答