Spring AOP有多种实现方式,基于Spring xml配置的,基于注解配置的,基于切点函数配置等等,还有其他的实现方式,这里主要记录提到的三种方式
一.基于xml配置的AOP
- 首先定义一个AOP的通知类,包含方法执行前的方法,方法执行后的方法,还有环绕等方法;如下:
/**
* @author eleven
* @date 2018/11/4
* @description
*/
@Slf4j
public class GlobalAdvices {
public void before(){
log.warn(">>>>>前置通知:before");
}
public void after(){
log.info(">>>>>后置通知:after");
}
}
2.将通知类交给spring容器管理,并且配置AOP,如下:
<bean id="globalAdvices" class="cn.eleven.common.intercept.GlobalAdvices"/>
<!--<!–全局的service层的方法调用都会被记录日志–>-->
<aop:config>
<aop:aspect id="class" ref="globalAdvices">//定义AOP切面
<aop:pointcut id="log" expression="execution(* cn.llf.framework.services..*.*(..))"/>//定义AOP切入点
<aop:before method="before" pointcut-ref="log"/>//前置通知方法
<aop:after method="after" pointcut-ref="log"/>//后置通知方法
</aop:aspect>
</aop:config>
3.这样基于xml配置的AOP就算完成,所有调用service方法都会被切到;
二.基于注解配置的AOP
1.定义一个通知类,如下:
/**
* @author eleven
* @date 2018/11/4
* @description AOP
*/
@Slf4j
@Aspect
public class RequestAdvice {
/**
* 定义一个通用的切点,解决切点复用问题
*/
@Pointcut("execution(* cn.llf.framework.services..*.*(..))")
public void pointcut(){}
/**
* 直接在通知方法定义切点
* @param joinPoint
*/
@Before("pointcut()")
public void before(JoinPoint joinPoint){
log.warn(">>>>>前置通知");
}
/**
* 引用定义好的切点
* @param joinPoint
*/
@After("pointcut()")
public void after(JoinPoint joinPoint){
log.warn(">>>>>后置通知");
}
}
2.如 注释所说,可以定义先定义一个切入点,接着之后的各种通知就可以复用一个切入点,避免切入点代码重复;
3.对应的前制通知使用@before声明为前制通知,用@after声明为后置通知;
4.类头需要使用@asperct申明该类为切面,这样spring容器才能识别;
5.接着将该切面类收进spring容器管理,需要在xml配置,如下:
<!--基于注解的aop切面-->
<bean class="cn.llf.framework.aop.RequestAdvice"/>
6.最后还需要在spring中配置启动@asperct注解的支持,aspect注解就是包括上面的@asperct,@before等注解,配置如下:
<!-- 启动对@AspectJ注解的支持 -->
<!--<!–通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller –>-->
<aop:aspectj-autoproxy proxy-target-class="true" />
7.上述就是基于注解的AOP的实现方式,配置完成,所有的service层的方法都已经纳入到AOP的切面范围了;
8.JoinPoint解释:进入切面之后,可以获取到拦截到的方法的一些参数信息;注意区别Joinpoint类;仅差一个P大小写问题,却关系到AOP能否启动
三.基于切点函数配置的AOP
- 基于切点函数的AOP,其切点函数包括9种的函数,
@AspectJ使用AspectJ专门的切点表达式描述切面,Spring所支持的AspectJ表达式可分为四类:
- 方法切点函数:通过描述目标类方法信息定义连接点。
- 方法参数切点函数:通过描述目标类方法入参信息定义连接点。
- 目标类切点函数:通过描述目标类类型信息定义连接点。
- 代理类切点函数:通过描述代理类信息定义连接点。
常见的AspectJ表达式函数:
- execution():满足匹配模式字符串的所有目标类方法的连接点
- @annotation():任何标注了指定注解的目标方法链接点
- args():目标类方法运行时参数的类型指定连接点
- @args():目标类方法参数中是否有指定特定注解的连接点
- within():匹配指定的包的所有连接点
- target():匹配指定目标类的所有方法
- @within():匹配目标对象拥有指定注解的类的所有方法
- @target():匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
- this():匹配当前AOP代理对象类型的所有执行方法
2.上述9种切点函数种,execution函数在前面的两种实现方式已经提及,这里另外主要记录@annotation方式的切点函数
3.首先需要定义在方法上要使用的注解,如下:
/**
* @author eleven
* @date 2018/11/4
* @description AOP
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInvocationStatistic {
/**
* 方法作用
* @return
*/
String methodName() default "";
/**
* 方法类型
*/
int type() default 1;
}
4.其次在需要AOP的方法上使用该注解:
@Override
@MethodInvocationStatistic(methodName = "获取用户分页数据")
public List<UserInfoPO> list(UserInfoPO query) {
List<UserInfoPO> list = dao.list(query);
return list;
}
5.定义AOP通知类,其中的通知方法不在使用execution函数,而是使用这里提到的annotation函数,如下:
/**
* @author eleven
* @date 2018/11/4
* @description AOP
*/
@Slf4j
@Aspect
public class RequestAdvice {
/**
* 切面范围为有使用 {@link MethodInvocationStatistic}的方法
* @param joinPoint
*/
@Before("@annotation(cn.llf.framework.annotation.MethodInvocationStatistic)")
public void actionRecord(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
log.info("标有注解的【{}】的方法被AOP切面切到,方法签名:【{}】", MethodInvocationStatistic.class.getName(),signature);
}
}
6.同样将该通知类交给spring容器管理,如下:
<!--基于注解的aop切面-->
<bean class="cn.llf.framework.aop.RequestAdvice"/>
7.最后还是启动aspect注解的支持
<!-- 启动对@AspectJ注解的支持 -->
<!--<!–通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller –>-->
<aop:aspectj-autoproxy proxy-target-class="true" />