上一篇文章詳細(xì)介紹了靜態(tài)代理和動(dòng)態(tài)代理的作用和實(shí)現(xiàn)方式,并介紹了動(dòng)態(tài)代理實(shí)現(xiàn)的兩種方式。熟練掌握反射技術(shù)是一個(gè)程序員走向高級(jí)的必備技能,今天我們來了解一下如何用反射來實(shí)現(xiàn)自定義的 AOP 代碼,結(jié)合配置文件的使用,完成一個(gè)反射高級(jí)應(yīng)用的實(shí)例,大家感受一下反射的魅力。
為了學(xué)習(xí)的連貫性,強(qiáng)烈建議大家先閱讀 代理設(shè)計(jì)模式與AOP 一文,然后再來閱讀下面的內(nèi)容。
一、如何統(tǒng)計(jì)方法執(zhí)行的時(shí)間?
在日常開發(fā)中,我們經(jīng)常會(huì)用到一個(gè)功能就是統(tǒng)計(jì)一個(gè)服務(wù)或者是一個(gè)方法的執(zhí)行時(shí)間,也就是說在進(jìn)入方法開始執(zhí)行到方法執(zhí)行完畢,總共耗費(fèi)的時(shí)間是多少,用來評(píng)估一個(gè)方法的執(zhí)行效率。
初級(jí)工程師 A 會(huì)說:“解決這個(gè)問題簡(jiǎn)單啊,在這個(gè)方法的開始加一行代碼記錄開始時(shí)間 beginTime,在方法的結(jié)尾加一行代碼記錄結(jié)束時(shí)間 endTime,結(jié)束時(shí)間 endTime 減去開始時(shí)間 beginTime 就是方法的執(zhí)行時(shí)間,最后打印這個(gè)時(shí)間差就好了呀!”。
中級(jí)工程師 B 聽了初級(jí)工程師 A 的解決方案后說:“你這個(gè)方案是能解決問題,但是我們系統(tǒng)里代碼幾萬行,方法那么多,按你這個(gè)思路實(shí)現(xiàn),不得累死大家??!你的建議不妥,我們應(yīng)該用代理模式來解決這個(gè)問題,寫一個(gè)代理類來統(tǒng)一代理這些方法的執(zhí)行時(shí)間的代碼,那個(gè) Spring 框架的 AOP 就能很好地解決這個(gè)問題啊!”。
高級(jí)工程師 C 點(diǎn)了點(diǎn)頭說:“小 B 同學(xué)說的沒錯(cuò),解決這個(gè)問題的首選方案還是要用 AOP 切面編程,Spring 框架確實(shí)可以完美地解決這個(gè)問題,你們知道 Spring AOP 是如何實(shí)現(xiàn)的嗎?如果沒有 Spring 框架,我們自己寫代碼的話,如何實(shí)現(xiàn)這個(gè)功能呢? ”。
A 和 B 頓時(shí)來了興趣,不約而同地看向高級(jí)工程師 C,異口同聲地說:“C 哥,那你給我們講講唄......”。
二、自定義 AOP 框架介紹
我們今天實(shí)現(xiàn)的這個(gè) AOP 框架主要的功能就是完成統(tǒng)計(jì)方法執(zhí)行的時(shí)間,用到的例子是上篇文章中的訂單服務(wù)、用戶服務(wù)、支付服務(wù)。
自定義 AOP 框架里有以下幾個(gè)部分:
1、Advice 接口:用于定義方法的功能增強(qiáng)的接口。 2、Advice 接口的實(shí)現(xiàn)類 ExecutionTimeAdvice:方法執(zhí)行時(shí)間統(tǒng)計(jì)功能增強(qiáng)的實(shí)現(xiàn)類。 3、BeanFactory 類:創(chuàng)建對(duì)象的工廠類,用它創(chuàng)建對(duì)象類似于用 new 關(guān)鍵字實(shí)例化一個(gè)對(duì)象。 4、BeanFactoryProxy 類:創(chuàng)建對(duì)象的工廠類 BeanFactory 的代理類,用它來代理具體類的對(duì)象方法的執(zhí)行,從而可以增強(qiáng)由它代理的類的方法的功能,比如實(shí)現(xiàn)方法執(zhí)行時(shí)間的統(tǒng)計(jì)。 5、config.properties 配置文件:用來配置實(shí)體類 bean 以及功能增強(qiáng)類 advice。
代碼結(jié)構(gòu)如下圖:

三、一步一步地實(shí)現(xiàn)自定義 AOP 框架
1、定義 Advice 接口
Advice 主要是用來定義方法的功能增強(qiáng)的接口,在本例的 Advice 接口中定義兩個(gè)方法 beforeMethod() 和 afterMethod(),分別表示方法執(zhí)行前做的事情和方法執(zhí)行后做的事情。
public interface Advice {
public void beforeMethod(Method method);
public void afterMethod(Method method);
}
2、定義Advice 接口的實(shí)現(xiàn)類 ExecutionTimeAdvice
ExecutionTimeAdvice 類實(shí)現(xiàn)了 Advice 接口,主要是用來定義統(tǒng)計(jì)方法執(zhí)行時(shí)間的功能增強(qiáng)的具體實(shí)現(xiàn)。
public class ExecutionTimeAdvice implements Advice {
long beginTime = 0;
public void beforeMethod(Method method) {
beginTime = System.currentTimeMillis();
}
public void afterMethod(Method method) {
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " 方法運(yùn)行的時(shí)間為:" + (endTime - beginTime));
}
}
3、定義 BeanFactory 類
BeanFactory 類用于根據(jù)配置文件里配置的具體的 bean 的名稱,來創(chuàng)建具體的對(duì)象。
public class BeanFactory {
Properties prop = new Properties();
public BeanFactory(InputStream is) throws IOException {
if (is != null) {
prop.load(is); // 加載配置文件
}
}
/**
* 獲取配置文件配置的具體對(duì)象的實(shí)例
*/
public Object getBean(String beanName) throws Exception {
String className = prop.getProperty(beanName);
Class<?> clazz = Class.forName(className);
Object bean = clazz.newInstance();
if (bean instanceof BeanFactoryProxy) {
// 如果是代理類,則根據(jù)配置文件的內(nèi)容設(shè)置被代理類和對(duì)應(yīng)的增強(qiáng),否則返回配置的bean的對(duì)象實(shí)例
BeanFactoryProxy proxyFactoryBean = (BeanFactoryProxy) bean;
Object target = Class.forName(prop.getProperty(beanName + ".target")).newInstance();
Advice advice = (Advice) Class.forName(prop.getProperty(beanName + ".advice")).newInstance();
proxyFactoryBean.setTarget(target); // 設(shè)置被代理類
proxyFactoryBean.setAdvice(advice); // 設(shè)置功能增強(qiáng)
return proxyFactoryBean.getProxy();
}
return bean;
}
}
4、定義 BeanFactoryProxy 類
BeanFactoryProxy 類是 BeanFactory 類的代理類,用它來代理具體類的對(duì)象方法的執(zhí)行,從而可以增強(qiáng)由它代理的類的方法的功能,比如實(shí)現(xiàn)方法執(zhí)行時(shí)間的統(tǒng)計(jì)。
public class BeanFactoryProxy {
private Object target; // 被代理類
private Advice advice; // 功能增強(qiáng)
/**
* 通過動(dòng)態(tài)代理實(shí)現(xiàn)方法功能增強(qiáng)
*/
public Object getProxy() {
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.beforeMethod(method); // 在方法執(zhí)行前調(diào)用
Object retVal = method.invoke(target, args);
advice.afterMethod(method); // 在方法執(zhí)行后調(diào)用
return retVal;
}
});
return proxy;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
}
5、定義 config.properties 配置文件
配置文件 config.properties 用來配置實(shí)體類 bean,功能增強(qiáng)類 advice,以及被代理類 target。
## 此配置表示beanName為order的Bean不使用方法的增強(qiáng)功能
order = com.jpm.reflection.aop.service.OrderService
## 此配置表示beanName為user的Bean,通過代理類實(shí)現(xiàn)方法的增強(qiáng)功能
user = com.jpm.reflection.aop.BeanFactoryProxy
user.advice = com.jpm.reflection.aop.ExecutionTimeAdvice
user.target = com.jpm.reflection.aop.service.UserService
## 此配置表示beanName為pay的Bean,通過代理類實(shí)現(xiàn)方法的增強(qiáng)功能
pay = com.jpm.reflection.aop.BeanFactoryProxy
pay.advice = com.jpm.reflection.aop.ExecutionTimeAdvice
pay.target = com.jpm.reflection.aop.service.WeChatPayService
## 此配置表示beanName為wechatpay的Bean不使用方法的增強(qiáng)功能
wechatpay = com.jpm.reflection.aop.service.WeChatPayService
四、感受自定義的 AOP 框架的魅力
通過以下的測(cè)試代碼,快來感受一下自定義的 AOP 框架的魅力吧:
public class TestAop {
public static void main(String[] args) throws Exception {
InputStream is = TestAop.class.getClassLoader()
.getResourceAsStream("com//jpm//reflection//aop//config.properties");
BeanFactory beanFactory = new BeanFactory(is);
System.out.println("1、配置文件使用具體類OrderService,執(zhí)行OrderService的查詢方法和更新方法的結(jié)果:");
DaoService order = (DaoService) beanFactory.getBean("order");
order.query();
order.update();
System.out.println("2、配置文件使用代理類BeanFactoryProxy,執(zhí)行UserService的查詢方法和更新方法的結(jié)果:");
DaoService user = (DaoService) beanFactory.getBean("user");
user.query();
user.update();
System.out.println("3、配置文件配置使用代理類BeanFactoryProxy,執(zhí)行WeChatPayService的查詢方法和更新方法的結(jié)果:");
PayService pay = (PayService) beanFactory.getBean("pay");
pay.pay();
System.out.println("4、配置文件使用具體類WeChatPayService,執(zhí)行WeChatPayService的查詢方法和更新方法的結(jié)果:");
PayService weChatPay = (PayService) beanFactory.getBean("wechatpay");
weChatPay.pay();
}
}
運(yùn)行結(jié)果:
1、配置文件使用具體類OrderService,執(zhí)行OrderService的查詢方法和更新方法的結(jié)果:
OrderService.query()
OrderService.update()
2、配置文件使用代理類BeanFactoryProxy,執(zhí)行UserService的查詢方法和更新方法的結(jié)果:
UserService.query()
query 方法運(yùn)行的時(shí)間為:0
UserService.update()
update 方法運(yùn)行的時(shí)間為:0
3、配置文件配置使用代理類BeanFactoryProxy,執(zhí)行WeChatPayService的查詢方法和更新方法的結(jié)果:
WeChatPayService.pay()
pay 方法運(yùn)行的時(shí)間為:0
4、配置文件使用具體類WeChatPayService,執(zhí)行WeChatPayService的查詢方法和更新方法的結(jié)果:
WeChatPayService.pay()
通過以上的結(jié)果,我們可以看出,自己實(shí)現(xiàn)的 AOP 框架,完全可以達(dá)到根據(jù)配置文件來實(shí)現(xiàn)自由的切換哪些方法需要輸出執(zhí)行時(shí)間,哪些方法不需要輸出執(zhí)行時(shí)間,可以靈活的進(jìn)行配置。
通過以上的介紹,大家應(yīng)該感受到反射技術(shù)的強(qiáng)大的威力啦。其實(shí) AOP 的原理就是這么簡(jiǎn)單,關(guān)鍵的核心代碼也就是以上的代碼,希望大家可以熟練掌握。