总有那么一些代码,在测试环境下,是不能轻易被调用的。
比如:
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构建)。
演示代码