spring-AOP之通知和顾问

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

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

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

前置通知:在目标方法执行之前执行,不改变方法的执行流程及执行结果,前置通知的实现类要实现“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来获取的,我们真正想要的是目标对象,而不是代理对象。

时间: 2024-10-21 06:37:47

spring-AOP之通知和顾问的相关文章

spring AOP和通知

1.  spring的通知 1.1.  AOP的概念 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象.事务管理是J2EE应用中一个关于横切关注点的很好的例子.在Spring AOP中,切面可以使用基于模式或者基于注解的方式来实现. 连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候.在Spring AOP中,一个连接点总是表示一个方法的执行. 通知(Advice):在切面的某个特定的连接点上执行的动作.其中包括了"aroun

Spring AOP 之 通知、连接点、切点、切面。

1:知识背景 软件系统可以看成是由一组关注点组成的,其中,直接的业务关注点,是直切关注点.而为直切关注点提供服务的,就是横切关注点. 2:面向切面的基本原理 什么是面向切面编程 横切关注点:影响应用多处的功能(安全.事务.日志) 切面: 横切关注点被模块化为特殊的类,这些类称为切面 优点: 每个关注点现在都集中于一处,而不是分散到多处代码中 服务模块更简洁,服务模块只需关注核心代码. AOP 术语 通知: 定义:切面也需要完成工作.在 AOP 术语中,切面的工作被称为通知. 工作内容:通知定义了

spring aop环绕通知记录应用的日志

使用的框架是spring mvc+spring 最近想利用spring aop的环绕通知来处理web的日志问题,总的来讲,如果在controller层做切入,则难监控实际运行情况,在service层做切入,则只能监控到service层的情况,通过捕捉service抛出的异常来记录日志,对于目前本人应用而言,已经足够了,先将记录如下: 代码: @Component @Aspect public class ExceptionLog { /** * 61 * 环绕通知需要携带ProceedingJo

Spring AOP前置通知和后置通知

Spring AOP AspectJ:Java社区里最完整最流行的AOP框架 在Spring2.0以上的版本中,可以使用基于AspectJ注解或基于XML配置的AOP 在Spring中启用AspectJ注解支持 要在Spring应用中使用AspectJ注解,必须在classpath下包含AspectJ类库:aopalliance.jar.aspectj.weaver.jar和spring-aspects.jar 将aop Schema添加到<beans>根元素中. 要在Spring IOC容器

Spring AOP 前置通知和后置通知

加入JAR包: 在配置文件中加入AOP 的命名空间 基于注解的注解的方式,配置文件如下: beans-aop-helloworld.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema

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

本文例子完整源码地址:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/Spring%20AOP%E9%AB%98%E7%BA%A7%E2%80%94%E2%80%94%E6%BA%90%E7%A0%81%E5%AE%9E%E7%8E%B0%EF%BC%882%EF%BC%89Spring%20AOP%E4%B8%AD%E9%80%9A%E7%9F%A5%E5%99%A8%EF%BC%88Advisor

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

今天又看了下韩顺平的SpringAOP的讲解,讲解的很透彻.仿照视频自己使用下前置通知. 一.引出问题 有个接口TestServiceInter,有两个实现方法TestService和Test2Service.他们都有sayHello():我们的需求是在调用这两个方法之前,要先完成写日志的功能: 二.菜鸟的想法 我在各个实现类的sayHello()方法里面写上写日志的功能就是了. 这样实现存在的问题:代码冗余.当所有实现类都要加上日志功能的时候,需要写很多重复代码 三.利用AOP前置通知实现此功

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

一.引出问题 有个接口TestServiceInter,有两个实现方法TestService和Test2Service.他们都有sayHello():我们的需求是在调用这两个方法之前,要先完成写日志的功能:二.菜鸟的想法 我在各个实现类的sayHello()方法里面写上写日志的功能就是了. 这样实现存在的问题:代码冗余.当所有实现类都要加上日志功能的时候,需要写很多重复代码三.利用AOP前置通知实现此功能3.1 第一步:我们需要定义一个接口 package com.jdc.aop; /** @a

Spring——AOP(通知)

1. 搭建项目环境 2. 新建lib文件夹,添加spring依赖jar包: spring-beans.jar.spring-context.jar.spring-core.jar.spring-expression.jar.[spring-aop.jar] 添加依赖包: commons-logging.jar.[aopalliance.jar] 3. 在项目src目录下新建applicationContext.xml <beans xmlns="http://www.springframe

Spring aop 前置通知

一.首先在项目中加入aop所需要的jar aopalliance-1.0.jaraspectjweaver-1.6.11.jarcommons-logging-1.1.1.jarspring-aop-3.0.5.RELEASE.jarspring-aspects-3.0.5.RELEASE.jarspring-beans-3.0.5.RELEASE.jarspring-context-3.0.5.RELEASE.jarspring-context-support-3.0.5.RELEASE.ja