AOP 的简单入门

AOP 的简单入门

自己也算是从业多年,对于AOP的概念应该算是听的烂的不能再烂了,这方面的书也看的不少,但是自己一直没有机会去实践下。
乘在这个稍微有点空闲的下午,就随手玩玩SPRING的AOP,也谈谈自己对于AOP的理解及其衍生的一些东西。

1.术语都是纸老虎
基本概念,也可以说是基本术语。任何一个软件概念提出时候,都少不了这个东西。CRM,AOP,SOA等等,伴随这些东西的都会有相应体系内的术语。
我个人的看法是一切术语的出现不是并不是向大众解释清楚这件事到底是怎么一回事,其主要是基于两个方面考虑:

1.让自己提出观点显得系统化,更具有说服力。
2.迷惑大众,或是迷糊那些刚进入这个领域的初学者。

两个看似矛盾的因素,其实归结到本质就是将一个简单的东西复杂化并迷糊那些辨别能力的一般的人,大家对于抽象到一定程度的东西但是心怀敬畏之心的,然后带着膜拜的心理去接受,生怕一不小心亵渎了内心的女神。扯开来讲现在社会,官员的道德沦丧,其中的一个诱因就是对于敬畏之心的缺失,当一个人无所畏时,才是最可怕的,因为这个时候已经没有任何约束能约束他的行为。

回归正题,既然提到术语,那么我们就将AOP中的那些术语列出来看看。
切面(Aspect)、连接点(Joinpoint)、通知(Advice)、切入点(Pointcut) 、目标对象(Target Object)、AOP代理(AOP Proxy)。
通知又分为几种:前置通知(Before advice)、后通知(After advice)、返回后通知(After return advice)、环绕通知(Around advice)、抛出异常后通知(After throwing advice) 等。

好了,现在我们来看看这些术语,谁能一眼就明白这些东西能告诉我们什么?谁能通畅的理清楚它们之间的关系。开始解释之前,我们看看维基百科上对AOP的定义是什么:
面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计、觀點導向編程)是计算机科学中的一个术语,指一种程序设计范型。该范型以一种称为侧面(aspect,又译作方面)的语言构造为基础,侧面是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。

多么简单的解释,就是对于那些在主程序业务之外的事情,我们该怎么设计,看清楚,是程序设计,而不是程序编码,很多地方都将AOP理解成面向切面的编码,那是错误,AOP约定的是一种设计范型,具体到java上的实现例如aspectJ,例如spring的AOP。再具体到实现技术就是JDK自带的动态代理,cglib的字节码修改等等。AOP != spring AOP ,AOP != cglib 。一个是设计范型,一个是实现。
(这里扯开点说,一些所谓的架构师喜欢谈概念,还有人提出架构师更关注抽象的东西,普通的码农更关注具象的东西。这些东西本身没错,在大量的实践之后,我们确实应该去在这大量的实践中归纳,总结出规律,这就是进行抽象。但是,但是,那些只跟你扯抽象而不落地的架构师,还是远离些,因为他们不是基于大量的具象后,进行抽象,他们不过是邯郸学步的抄袭那些真正的架构师的想法,并转变为自己的观点,TMD就是个大忽悠)

2.那些主程序之外的杂事
我们知道,一个程序是会被编译成计算机能识别的机器码,交给机器去执行,那么我们想知道机器在执行我们的代码时候,发生的一些事,例如:
1.一个输入是否得到我们想要的输出呢?
2.一个函数执行的时间开销是多少呢?
3.若是一个良好的程序遇到无法处理的异常我们该怎么办呢?
4.存在多个数据源,我们需要在多个数据源的更新都完成后,再提交该怎么处理呢?
5.我们想将一些我们不认可的请求拒绝,那又该怎么处理呢?

当以上的这些杂事出现时,我们该怎么做呢,一种很简单粗暴的方式就是硬编码的将这些杂事写入到我们的主要业务程序中,我想这种方式大家在日常的开发经常能看到,包括你去看一些所谓平台级别的产品也是采用这种方式,笔者现在所在单位的部分产品也是如此。不管你爽不爽,老子爽了就可以了。

3.上吧,骚年
用个不恰当的比喻来说:你是个开饭店的,来了很多顾客,当你女服务员在招待顾客时,你突然发现北大院长来了,想来OOXX下。你就定了一个流程,来北大院长,可OOXX,硬编码的方式就是:
1.你女服务员在招待
2.北大院长跟她OOXX
3.你女服务员在招待
4.屌丝来了,无视之
……
这个过程中你不管你女服务员是否来例假,你不关心你女服务员是否今天心情不好。嗯,院长爽了,若是你得到了某种回报,也还好,爽了;若是什么都得不到,那可就欲哭无泪。
如果你足够幸福的话,有另一个口味美女服务员,她会隐藏技能—洗脚。恰巧经管学院院长也来了,这个家伙还喜欢洗脚,那怎么办,那就上吧:
1.你女服务员在招待
2.北大院长跟她OOXX+洗脚
3.你女服务员在招待
4.屌丝来了,无视之
……
如果你生意足够好,恰好北大院长又很多,好吧,你的美女们一直处在XXOO状态中….. 很high,很happy。但是,我们回到最开始,你为什么要招女服务员?找她们来,是因为你需要她们去招待顾客,一个屌丝在等吃饭没关系,若是一群的屌丝在等吃饭,你就悲剧,没人招待屌丝们了,因为你的那些服务员都在跟院长们OOXX中,因为命令已经固化到流程中,你改不了,至少在你修改流程之前。通过我们软件术语来说,就是不能及时、灵活的应对自身内部(你的美女们身体、心情)和外部(屌丝数量)的变化。当然, 你若是铁道部这样的共和国长子,那是没关系的,让那群屌丝们等去吧,因为方圆960万平方公里就此一家,别无分号。

若你不是,哪天觉得自己有点生意有点扛不住或是那点生殖器破事被某个黑心,吃不到葡萄的院长小弟揭发了,扛不住随之而来的社会舆论压力,不能跟院长们OOXX了,只准对他们笑个,这个时候你得通知那些女服务员,说不准OOXX了,只能看了。若是你只有一家店,还好,自己喊一声,重新打印流程规章表,若是全国连锁的话…… 一桌的杯具摆在你茶几上。

4.正义化身的出现
好了,扯了这么多,终于要该AOP兄弟出场了,再不出估计戏都散场了。
针对以上的种种问题,我们该怎么处理这些我们店主要生意之外的杂事呢(OOXX),有什么更好的方式来随时应对种种变化。这个就是我们AOP兄弟想干的事情,从主业中剥离出这些杂事,进行单独处理的设计。主业务只关注于自己的领域,对于特殊领域的处理(OOXX),通过侧面来封装维护,这样使得散落在不同口味美女的特殊操作可以很好的管理起来。来段专业点的说明吧:
从主关注点中分离出横切关注点是面向侧面的程序设计的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过侧面来封装、维护,这样原本分散在在整个应用程序中的变动就可以很好的管理起来。

好了,AOP的就是个这么简单的东西,别去想那些繁杂的spring配置和概念术语。它只是一种设计范型。
绕了这么久,让我们来打倒那些纸老虎吧。
我开饭店,屌丝、院长来吃饭,美女们招待顾客 ,这个是我们的主业。 ======== 目标对象(Target Object) 就是店主我,我开了两个店,戏院和饭店
哦,北大院长来饭店吃饭了 ======== 切入点(Pointcut) 他们来我戏院看戏的话,不管,只管饭店的事
院长开始吃饭,喝酒了。 ======== 连接点(Joinpoint) ,就是我们的一些行为,院长如果来围观的话,无视之,哥是开饭店的。
院长想跟美女们OOXX了 ======== 通知(Advice)院长来了,也吃了饭了,那接下来干什么呢?通知就是决定干什么:OOXX或是洗脚
院长除了想OOXX之外,还想洗脚,那么怎么办呢? ======== 切面(Aspect) ,规定院长来了可以干什么,就是决定可以有多少个通知:OOXX||洗脚 或是 OOXX && 洗脚
—————————————————————————————————-
院长想吃饭后洗脚 ======== 后通知(After advice)
院长想吃饭前洗脚 ======== 前置通知(Before advice)
院长想根据吃饭后的心情决定是OOXX还是洗脚 ======== 返回后通知(After return advice)
院长吃饭吃出脑中风了 ======== 抛出异常后通知(After throwing advice)这个时候有个通知跳出来:打120,送医院!
院长想饭前洗脚,饭后OOXX ======== 环绕通知(Around advice)

作为老板的我,应该怎么更好的切入这些洗脚啊,OOXX服务呢 ======== AOP代理(AOP Proxy)怎么在干好招待顾客这件事上切入 洗脚||OOXX

若是上面的这些你还是看不明白的话,那么我们就具象到spring上,看看到底是件上面事吧。spring中Aspect叫Advisor。Joinpoint叫Weaving。很操蛋,也很让人无语的术语啊

切面:
package com.zhaming.aop.advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

/**
* 后置通知
* @author inter12
*
*/
public class AfterAdvice implements AfterReturningAdvice {

@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(“拦截了:” + method.getName() + “方法”);
System.out.println(“洗脚”);
}

}
package com.zhaming.aop.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

/**
* 前置通知
*
* @author inter12
*/
public class BeforeAdvice implements MethodBeforeAdvice {

@Override
public void before(Method method, Object[] args, Object target) throws Throwable {

System.out.println(“拦截了:” + method.getName() + “方法”);
System.out.println(“OOXX”);
}

}
package com.zhaming.aop.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
* 环绕通知
*
* @author inter12
*/
public class CompareAdvice implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = null;
String userName = invocation.getArguments()[0].toString();
if (null != userName && userName.equals(“yuanzhang”)) {
System.out.println(“院长通过—————“);
result = invocation.proceed();
} else {
System.out.println(“屌丝拒绝—————“);
}
return result;
}

}

目标对象:
package com.zhaming.aop.restaurant;

public interface RestaurantService {

public void zhaodaiguke(String userName);

public void weiguan(String userName);
}

package com.zhaming.aop.restaurant;

/**
* 目标对象
*
* @author inter12
*/
public class RestaurantServiceImpl implements RestaurantService {

@Override
public void zhaodaiguke(String userName) {
System.out.println(“——— 姑娘们在招待顾客:” + userName);
}

@Override
public void weiguan(String userName) {
System.out.println(userName + “:在围观”);

}

}

客户端:
package com.zhaming.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.zhaming.aop.restaurant.RestaurantService;

public class Main {

public static void main(String[] args) {
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(
“//home/inter12/workspace/Light/src/main/java/appcontext-aop.xml”);

RestaurantService bean = (RestaurantService) applicationContext.getBean(“restaurantService”);
bean.zhaodaiguke(“yuanzhang”);
bean.zhaodaiguke(“diaosi”);
}
}

配置文件:
<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns=”http://www.springframework.org/schema/beans”
xmlns:context=”http://www.springframework.org/schema/context”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:aop=”http://www.springframework.org/schema/aop”
xmlns:tx=”http://www.springframework.org/schema/tx”
xsi:schemaLocation=”
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd”>

<!– 通知 –>
<bean id=”beforeAdvice” class=”com.zhaming.aop.advice.BeforeAdvice”></bean>
<bean id=”afterAdvice” class=”com.zhaming.aop.advice.AfterAdvice”></bean>
<bean id=”compareAdvice” class=”com.zhaming.aop.advice.CompareAdvice”></bean>

<!– 目标对象 –>
<bean id=”restaurantServiceTarget” class=”com.zhaming.aop.restaurant.RestaurantServiceImpl”></bean>

<bean id=”restaurantService” class=”org.springframework.aop.framework.ProxyFactoryBean”>

<!– 拦截那些接口 : 切入点 只关心饭店的事–>
<property name=”proxyInterfaces”>
<value>com.zhaming.aop.restaurant.RestaurantService</value>
</property>

<!– 对这些方式做那些拦截:切面 –>
<property name=”interceptorNames”>
<list>
<!–
<value>beforeAdvice</value>

<value>afterAdvice</value>
–>
<value>compareAdvice</value>
</list>
</property>

<property name=”target”>
<ref bean=”restaurantServiceTarget” />
</property>
</bean>

</beans>

 

5.AOP能干什么?
现在回头看看最初问的五个问题,那些杂事,是不是可以对应到软件中的几个概念:日志记录,性能统计,安全控制,事务处理,异常处理,更衍生的提还有缓存,持久化,同步等

作者: inter12

在这苦短的人生中,追求点自己的简单快乐

发表评论

电子邮件地址不会被公开。 必填项已用*标注