背景
最近在看 spring实战,看到第四章也就是”面向切面的Spring”。
看到其中说”在一个地方定义通用功能,但是我们可以通过声明的方法定义这个功能在何处应用,无需修改受影响的类,这些类称之为切面“。上面这段话,我们其实只要抓住几个关键词 :
- 通用功能:什么样的功能叫通用功能呢,比如日志、事务之类。
- 何处使用:在什么地方会使用上日志、事务。
- 无需修改:这算是aop 的一个特点吧,举个例子 ,如果你要在每个方法上加入日志,最简单最傻的方法是一个个加log,那么会带来一个问题,修改起来特别麻烦。这个时候就需要用到aop了。
AOP术语
- 通知:切面的工作被称为通知,他应该应用于某个方法被调用之前?之后?之前和之后?还是抛出异常?
- Spring 有5种类型的通知。
- Before 方法调用之前
- After 执行之后,无论方法是否成功。
- After-returning 方法调用成功之后
- After-throwing 方法抛出异常
- Around 通知包裹着被通知的方法。中间可以使用point.proceed() 继续执行方法。
- Spring 有5种类型的通知。
- 连接点:应用执行过程中插入切面的一个点,这个点可以是调用方法的时候、抛出异常。
- 切点: 定义在“何处”开始进行通知。
- 切面:切面是切点的集合
- 引入:引入允许我们向先有的类添加新的方法。
- 织入:织入是将切面应用到目标对象来创建的代理对象过程。(不是特别懂!).
最常用的估计也就是切点、连接点、通知、和切面。
Spring中该如何使用AOP
spring 中使用AOP有2种方法:一种是通过xml来配置。一种是通过注解的方式。我首先介绍xml方式。首先不管是xml配置还是注解配置,在配置文件中都得有AOP的命空间,具体如下所示:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
1. xml配置
配置如下:
<bean id="systemLog" class="com.storm.aop.SystemLog"/>
<!--spring处理AOP代理有2种代理实现方式,一种是默认的JDK代理实现,一种是cglib代理实现,这里false 就是默认的,true是使用cglib代理 --!>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--所有配置都在这里 --!>
<aop:config>
<aop:aspect id="systemLog" ref="systemLog">
<aop:pointcut id="log" expression="execution(* com.storm.controller.UserController.*(..))"/>
<aop:before method="doBefore" pointcut-ref="log"/>
<aop:after method="doAfter" pointcut-ref="log"/>
<aop:around method="doAround" pointcut-ref="log"/>
</aop:aspect>
</aop:config>
下面我就对这段代码进行解释
<bean id="systemLog" class="com.storm.aop.SystemLog"/>`
<aop:aspect id="systemLog" ref="systemLog">
<aop:aspect>
声明一个切面类,这里是指向的一个 SystemLog bean类。
aop:pointcut id="log" expression="execution(* com.storm.controller.UserController.*(..))"/>
这里是定义了一个切点,是指在 com.storm.controller.UserController 这个类里面 所有的方法 ((..)是表示不管无参有参的方法)都会通过aop去代理。
execution(* ) 这个 * 是通配符表示返回任意类型。
比如 我要controller这个包下的所有方法都要经过AOP:
aop:pointcut id="log" expression="execution(* com.storm.controller.*.*(..))"/>
<aop:before method="doBefore" pointcut-ref="log"/>
<aop:after method="doAfter" pointcut-ref="log"/>
<aop:around method="doAround" pointcut-ref="log"/>
都是通知 这就对应了我前面所说的几种通知方式(忘了可以回头看看)。method对应具体实现的方法。
xml配置完成之后,我们就要编写通知类(也就是上面的 SystemLog),具体代码如下所示:
public class SystemLog {
public void doBefore() {
System.out.println("begin method");
}
public void doAfter() {
System.out.println("end method");
}
public Object doAround(ProceedingJoinPoint joinpoint) {
Object result = null;
System.out.println("around begin");
try {
result = joinpoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("around end");
return result;
}
}
我们可以看到 这里的doBefore 和doAfter doAround 就是 xml配置中method 所指向的方法。
到这里 xml 配置已经完成,运行项目,我们可以看到如下的结果:(库日天cj就是程序运行过程中输出的信息)
xml配置方法总结:
xml配置只要记着两个步骤就可以:
- 配置文件中配置AOP相关信息。参见配置文件
- 通知类编写。(通知类中如何获取参数信息 可以通过JoinPoint 这个类来获取)
2 注解配置
注解这种方式相比较xml,形式上更为简洁,所以我推荐使用注解使用,不然一大推东西都在xml 中,找都找不到。
配置文件如下:(其实只要在spring-mvc 配置文件中加入一行就好了)
<aop:aspectj-autoproxy proxy-target-class="true"/>
对应的通知类
package com.storm.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* Created by cj on 2018/3/11.
*/
@Aspect
@Component
public class SystemLog {
/**
* Created by cj on 2018/3/11.
*/
public void SystemLog(){
System.out.println("loading systemLog");
}
@Pointcut("execution(* com.storm.controller.*.*(..))")
public void action() {
}
@Before("action()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("----------------------");
System.out.println("begin method");
}
@After("action()")
public void doAfter(JoinPoint joinPoint) {
System.out.println("end method");
}
@Around("action()")
public Object doAround(ProceedingJoinPoint joinpoint) {
Object result = null;
System.out.println("around begin");
try {
result = joinpoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("around end");
return result;
}
}
执行结果如下:
总结:
对于spring中aop 的使用 需要注意的几点:
- 在编写切点时 先尽量将范围缩小到具体的某一个方法,从具体的某一个方法开始。这样才能避免出错。
- 在编写通知类是,我建议多写一个构造函数,早构造函数里打印一些信息,这些信息会帮助你排除一些错误。
- 如果要对controller层进行AOP管理,有关AOP配置的信息写到spirng-mvc配置文件中(我也不知道为什么)。
- 其实记住以上几个步骤。只需要记住2个问题:1 我对什么类或者方法感兴趣(也就是PointCut)。2 我该在这个方法的什么时期进行我自定义的操作(也就是Advice).
结束语:
新人的真正意义上的第一篇博客,希望看过的人都能评论下,也希望顺手给个关注,以后每周都会更新,虽不是什么很牛逼的博文,但我保证都是用心写,谢谢大家!!!!!!!!!!