本人初学aop,刚学时可能会被里面专业的词语(通知,连接点,切入点,切面。。。)搞得晕头转向,其实个人觉得最好的方法还是通过例子学习。
个人理解,AOP简单的说就是将与业务或者主流程无关的代码剥离出来,降低耦合,这样核心代码更加突出,不会穿插很多与业务无关的代码,比如日志模块等。
拿《Spring in Action》里的经典例子来做解释,比如一个演奏家在演奏音乐,在演奏前观众要就坐,关掉手机,演奏高潮时观众要鼓掌。如果我们正常编程序,可能在编写演奏家演奏音乐时(perform())得穿插进观众的程序,即演奏家演奏音乐时,还得忙着指导观众来鼓掌,如下面程序所示:
public void perform(){
audience.takeSeats();
audience.turnOffCellPhones();
System.out.println("Perform");
audience.applaud();
}
先看下面演奏者的程序:
public interface Performer {
public void perform();
}
@Component
public class Instrumentalist implements Performer{
@Override
public void perform() {
System.out.println("Perform.");
}
}
上面定义了演奏者的动作(perform()),这个也是演奏者的主要负责的事情,没有穿插进观众的动作。那么观众应该如何加入进来呢?这时候就用到AOP了,
再贴出一段程序:
@Aspect
@Component
public class Audience {
@Pointcut("execution(* *.perform(..))")
public void performance(){};
@Before("performance()")
public void takeSeats(){
System.out.println("Take Seats.");
}
@Before("performance()")
public void turnOffCellPhones(){
System.out.println("Turn off CellPhones.");
}
@AfterReturning("performance()")
public void applaud(){
System.out.println("Applaud");
}
public void demandRefund(){
System.out.println("demandRefund");
}
}
这里的观众其实就是一个切面(@Aspect),切面包括了通知(Advice)和切入点(Pointcut),他们组合起来完整的表达了功能,何时何地完成其功能。
切入点定义了“何地”,就是通知要织入的一个或多个连接点,一般是明确的类和方法或者用正则表达式匹配,这里的
@PointCut就相当于切入点,execution(* *.perform(..))中的execution表示执行方法时,第一个*代表任意返回类型,第二个*代表任意类,perform代表perform方法,(..)代表任意参数类型。合起来代表在任意返回类型,任意参数类型的peform()方法中进行切入,即将观众这个切面切入到perform() 方法中。
通知定义了切面的“什么”和“何时使用”,在某个方法被调用之前?之后?之前和之后?这里的@before和@AfterReturning就相当于通知,表示在perform()执行之前和返回之后执行。
这样从代码上,观众与演奏家完全解耦,却可以实现同样的功能。
代码结构如下:
Spring的配置文件如下:
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"
default-autowire="byName">
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="net.csdn.aop">
</context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
测试程序如下:
public class Main {
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
ctx.getBean(Performer.class).perform();
}
}
这就是一个简单的测试用例,当然,也可以不使用注解,把其中的注解全部去掉,全部在XML中配置,如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd"
default-autowire="byName">
<bean id="instrumentalist" class="net.csdn.aop.Instrumentalist"></bean>
<bean id="audience" class="net.csdn.aop.Audience"></bean>
<aop:config>
<aop:aspect ref="audience">
<aop:before method="takeSeats" pointcut="execution(* *.perform(..))"/>
<aop:before method="turnOffCellPhones" pointcut="execution(* *.perform(..))"/>
<aop:after-returning method="applaud" pointcut="execution(* *.perform(..))"/>
</aop:aspect>
</aop:config>
</beans>
个人比较喜欢用注解,两种方式可以各选其一~