其实对于Spring AOP虽然我学过,但是我觉得我一直不太理解他是干嘛的?AOP就是切面编程。主要是为了将与业务逻辑无关的代码分离开来,比如:日志,权限,事务处理,异常处理,性能统计。例如在每次执行某个方法之前我们需要记录日志,或者判断有无权限去执行接下来的代码,我们经常会多个方法上做这些操作,但是这些操作是和我们的业务逻辑无关的,又需要多次使用,那么我们就可以将这些方法抽取出来作为一个切面类。
(图是我盗的,嘿嘿)
上图中横线代表了程序执行的逻辑,竖线就像是一个个切面类,即需要切入的代码,切线的位置就是我们要定义的切点。如上图就像是在5个流程中固定位置插入了3个逻辑代码,如果你不需要的时候,你完全可以撤出红线代码,而要修改的话,也只需修改这三个类(竖线)即可,因为主代码(横线)和添加代码(竖线)是没有耦合的,相当于插拔式的。而且spring aop支持声明式的配置,使得aop更加方便。
比如在service层中我们可能会用到日志的记录和权限的判断,那我们就可以将service作为切点,在我们的切面类中添加上通知,定义通知的类型,用来决定什么时候执行这个方法。
常见的通知类型有
AspectJ 支持 5 种类型的通知注解:
注意:Spring拦截的颗粒度比较粗,只能是方法级的拦截,不能更加细化的拦截
l @Before: 前置通知, 在方法执行之前执行
l @After: 后置通知, 在方法执行之后执行
l @AfterRunning: 返回通知, 在方法返回结果之后执行
l @AfterThrowing: 异常通知, 在方法抛出异常之后
l @Around: 环绕通知, 围绕着方法执行
环绕通知是这几种最强大的一种,编程方便,但是由于偏向于重量级,一般建议使用合适的增强• 环绕通知是所有通知类型中功能最为强大的, 能够全面地控制连接点. 甚至可以控制是否执行连接点.
• 对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 是否执行连接点.
• 在环绕通知中需要明确调用 ProceedingJoinPoint 的 proceed() 方法来执行被代理的方法. 如果忘记这样做就会导致通知被执行了, 但目标方法没有被执行.
• 注意: 环绕通知的方法需要返回目标方法执行之后的结果, 即调用 joinPoint.proceed(); 的返回值, 否则会出现空指针异常
具体的使用实例这里就不给出了,等到要用的时候再整理吧。
我的有道云笔记:http://note.youdao.com/noteshare?id=1784102a7fc570d61ad4c1677f15618a&sub=F9C5175A598048259D91B9AFC2F364CC
如果我们想要在方法中获取方法相关的参数,可以在方法中增加一个JoinPoint类型的参数。getArgs获取方法参数,getSignature获得方法签名。
编写 AspectJ 切入点表达式
最典型的切入点表达式时根据方法的签名来匹配各种方法:
l execution * com.wjh.spring.ArithmeticCalculator.*(..):
匹配com.wjh.spring包中ArithmeticCalculator 中声明的所有方法,
第一个 * 代表任意修饰符及任意返回值.
第二个 * 代表任意方法. .. 匹配任意数量的参数. 若目标类与接口与该切面在同一个包中, 可以省略包名.
l execution public * ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 接口的所有公有方法.
l execution public double ArithmeticCalculator.*(..): 匹配 ArithmeticCalculator 中返回 double 类型数值的方法
l execution public double ArithmeticCalculator.*(double, ..): 匹配第一个参数为 double 类型的方法, .. 匹配任意数量任意类型的参数
l execution public double ArithmeticCalculator.*(double, double): 匹配参数类型为 double, double 类型的方法.
在 AspectJ 中, 切入点表达式可以通过操作符 &&, ||, ! 结合起来.
前面主要介绍了需要了解的知识,下面讲一下实际应用
1,首先需要在pom.xml中添加jar包
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.1version>
dependency>
2,在父容器applicatonContext.xml(spring-mybatis.xml)中添加配置
<aop:aspectj-autoproxy />
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.wjh.service..*.*(..))" />
aop:config>
3,定义你的切面类,如日志记录类,权限判断类
package com.wjh.log;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.wjh.service.impl.UserServiceImpl;
@Aspect
@Component
public class MyLog {
private static final Logger logger=LoggerFactory.getLogger(UserServiceImpl.class);
@After("execution(* com.wjh.service.impl.UserServiceImpl.login(..))")
public void loginLog(){
logger.info("user login");
}
}
通过AOP我们将那些与业务无关的代码分离开来,使得程序的条理更加清晰了,也提高了代码的复用。想用的时候只要在处理方法上就加上通知进行申明就可以了。