转发地址:https://www.iteye.com/blog/elim-2396274

8 advisor标签

advisor标签是需要定义在aspect标签里面的,其作用与aspect类似,可以简单的把它理解为一个特殊的切面,用于把一个Advice和一个Pointcut组合起来。一个advisor标签对应的就是一个Advisor接口的实现类,默认是DefaultBeanFactoryPointcutAdvisor实现。其使用的基本语法类似如下这样。

<aop:config>
<aop:advisor advice-ref="" pointcut-ref=""/>
</aop:config>

上面的advice-ref属性用于指定一个org.aopalliance.aop.Advice实现,该接口没有任何内容,只是起到标记作用,用于标记某个类是Advice。pointcut-ref用于指定一个通过已经存在的Pointcut定义,当然也可以直接通过pointcut属性指定对应的Pointcut表达式。如果在一个config元素下既定义了aspect,又定义了advisor,那advisor必须定义在aspect之前。接下来看一下如何通过advisor标签应用常用的5种Advice,本文旨在介绍advisor是如何用的,以及如何使用5种Advice,至于每种Advice的功能、区别啥的已经在之前的文章中已经介绍过了,就不再赘述了,有兴趣的读者请参考以前的博文。

8.1 before Advice

对应于切入点方法执行前的拦截的Advice接口是BeforeAdvice接口,这个接口也是一个空接口没有实现,我们在自定义自己的BeforeAdvice实现时不直接实现BeforeAdvice接口,而是实现MethodBeforeAdvice接口。这是Spring为了将来可以支持对类的成员变量的访问进行拦截而预留的定义,也就是说将来BeforeAdvice还会有一个基于对类的成员变量访问的拦截的子接口定义。MethodBeforeAdvice接口中定义了一个before方法,在调入目标方法前就会调用before方法。如下就是一个MethodBeforeAdvice的实现示例。

public class LogBeforeAdvice implements MethodBeforeAdvice {

	@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("===============before advice start==============");
System.out.println("method: " + method);
System.out.println("args: " + args);
System.out.println("target: " + target);
System.out.println("===============before advice end================");
} }

定义了Advice之后,我们就可以通过advisor标签来把它和指定的PointCut绑定了。

<aop:config>
<aop:pointcut expression="bean(userService)" id="userServicePointcut"/>
<aop:advisor advice-ref="logBeforeAdvice" order="1" pointcut-ref="userServicePointcut"/>
</aop:config> <bean id="logBeforeAdvice" class="com.elim.spring.aop.advice.LogBeforeAdvice" />

8.2 around Advice

around Advice的实现需要实现org.aopalliance.intercept.MethodInterceptor接口,该接口定义了一个接收MethodInvacation类型的参数的invoke方法。通过MethodInvocation对象可以获取到目标方法、方法参数等信息,然后还可以通过调用其proceed方法来调用对应的目标方法,所以我们可以根据需要来判断是否需要调用目标方法。invoke方法的返回值将作为目标方法的调用者接收到的返回值,所以我们也可以在invoke方法中根据需要判断需要给目标方法调用者返回什么样的结果。以下是一个MethodInterceptor接口实现示例。

public class LogAroundAdvice implements MethodInterceptor {

	@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("=============================方法调用开始===" + invocation.getMethod());
try {
Object result = invocation.proceed();
System.out.println("=============================方法调用正常结束===" + invocation.getMethod());
return result;
} catch (Exception e) {
System.out.println("=============================方法调用异常===" + invocation.getMethod());
throw e;
}
} }

之前介绍基于XML配置和基于Aspectj注解的配置进行Around Advice配置时都可以在运行时根据条件来改变实际调用目标方法时传递的参数,那么如果我们直接实现MethodInterceptor接口是否又可以呢?答案是肯定的,MethodInterceptor的invoke方法参数MethodInvocation中已经封装了或者目标方法和参数的信息,如果需要改变传递的参数,我们可以不调用MethodInvocation的proceed方法,而是选择获取当前Method,然后直接调用Method的invoke方法传递自己所需要的参数。

public class LogAroundAdvice implements MethodInterceptor {

	@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("=============================方法调用开始===" + invocation.getMethod());
try {
Object result = invocation.getMethod().invoke(invocation.getThis(), 1);
System.out.println("=============================方法调用正常结束===" + invocation.getMethod());
return result;
} catch (Exception e) {
System.out.println("=============================方法调用异常===" + invocation.getMethod());
throw e;
}
} }

其配置与before advice的配置是类似的。

<aop:config>
<aop:pointcut expression="bean(userService)" id="userServicePointcut"/>
<aop:advisor advice-ref="logAroundAdvice" pointcut-ref="userServicePointcut"/>
</aop:config> <bean id="logAroundAdvice" class="com.elim.spring.aop.advice.LogAroundAdvice"/>

8.3 afterReturning Advice

AfterReturning Advice将在目标方法正常返回时触发,对应的是AfterReturningAdvice接口,其定义如下,第一个参数是目标方法的返回值。

public interface AfterReturningAdvice extends AfterAdvice {

	void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;

}

以下是笔者的一个测试实现。

public class LogAfterReturningAdvice implements AfterReturningAdvice {

	@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("==============调用成功返回,返回值是:" + returnValue);
System.out.println("Method: " + method);
if (returnValue instanceof User) {
//不能修改返回值,但可以修改返回值的某些属性,因为是对象引用
((User) returnValue).setName("modifyedName");
}
} }

8.4 afterThrowing Advice

afterThrowing Advice对应的Advice接口子类是ThrowsAdvice,该接口也是一个空接口,也是用于标记作用的,但是不同于BeforeAdvice定义了可供用户实现的包含方法定义的子接口MethodBeforeAdvice,ThrowingAdvice没有这样的子接口。这是因为用户可能需要同时对多种异常进行处理,如果把接口方法定义好了,那用户只能在方法体中判断当前捕获的异常类型了。没有方法定义时用户就可以在实现类中定义很多的异常处理方法了,但是这些方法也不是随便定义的,它们必须满足以下形式。其中的方法名必须为afterThrowing,方法参数只有最后一个subclassOfThrowable是必须的。

afterThrowing([Method, args, target], subclassOfThrowable)

以下是一个实现示例,在示例中我们一共实现了三个处理Exception的方法,前两个处理方法用于处理特定的异常类型,而且只接收一个异常类型参数,最后一个处理方法接收所有的参数,处理除前两者以外的其它异常。

public class LogThrowsAdvice implements ThrowsAdvice {

	/**
* 处理IllegalArgumentException
* @param e
*/
public void afterThrowing(IllegalArgumentException e) {
System.out.println("=====================方法调用异常,抛出了IllegalArgumentException");
} /**
* 处理NumberFormatException
* @param e
*/
public void afterThrowing(NumberFormatException e) {
System.out.println("=====================方法调用异常,抛出了NumberFormatException");
} /**
* 处理其它所有的异常
* @param method
* @param args
* @param target
* @param e
*/
public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
System.out.println("=====================方法调用异常了," + e);
System.out.println("Method: " + method);
System.out.println("Args: " + args);
System.out.println("Target: " + target);
} }

8.5 after Advice

对于After Advice类型的Advice没有特定的接口供我们实现,如果需要自己实现一个Advice可以达到after Advice那样的效果,即无论切入点方法是否抛出异常都执行某些逻辑时,可以使用MethodInterceptor代替,在方法实现中使用try…finally形式即可。

上面配置的完整配置如下。

<aop:config>
<aop:pointcut expression="bean(userService)" id="userServicePointcut"/>
<aop:advisor advice-ref="logBeforeAdvice" order="1" pointcut-ref="userServicePointcut"/>
<aop:advisor advice-ref="logThrowsAdvice" order="2" pointcut-ref="userServicePointcut" />
<aop:advisor advice-ref="logAfterReturningAdvice" order="3" pointcut-ref="userServicePointcut"/>
<aop:advisor advice-ref="logAroundAdvice" pointcut-ref="userServicePointcut"/>
</aop:config> <bean id="logBeforeAdvice" class="com.elim.spring.aop.advice.LogBeforeAdvice" />
<bean id="logThrowsAdvice" class="com.elim.spring.aop.advice.LogThrowsAdvice" />
<bean id="logAfterReturningAdvice" class="com.elim.spring.aop.advice.LogAfterReturningAdvice" />
<bean id="logAroundAdvice" class="com.elim.spring.aop.advice.LogAroundAdvice"/>

(注:本文是基于Spring4.1.0,Elim写于2017年1月23日星期一)

Spring Aop(八)——advisor标签的更多相关文章

  1. 死磕Spring之AOP篇 - Spring AOP常见面试题

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  2. 死磕Spring之AOP篇 - Spring AOP注解驱动与XML配置

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  3. Spring AOP Example – Pointcut , Advisor

    In last Spring AOP advice examples, the entire methods of a class are intercepted automatically. But ...

  4. Spring AOP高级——源码实现(2)Spring AOP中通知器(Advisor)与切面(Aspect)

    本文例子完整源码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/Spring%20AOP%E9%A ...

  5. 关于spring aop Advisor排序问题

    关于spring aop Advisor排序问题 当我们使用多个Advisor的时候有时候需要排序,这时候可以用注解org.springframework.core.annotation.Order或 ...

  6. Spring学习(十六)----- Spring AOP实例(Pointcut(切点),Advisor)

    在上一个Spring AOP通知的例子,一个类的整个方法被自动拦截.但在大多数情况下,可能只需要一种方式来拦截一个或两个方法,这就是为什么引入'切入点'的原因.它允许你通过它的方法名来拦截方法.另外, ...

  7. Spring笔记07(Spring AOP的通知advice和顾问advisor)

    1.Spring AOP的通知advice 01.接口代码: package cn.pb.dao; public interface UserDao { //主业务 String add(); //主 ...

  8. Spring Boot 2.X(八):Spring AOP 实现简单的日志切面

    AOP 1.什么是 AOP ? AOP 的全称为 Aspect Oriented Programming,译为面向切面编程,是通过预编译方式和运行期动态代理实现核心业务逻辑之外的横切行为的统一维护的一 ...

  9. spring aop中aspect和advisor的区别

    之前看到spring AOP配置aspect(切面)有两种方式,一种是利用注解的方式配置,一种是利用XML的方式配置. 我们的配置是这样的<aop:aspect>,还有另外一种<ao ...

随机推荐

  1. Hadoop的简单了解与安装

    hadoop 一, Hadoop  分布式 简介Hadoop  是分布式的系统架构,是  Apache  基金会顶级金牌项目 分布式是什么?学会用大数据的思想来看待和解决问题 思 想很重要 1-1 . ...

  2. Cairo

    Cairo 英[ˈkaɪrəʊ] 美[ˈkaɪroʊ] n. 开罗(埃及首都); [例句]From Cairo came expressions of regret at the attac

  3. 0001SpringBoot整合Mybatis

    SpringBoot整合Mybatis主要分为以下几个步骤: 1.添加Mybatis的起步依赖(pom.xml) 2.添加数据库驱动坐标(pom.xml) 3.添加数据库连接信息(applicatio ...

  4. TODO 疑问:java中的工厂类,在实际项目中如何运用

    正在读core of java,工厂类一直没搞懂.感觉和静态方法相类似,但是不知道是怎么运用在实际中. 20190711: 读了设计模式,里面给出的解读是这样的 Define an interface ...

  5. c#动态调用WEBSERVICE接口

    C#动态webservice调用接口 1 using System; 2 using System.Collections; 3 using System.IO; 4 using System.Net ...

  6. C# 操作文件夹、文件

    Form namespace FileProperties { public partial class Form1 : Form { private string currentFolderPath ...

  7. python执行playbook

    from collections import namedtuple from ansible.parsing.dataloader import DataLoader from ansible.va ...

  8. HTMl5的存储方式sessionStorage和localStorage区别及联系

    localStorage 和 sessionStorage 属性允许在浏览器中存储 key/value 对的数据. localStorage 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手 ...

  9. 删除harbor项目下的所有镜像

    user= pswd= url= proid= REPOS=$(curl -s -X GET --header 'Accept: application/json' "${url}/api/ ...

  10. Bzoj 3036: 绿豆蛙的归宿(期望)

    3036: 绿豆蛙的归宿 Time Limit: 2 Sec Memory Limit: 128 MB Description 随着新版百度空间的下线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归 ...