通知和顾问都是切面的实现形式,其中通知可以完成对目标对象方法简单的织入功能。

而顾问包装了通知,可以让我们对通知实现更加精细化的管理,让我们可以指定具体的切入点。

通知分为前置通知,环绕通知及后置通知。

前置通知:在目标方法执行之前执行,不改变方法的执行流程及执行结果,前置通知的实现类要实现“MethodBeforeAdvice”这个接口。

环绕通知:也叫方法拦截器,可以改变方法的执行流程及执行结果,环绕通知的实现类要实现“MethodInterceptor”这个接口。

后置通知:在目标方法执行之后执行,不改变方法的执行流程及执行结果,后置通知的实现类要实现“AfterReturningAdvice”这个接口。

为了说明以上三者的区别,我们还是用实验来说明,这次的实例是实现一个计算器,有加法和除法。

首先是计算器的接口。

public interface ICalculatorService {

    int add(int a,int b);

    int division(int a ,int b); 

}

实现类:

public class CalculatorServiceImpl implements ICalculatorService {

    @Override
public int add(int a, int b) {
return a+b;
} @Override
public int division(int a, int b) {
return a/b;
} }

前置通知的实现类:

public class TestMethodBeforeAdive implements MethodBeforeAdvice {

    @Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行前置通知--->"+"正在执行的方法名为"+method.getName());
} }

环绕通知的实现类:

public class TestMethodInterceptor implements MethodInterceptor {

    @Override
public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("执行环绕通知--->"+"正在执行的方法名为"+invocation.getMethod().getName()); Object[] arguments = invocation.getArguments();
int a = (int)arguments[0];
int b = (int)arguments[1];
if(b == 0){
System.err.println("除数不能为0");
return -1;
}
if(a == 0){
return 0;
} return invocation.proceed();
} }

后置通知的实现类:

public class TesAfterRunningAdive implements AfterReturningAdvice {

    @Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行后置通知--->"+"正在执行的方法名为"+method.getName());
System.err.println("执行结果为:"+returnValue.toString());
} }

xml的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 目标对象 -->
<bean id="calculatorServiceTarget" class="com.opensource.service.impl.CalculatorServiceImpl"/>
<!-- 通知 -->
<bean id="methodBeforeAdive" class="com.opensource.service.impl.TestMethodBeforeAdive"/>
<bean id="afterRunningAdive" class="com.opensource.service.impl.TesAfterRunningAdive"/>
<bean id="methodInterceptor" class="com.opensource.service.impl.TestMethodInterceptor"/> <!-- 代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="calculatorServiceTarget"/>
<property name="interfaces" value="com.opensource.service.ICalculatorService"/>
<property name="interceptorNames">
<list>
<value>methodBeforeAdive</value>
<value>afterRunningAdive</value>
<value>methodInterceptor</value>
</list>
</property>
</bean>
</beans>

测试类:

public class MyTest {
public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("spring-bean.xml");
ICalculatorService bean = (ICalculatorService)ac.getBean("proxyFactoryBean");
int division = bean.division(10, 0);
System.out.println("两数相除商为:"+division);
//bean.add(0, 2);
} }

实验结果:

使用通知这种切面对目标对象的方法进行织入的缺点是是显而易见的,因为他会对目标对象中的所有方法进行织入。如上例中,我们定义的环绕通知这个切面只是用来对目标对象中的"division"这一方法进行织入而对“add”方法,不加织入。但使用通知进行织入的话,会把目标对象中所有的方法都进行了织入。也就是说目标对象中所有的方法都成为了切入点。要实现对通知更加精细化的管理,就要引入顾问,可以让我们有选择性的对目标对象的方法进行织入。

如上例中,我们希望环绕通知只对目标对象的“division”方法进行织入,那么使用顾问就可以这么做(这里我们使用NameMatchMethodPointcutAdvisor这种顾问):

修改配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:task="http://www.springframework.org/schema/task" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 目标对象 -->
<bean id="comparatorServiceTarget" class="com.opensource.service.impl.ComparatorServiceImpl"/>
<bean id="calculatorServiceTarget" class="com.opensource.service.impl.CalculatorServiceImpl"/>
<!-- 通知 -->
<bean id="methodBeforeAdive" class="com.opensource.service.impl.TestMethodBeforeAdive"/>
<bean id="afterRunningAdive" class="com.opensource.service.impl.TesAfterRunningAdive"/>
<bean id="methodInterceptor" class="com.opensource.service.impl.TestMethodInterceptor"/>
<!-- 顾问 -->
<bean id="advisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="methodInterceptor"/>
<property name="mappedNames" value="division"/>
</bean>
<!-- 代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="calculatorServiceTarget"/>
<property name="interfaces" value="com.opensource.service.ICalculatorService"/>
<property name="interceptorNames">
<list>
<value>methodBeforeAdive</value>
<value>afterRunningAdive</value>
<value>advisor</value>
</list>
</property>
</bean>
</beans>

测试类:

public class MyTest {
public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("spring-bean.xml");
ICalculatorService bean = (ICalculatorService)ac.getBean("proxyFactoryBean");
/*int division = bean.division(10, 0);
System.out.println("两数相除商为:"+division);*/
int add = bean.add(0, 2);
System.out.println("两数想加和为:"+add);
} }

实验结果:

另外在顾问中对多个切入点进行指定的时候可以使用逗号隔开,还有可以使用模糊匹配的方式例如“query*”这种方式。

分析以上的使用方式我们会发现两个缺点

1):由于我们的代理对象是由ProxyFactoryBean工具类生成的,这就决定了一个代理对象只能代理一个目标对象,当有多个目标对象时,就需要有多个代理对象,这样就很麻烦。

2):我们在测试类中获取bean时,用的是代理对象的id获取的,不是通过我们定义的目标对象的id来获取的,我们真正想要的是目标对象,而不是代理对象。

下篇博客将会给出这个问题的解决方法。

  最后说一点,我们作为程序员,研究问题还是要仔细深入一点的。当你对原理了解的有够透彻,开发起来也就得心应手了,很多开发中的问题和疑惑也就迎刃而解了,而且在面对其他问题的时候也可做到触类旁通。当然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,你有大把的时间或者空余的时间,你大可去刨根问底,深入的去研究一项技术,为觉得这对一名程序员的成长是很重要的事情。

spring7——AOP之通知和顾问的更多相关文章

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

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

  2. spring-AOP之通知和顾问

    通知和顾问都是切面的实现形式,其中通知可以完成对目标对象方法简单的织入功能. 而顾问包装了通知,可以让我们对通知实现更加精细化的管理,让我们可以指定具体的切入点. 通知分为前置通知,环绕通知及后置通知 ...

  3. Spring AOP 四大通知

    Spring AOP 四大通知 Spring 3.X 以前 1.前置通知,实现  MethodBeforeAdvice 接口,重写 public  void  before(Method  metho ...

  4. spring aop环绕通知

    [Spring实战]—— 9 AOP环绕通知   假如有这么一个场景,需要统计某个方法执行的时间,如何做呢? 典型的会想到在方法执行前记录时间,方法执行后再次记录,得出运行的时间. 如果采用Sprin ...

  5. Spring AOP前置通知实例说明AOP相关概念

    今天又看了下韩顺平的SpringAOP的讲解,讲解的很透彻.仿照视频自己使用下前置通知. 一.引出问题 有个接口TestServiceInter,有两个实现方法TestService和Test2Ser ...

  6. AOP 环绕通知 集成了前置 后置 返回通知等功能

    AOP 环绕通知 集成了前置 后置 返回通知等功能

  7. aop 例外通知就是记录业务方法出现错误 并保存到日志里面的功能

    aop 例外通知就是记录业务方法出现错误 并保存到日志里面的功能

  8. Spring AOP前置通知实例讲解与AOP详细解析

    一.引出问题 有个接口TestServiceInter,有两个实现方法TestService和Test2Service.他们都有sayHello():我们的需求是在调用这两个方法之前,要先完成写日志的 ...

  9. Spring AOP(通知、连接点、切点、切面)

    一.AOP术语 通知(Advice)  切面的工作被称为通知.通知定义了切面是什么以及何时使用.除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题.5种通知类型: 前置通知(Before): ...

随机推荐

  1. 【Unity3D与23种设计模式】组合模式(Composite)

    前段时间在忙一个新项目 博客好久没有更新了 GoF中定义: "将对象以树状结构组合,用以表现部分-全体的层次关系.组合模式让客户端在操作各个对象或组合时是一致的." 是一致的意思就 ...

  2. Javascript的那些硬骨头:作用域、回调、闭包、异步……

    终于到了神话破灭的时刻-- 这注定是一篇"自取其辱"的博客,飞哥,你们眼中的大神,Duang,这次脸朝下摔地上了. 故事得从这个求助开始:e.returnValue 报错:未定义, ...

  3. Vue2.0+Node.js+MongoDB全栈打造商城系统 免费下载

    <ignore_js_op> 课程目录||--第01章 课程介绍|    01-01 课程-导学.mp4|    01-02 前端框架回顾.mp4|    01-03 vue概况以及核心思 ...

  4. PHP面试和PHP开发者都应掌握的10个问题

    PHP面试和PHP开发者都应掌握的10个问题 问题 :1 MySQL里的存储引擎有什么不同,哪一个是默认的? 答案: 1 我们可以一下存储引擎: 1. MyISAM(MySQL的默认引擎. 每个MyI ...

  5. Mecanim之IK动画

    序言:IK动画全名是Inverse Kinematics 意思是逆向动力学,就是子骨骼节点带动父骨骼节点运动. 比如体操运动员,只靠手来带动身体各个部位的移动.手就是子骨骼,身体就是它的父骨骼,这时运 ...

  6. 设置placeholder字体的颜色

    ::-webkit-input-placeholder { /* WebKit browsers */ color:#999; } :-moz-placeholder { /* Mozilla Fir ...

  7. 【Python】 linux中python命令的命令行参数

    Python命令行参数 原文地址:http://blog.163.com/weak_time/blog/static/25852809120169333247925/ Python的命令行参数,提供了 ...

  8. nginx域名跳转技巧

    1.地址重写:访问server_name的时候跳转到http://www.cnblogs.com/qinyujie/ 修改nginx配置文件.加入到server{...}字段或者location字段里 ...

  9. TCP为什么不是两次握手而是三次?

    为什么不采用两次握手?如果是两次握手的情景:客户端在发送一个连接建立请求之后进入等待状态,等到服务端确认之后就进入established状态.服务端在发送一个确认连接建立请求报文之后(不管客户端是否有 ...

  10. pycharm 的安装及selenium环境的搭建

    6.呵呵哒,前面写了一篇pycharm的安装,,结果自己都看不懂了,copy了别人的,,,自己现在再写一遍,这篇文章主要写pycharm 的安装及selenium 环境的搭建,selenium的搭建不 ...