一、AOP的概念
1、AOP解释为面向切面编程,是OOP面向对象编程的补充。
什么是面向切面编程呢?听起来很抽象,举个例子:假设我们要执行一个核心交易,但是我想打印核心交易前和交易后的日志,最简单的办法就是在核心交易的程序的前面和后面分别加上打印日志的程序。是的,很正确,但是我如果有成千上个这样的程序都要打印日志呢?如果你在每个核心的程序里面都加上打印日志的程序,核心程序和非核心的程序相互交错,不利于程序的维护,费时费力。
So,此时我们的面向切面编程就要横空出世了。面向切面编程可以让核心程序专心执行核心程序,不用分心做其它的事情。而把打印日志这种事情当做一个切面,由切面程序去执行。你看,核心程序执行核心的,切面程序执行切面程序,两者互不干扰,最后一起输出结果,岂不妙哉(实质切面程序是有代理关系的,暂且不管)。如果还想在核心程序上面加上其它的一些动作,都可以把这些划分为一个一个的切面。
2、AOP还有两个比较抽象的概念:通知、连接点和切点。
通知主要包括5种通知:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
连接点就是在什么位置把核心程序和切面程序连接起来,切点就是告诉去寻找连接点。有点抽象,下面通过代码来演示。
二、代码实例
本次实例核心人物是计算两个数字的加减乘除,但是我要在计算的前后分别打印日志,把打印日志放在切面程序里面。本实例采用spring4.0版本,在新建立一个工程时,需要导入一下几个jar包:
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
commons-logging-1.1.1.jar
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
1、建立一个接口
该ArithmeticCalculator 接口定义了两个数字的加减乘除法
package lzj.com.spring.aop;
//服务接口
public interface ArithmeticCalculator {
int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
2、创建该接口的实现类
package lzj.com.spring.aop;
import org.springframework.stereotype.Component;
//一定要该注解,否则无法装载arithmeticCalculator这个bean到容器中
@Component("arithmeticCalculator")
public class ArithmeticCalculatorIml implements ArithmeticCalculator {
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i - j;
return result;
}
@Override
public int mul(int i, int j) {
int result = i * j;
return result;
}
@Override
public int div(int i, int j) {
int result = i / j;
return result;
}
}
3、创建切面程序
该切面程序主要负责打印加减程序前后的日志
package lzj.com.spring.aop;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component //一定要加上此注解,否则找不到该代理Bean,即找不到切面程序。
public class LogProxy {
//表示是在ArithmeticCalculator接口的所有方法的前面执行beforMethod方法
@Before("execution(* lzj.com.spring.aop.ArithmeticCalculator.*(..))")
public void beforMethod(){
System.out.println("在调用前……");
}
表示是在ArithmeticCalculator接口的所有方法的后面执行beforMethod方法
@After(("execution(* lzj.com.spring.aop.ArithmeticCalculator.*(..))"))
public void afterMethod(){
System.out.println("在调用之后……");
}
}
4、创建配置类
核心代码和切面程序都创建完毕了,还要对他们配置,才能在容器中发现他们。
package lzj.com.spring.aop;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
//配置类,以便能够发现容器中的bean
@Configuration
@ComponentScan //配置该注解,在容器中能发现装载的bean
@EnableAspectJAutoProxy //配置该注解,激活切面程序
public class AnnotationConfig {
}
5、创建测试类
package lzj.com.spring.aop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AnnotationConfig.class);
ArithmeticCalculator arithmetic = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
int resultAdd = arithmetic.add(3, 2);
System.out.println("add的输出结果为:" + resultAdd);
}
}
输出结果为:
在调用前……
在调用之后……
add的输出结果为:5
在执行int resultAdd = arithmetic.add(3, 2);这句代码后,分别打印了“在调用前……”和“在调用之后……”。
总结:你看,是不是减少了核心程序和日志程序之间的耦合性,你走你的阳关道,我走我的独木桥,但两者却同时到达了目的地。本次试验只演示了通知中的Before和After,关于后面的三种,有兴趣的自己演示即可,同上面的方法一模一样。