總有那么一些代碼,在測試環境下,是不能輕易被調用的。
比如:
1)發送系統任務郵件到客戶郵箱,可能一不小心,就把測試郵件發送給了真實客戶的郵箱里;
2)調用跨公司的系統接口,而對方系統沒有測試環境,每調用一次接口,就會在對方系統產生垃圾數據;
3)調用的代碼可能需要大量的cpu運算,占用大量的內存空間,消耗大量的資源;
等等。。。
為了解決這樣的需求,
1)在代碼中,到處充斥著這樣的代碼:
1 if(在測試環境下) {
2 打印日志;
3 } else {
4 調用真實的業務邏輯;
5 }
于是乎,需要到處維護這樣的代碼,一旦增加此類需求,就需要編寫同樣的代碼
2)部分懶惰的程序員,連這樣的if...else...也不愿意寫,僅僅在注釋中說明下在測試環境中調用方法的危害性。
于是,在測試階段,一旦和測試部門溝通不足,導致代碼還是經常被調用到,如果是在作壓力,性能測試,那么危害性可想而已。
曾發生過,壓力測試某個功能,結果把大量的測試郵件,發送給了客戶,影響很差。
那么,如何解決這樣的需求場景呢?
沒錯,采用proxy模式,可以搞定。考慮到現在很多企業都使用Spring作為IOC容器,本文就簡單介紹,如何采用spring aop來解決問題。
以發送郵件的需求作為虛擬場景。
現在有個Service,專門負責郵件的發送。
1. MyService.java
1 public class MyService {
2 public void sendMailSafely() {
3 System.out.println("send mail successfully.");
4 }
5 }
如果這個sendMailSafely被客戶端調用,那么毫無疑問,郵件不管任何環境下,都會被成功發送。
需要有個方法攔截器,對這個方法做攔截。
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 }
這個攔截器,根據配置文件的參數isProduction判斷是否在正式環境,如果是在測試環境,對方法做攔截,僅僅打印log,不真實調用業務邏輯。
如何讓sendMailSafely()方法被此攔截器做攔截,所以通過spring配置文件,配置一個advisor,通知對以Safely結尾的方法做攔截
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的全部內容
1 <beans default-autowire="byName">
2
3 <!-- service實例 -->
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>
寫一個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 }
大家可以看看最終輸出的結果內容。
一直來,我都不會濫用AOP,尤其反感使用AOP來寫業務邏輯內容,但是對于這類非業務邏輯需求,采用spring aop技術那是剛剛好啊。
最后,附上全部代碼文件(使用maven構建)。
演示代碼