AOP面向切面编程,实现原理就是利用动态代理的方式,创建一个代理对象,在代理对象中对被代理对象的方法进行增强。Spring提供的AOP方法是用过配置的方式实现。Spring框架会根据被代理对象是否实现了接口来决定采用是基于接口和基于类创建代理对象。代理对象会拦截被代理对象的方法。
AOP相关术语:
连接点(Joinpoint):是程序执行到某个特定位置,比如类初始化前,类初始化后,类的某个方法调用前/调用后。Spring只支持方法连接点,只能在方法调用前,方法调用后,方法抛出异常时,以及方法调用前后进行织入增强。
切入点(Pointcut):一个程序中会有多个连接点,比如一个类定义了两个方法,在spring中这两个方法都可以看做是连接点,但是,用户可能只需要对这两个方法中的一个方法感兴趣,那么AOP通过切点来定位所感兴趣的连接点,也就是说通过切点就是描述需要进行拦截的连接点。
通知/增强(Advice):就是拦截到连接点之后,需要执行的操作,执行操作包括两个方面:1就是执行的操作;2.这些操作执行的位置。执行操作的位置一般也被称为通知类型。具体通知类型包括:前置,后置,异常,环绕。这里的前置,后置是在增加操作发生在被拦截的方法执行的位置。
引介(Introduciton):是一种特殊的增强,他可以为类添加一下属性和方法。例如一个类本没有实现某个接口,通过引介,也动态的为该类添加对此接口的实现。
目标对象(Target):代理的目标对象
织入(Weaving):就是把增强的内容添加到具体的连接点上的过程,AOP将目标类、增强或者引介编织在一起的。一般AOP有三种织入方法:
1.编译器织入
2.类装载期织入
3.动态代理织入
Spring使用的是动态代理织入
代理(Proxy):为目标对象添加了增强的代理对象。
切面(Aspect):切入点和增强的结合。Spring把切点和增强配置在一起,称为切面,完成AOP的功能实现。
Spring配置AOP
下面通过简单的例子是演示如果使用Spring来实现AOP功能。
- 定义一个目标(Target)类UserServiceImpl,用作为被代理的对象。
public class UserServiceImpl implements UserService {
@Override
public void saveUser(String name, Integer age) {
System.out.println("save user");
}
}
- 定义一个用于实现增强的操作的类,使用该类的loggerInfo方法来增强UserServiceImpl类中的saveUser方法。
public class LogFactory {
public void loggerInfo(){
System.out.println("logger");
}
}
- 配置xml文件,在配置文件中需要引入AOP所需要命名空间,首先在xml配置好目标对象和提供增强功能的类。
然后开始配置AOP,Spring把使用aop:config标签来定义AOP切面,一个aop:config可以配置多个aop-aspect标签,每个aop-aspect就是一个AOP切面定义。
aop-aspect标签:id属性为定义的切面提供唯一属性,ref属性是定义通知(增强)bean的id。
aop-before标签:定义前置通知的标签,spring提供了4种位置标签,method属性是定义通知类所使用的通知方法,pointcut属性是定义切点表达式,切点表达式定义的是所要拦截的目标类,目标类的方法定义。切点表达式使用execution关键词来构建。
切点表达式规则:切点表达由访问修饰符,返回值,包名,类名,方法名和参数名定义,结构如下:
public void com.ws.springtest.service.impl.UserServiceImpl.saveUser(String,Integer))
切点表达式有以下几点:
1 访问控制符可以省略
2 返回值可以使用 * 来匹配所有的返回值类型
3 包名称可以使用 * 来表示任意包名称,但是有几级包,就需要写几个*.
4 ..表示当前包及其子包
5 方法名称也可使用 * 表示任意名称
6 方法参数可以是用 .. 表示任意数量和任意类型,如果需要精确匹配参数类型,那么如果参数是基本类型,直接写类型,如果是引用类型,需要写包名+类名的方式。方法参数也可以使用 * 来表示一个任意类型的参数。
在xml中配置切面的时候,可以先配置一个单独的切点表达式bean,然后在所有的配置的切面中去引入这个切点表达bean,从而可以达到省略配置的目的。
通知(增强的)四种通知类型
1.aop:before 前置通知,在切入点方法执行之前执行
2.aop:after 最终通知,无论切入点方法是否正常执行,该通知都会在切入点方法执行后执行
3.aop:after-returning 后置通知,在切入点方法成功执行之后调用
4.aop:after-throwing 异常通常,当切入点方法执行抛出异常后执行
5.aop:around 环绕通知,它是spring为用户提供了一个手动控制增强方法何时执行的通知方式。在使用环绕通知的时候,需要在定义通知方法的时候传入ProceedingJoinPoint类型。
1.在xml配置一个环绕通知的切面:
2.编写环绕通知的通知方法loggerInfo
public Object aroundLogger(ProceedingJoinPoint pjp) {
//获取切点方法的参数
Object[] args = pjp.getArgs();
Object result = null;
try {
System.out.println("前置");
//执行切点方法
result = pjp.proceed(args);
System.out.println("后置");
//返回切点方法执行后的结果
return result;
} catch (Throwable t) {
System.out.println("异常");
throw new RuntimeException(t);
} finally {
System.out.println("最终");
}
}
由上面的方法定义可以知道,环绕通知需要通过编码的方式,手动执行切点的方法,并且把切点方法执行后的结果进行返回,在上面定义的通知方法中,用户可以随意定义各种通知信息和通知位置。