spring_aop
1、AOP简介
1.1、什么是AOP
概念:AOP(面向切面编程)是一种思想,是基于OOP(面向对象编程)提出的一个新的思想。
特点:在不修改原有代码的情况下,增加跟主要业务没有关系的公共代码到之前写好的方法中的指定位置,比如日志管理、安全监控、事务管理等。
1.2、AOP代理
AOP的底层用的是代理,代理是一种设计模式。
1.2.3、AOP相关概念点
切面(Aspect):指关注点模块化,这个关注点可能会横切多个对象。就是将与主要业务代码没有关系的一块模块化,用它去横切多个对象或者方法,方法之前,方法之后,方法异常以及方法返回等等被切分开来。
连接点(Join point):切面和需要被增强的方法所产生的交叉点就是连接点。比如在一个方法之前打日志,这个打日志的模块就和需要打日志的方法(被增强的方法)的交叉处产生的点就是连接点。
通知(Advice):在切面的某个特定的连接点上执行的动作,例如before、after等这些方法。就好比说在执行这个需要增强的方法之前,通知它要先执行before方法。
切点(Pointcut):Spring Aop可以自由控制需要增强某个方法的某个点,被控制的那些点就是切点。
1.2.4、如何使用AOP
1、设置扫描包,设置aop开启。
2、设置切面类。
3、设置切面的方法需要切入哪些方法。
<!-- 设置扫描包 -->
<context:component-scan base-package="cool.ale"></context:component-scan>
<!-- 开启aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
// 设置切面类
// 设置切面的方法需要切入哪些方法。@Before("execution(* cool.ale.service..*.*(..))")
@Component
@org.aspectj.lang.annotation.Aspect
public class Aspect {
@Before("execution(* cool.ale.service..*.*(..))")
public static void before(){
System.out.println("方法执行前。");
}
}
1.2.5、切点的方法
@Before:在目标方法之前运行,前置通知
@After:在目标方法之后运行,后置通知
@AfterThrowing:在目标方法抛出异常时运行,异常通知
@AfterReturning:在目标方法正常返回后运行,返回通知
@Around:环绕,环绕通知
2、AOP详解
2.1、AOP代理分类
当我们使用aop代理 时,会有两种代理方式,一种是基于jdk的代理,一种是基于cglib的代理,jdk依赖于接口,而cglib不依赖于接口。
现在是不使用aop代理的测试:
// Test
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring.xml");
IUserService bean = ioc.getBean(IUserService.class);
System.out.println(bean.getClass());
现在测试的类实现了接口
@Service
public class UserService implements IUserService {
}
// Test
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring.xml");
IUserService bean = ioc.getBean(IUserService.class);
System.out.println(bean.getClass());
现在测试的类没有实现接口:
@Service
public class UserService {
}
// Test
public void test01(){
ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring.xml");
UserService bean = ioc.getBean(UserService.class);
System.out.println(bean.getClass());
}
2.2、切点表达式
2.2.1、切点表达式的分类
切点表达式有很多个,最常用的就是execution。
修饰符:可以省略,如果不写,则是任意的修饰符。
返回值:如果是java的,就可以写省略的,比如说void就写void,如果是自己自定义的类,就必须写类的完整性限定名。
类限定:..代表子孙类,可以模糊匹配,*Service。
方法名:*代表任意,可以模糊匹配,*add。
参数:*代表任意。
within:用于匹配连接点所在的Java类或者包。
this:用于匹配到实现了哪个接口。
target:用于向通知方法中传入目标对象的引用。
args:用于匹配使用了哪个参数。
@annotation :匹配连接点被它参数指定的Annotation注解的方法。也就是说,所有被指定注解标注的方法都将匹配。
bean:通过受管Bean的名字来限定连接点所在的Bean。该关键词是Spring2.5新增的。
2.2.2、合并切点表达式
可以使用&&、||、!来操作多个切点表达式。
2.2.3、方法执行顺序
正常:前置–方法–后置–后置返回
异常:前置–方法–后置–异常
2.3、aop取参数
@Before("execution(* cool.ale.service..*.*(..))")
public static void before(JoinPoint joinPoint){
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取方法参数
Object[] object = joinPoint.getArgs();
System.out.println(methodName + "方法执开始,参数是:" + Arrays.asList(object));
}
@AfterThrowing(value = "execution(* cool.ale.service..*.*(..))",
throwing = "ex")
public static void afterThrowing(Exception ex){
System.out.println("方法抛出异常时。" + ex);
}
@AfterReturning(value = "execution(* cool.ale.service..*.*(..))",
returning = "returnValue")
public static void afterReturning(Object returnValue){
System.out.println(returnValue);
}
2.4、声明公用切点类
/**
* 可声明一个切点类
*/
@Pointcut("execution(* cool.ale.service..*.*(..))")
public void pointCut(){
}
@Before("pointCut()")
public static void before(JoinPoint joinPoint){
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取方法参数
Object[] object = joinPoint.getArgs();
System.out.println(methodName + "方法执开始,参数是:" + Arrays.asList(object));
}
2.5、环绕通知
@Around("pointCut()")
public void around(ProceedingJoinPoint joinPoint){
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 获取方法参数
Object[] object = joinPoint.getArgs();
try {
System.out.println("环绕:前置通知。");
joinPoint.proceed();
System.out.println("环绕:后置通知。");
}catch (Throwable throwable) {
System.out.println("环绕:异常通知。");
throwable.printStackTrace();
}finally {
System.out.println("环绕:返回通知。");
}
}
2.6、基于xml方式实现aop
<aop:config>
<aop:aspect ref="logAspectByXml"> <!-- 引入一个切面类,这个类必须在ioc中注册 -->
<aop:pointcut id="cutAllService" expression="execution(* cool.ale.service..*.*(..))"/>
<aop:before method="before" pointcut-ref="cutAllService" ></aop:before>
<aop:after method="after" pointcut-ref="cutAllService"></aop:after>
<aop:after-throwing method="afterThrowing" pointcut-ref="cutAllService" throwing="ex"></aop:after-throwing>
<aop:after-returning method="afterReturning" pointcut-ref="cutAllService" returning="returnValue"></aop:after-returning>
</aop:aspect>
</aop:config>