转发地址: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. 第59题:螺旋矩阵 II

    一. 问题描述 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], ...

  2. django.db.utils.OperationalError: (1050, "Table 'article_category' already exists")

    (转自:https://blog.csdn.net/huanhuanq1209/article/details/77884014) 执行manage.py makemigrations 未提示错误信息 ...

  3. confluent_kafka消费时内存泄漏

    confluent_kafka测试的内存泄漏的条件 多线程消费 centos6 预测和centos6底层库存在关系. 换用centos7(我是换了7.3)就行了. (起初以为是代码问题,定位问题位置后 ...

  4. 普通java类获取spring容器bean的方法

    很多时候,我们在普通的java类中需要获取spring的bean来做操作,比如,在线程中,我们需要操作数据库,直接通过spring的bean中构建的service就可以完成.无需自己写链接..有时候有 ...

  5. 富文本编辑器直接从 word 中复制粘贴公式

    在之前在工作中遇到在富文本编辑器中粘贴图片不能展示的问题,于是各种网上扒拉,终于找到解决方案,在这里感谢一下知乎中众大神以及TheViper. 通过知乎提供的思路找到粘贴的原理,通过TheViper找 ...

  6. Java进阶知识24 Spring的事务管理(事务回滚)

    1.事务控制概述   1.1.编程式事务控制         自己手动控制事务,就叫做编程式事务控制.         Jdbc代码: connection.setAutoCommit(false); ...

  7. java枚举类型总结

    java中的枚举类型是jdk1.5新增的一个东西,其本质是一个java.lang.Enum类的子类,每个枚举项是一个静态常量对象,由编译器为每个枚举项分配ordinal和name,其中ordinal是 ...

  8. fiddler在小米8下抓取https数据包.

    问题,在小米8下一直报 证书链问题,爬了半天帖子发现可能是Android版本问题,有的说用Charles没问题. 没有测试,网上接着爬帖子... 稍稍说下导入证书的问题吧. 可以使用浏览器下载证书,也 ...

  9. 2019.6.28 校内测试 T1 Jelly的难题1

    这题面有点难理解,建议直接跳到题意解释那一部分(虽然我觉得解释的不大对,但按照解释来做确实能AC): 按照“题意解释”的思路来思考这个题,那么就十分的简单了: 1.首先要读入这个字符矩阵,可以用cin ...

  10. MGR---mysql组复制多主模式

    组复制有两种模式:单主模式和多主模式. 1.在单主模式下,组复制具有自动选主功能,每次只有一个 server成员接受更新.2.在多主模式下,所有的 server 成员都可以同时接受更新. MGR的限制 ...