總有那么一些代碼,在測試環(huán)境下,是不能輕易被調(diào)用的。
比如:
1)發(fā)送系統(tǒng)任務(wù)郵件到客戶郵箱,可能一不小心,就把測試郵件發(fā)送給了真實(shí)客戶的郵箱里;
2)調(diào)用跨公司的系統(tǒng)接口,而對方系統(tǒng)沒有測試環(huán)境,每調(diào)用一次接口,就會(huì)在對方系統(tǒng)產(chǎn)生垃圾數(shù)據(jù);
3)調(diào)用的代碼可能需要大量的cpu運(yùn)算,占用大量的內(nèi)存空間,消耗大量的資源;
等等。。。
為了解決這樣的需求,
1)在代碼中,到處充斥著這樣的代碼:
1 if(在測試環(huán)境下) {
2 打印日志;
3 } else {
4 調(diào)用真實(shí)的業(yè)務(wù)邏輯;
5 }
于是乎,需要到處維護(hù)這樣的代碼,一旦增加此類需求,就需要編寫同樣的代碼
2)部分懶惰的程序員,連這樣的if...else...也不愿意寫,僅僅在注釋中說明下在測試環(huán)境中調(diào)用方法的危害性。
于是,在測試階段,一旦和測試部門溝通不足,導(dǎo)致代碼還是經(jīng)常被調(diào)用到,如果是在作壓力,性能測試,那么危害性可想而已。
曾發(fā)生過,壓力測試某個(gè)功能,結(jié)果把大量的測試郵件,發(fā)送給了客戶,影響很差。
那么,如何解決這樣的需求場景呢?
沒錯(cuò),采用proxy模式,可以搞定。考慮到現(xiàn)在很多企業(yè)都使用Spring作為IOC容器,本文就簡單介紹,如何采用spring aop來解決問題。
以發(fā)送郵件的需求作為虛擬場景。
現(xiàn)在有個(gè)Service,專門負(fù)責(zé)郵件的發(fā)送。
1. MyService.java
1 public class MyService {
2 public void sendMailSafely() {
3 System.out.println("send mail successfully.");
4 }
5 }
如果這個(gè)sendMailSafely被客戶端調(diào)用,那么毫無疑問,郵件不管任何環(huán)境下,都會(huì)被成功發(fā)送。
需要有個(gè)方法攔截器,對這個(gè)方法做攔截。
2. MyInterceptor.java
1 public class MyInterceptor implements MethodInterceptor {
2
3 private boolean isProduction = false;
4
5 @Override
6 public Object invoke(MethodInvocation invocation) throws Throwable {
7 if (!isProduction) {
8 System.out.println("is production environment.do nothing
");
9 return null;
10 }
11 return invocation.proceed();
12 }
13
14 public void setProduction(boolean isProduction) {
15 this.isProduction = isProduction;
16 }
17
18 }
這個(gè)攔截器,根據(jù)配置文件的參數(shù)isProduction判斷是否在正式環(huán)境,如果是在測試環(huán)境,對方法做攔截,僅僅打印log,不真實(shí)調(diào)用業(yè)務(wù)邏輯。
如何讓sendMailSafely()方法被此攔截器做攔截,所以通過spring配置文件,配置一個(gè)advisor,通知對以Safely結(jié)尾的方法做攔截
3. application.xml
1 <bean id="safetyAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" scope="singleton">
2 <property name="advice">
3 <ref local="myInterceptor" />
4 </property>
5 <property name="patterns">
6 <list>
7 <value>.*Safely</value>
8 </list>
9 </property>
10 </bean>
附上application.xml的全部內(nèi)容
1 <beans default-autowire="byName">
2
3 <!-- service實(shí)例 -->
4 <bean id="myService" class="cn.zeroall.javalab.aop.MyService" scope="singleton" />
5
6 <!-- 方法攔截器 -->
7 <bean id="myInterceptor" class="cn.zeroall.javalab.aop.MyInterceptor" scope="singleton">
8 <property name="production" value="false" />
9 </bean>
10
11 <!-- 通知者 -->
12 <bean id="safetyAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" scope="singleton">
13 <property name="advice">
14 <ref local="myInterceptor" />
15 </property>
16 <property name="patterns">
17 <list>
18 <value>.*Safely</value>
19 </list>
20 </property>
21 </bean>
22
23 <!-- myService代理類 -->
24 <bean id="safetyService" class="org.springframework.aop.framework.ProxyFactoryBean" scope="singleton">
25 <property name="interceptorNames">
26 <list>
27 <value>safetyAdvisor</value>
28 </list>
29 </property>
30 <property name="targetName" value="myService" />
31 </bean>
32
33 </beans>
寫一個(gè)Client類來做演示。
4. Client.java
1 public class Client {
2
3 private ApplicationContext ctx = new ClassPathXmlApplicationContext(
4 "cn/zeroall/javalab/aop/application.xml");;
5
6 public static void main(String[] args) {
7 Client c = new Client();
8 c.sendMail();
9 c.sendMailSafety();
10 }
11
12 public void sendMail() {
13 MyService myService = (MyService) ctx.getBean("myService");
14 myService.sendMailSafely();
15 }
16
17 public void sendMailSafety() {
18 MyService myService = (MyService) ctx.getBean("safetyService");
19 myService.sendMailSafely();
20 }
21
22 }
大家可以看看最終輸出的結(jié)果內(nèi)容。
一直來,我都不會(huì)濫用AOP,尤其反感使用AOP來寫業(yè)務(wù)邏輯內(nèi)容,但是對于這類非業(yè)務(wù)邏輯需求,采用spring aop技術(shù)那是剛剛好啊。
最后,附上全部代碼文件(使用maven構(gòu)建)。
演示代碼