spring学习笔记(8)AOP增强(advice)配置与应用

增强类型

增强(advice)主要包括如下五种类型
1. 前置增强(BeforeAdvice):在目标方法执行前实施增强
2. 后置增强(AfterReturningAdvice):在目标方法执行后实施增强
3. 环绕增强(MrthodInterceptor):在目标方法执行前后实施增强
4. 异常抛出增强(ThrowsAdvice):在目标方法抛出异常后实施增强
5. 引介增强(IntroductionIntercrptor):在目标类中添加一些新的方法和属性

前置增强和后置增强配置

下面通过实例代码来分析前置增强,假如现在要作日志记录,记录了什么人调用什么方法。前置增强的配置可归纳为如下步骤:

1. 配置代理接口:

package test.aop;

public interface ITarget {
    String speak(String name);
}

2. 定义被代理对象

package test.aop;
//被代理对象
public class Target implements ITarget{

    private static final String name = "zenghao";
    @Override
    public String speak(Integer age){
        System.out.println("hello I'm " + age + " years old");
        return "I'm return value";
    }
    public static String getName() {
        return name;
    }
}

3. 配置增强

package test.aop;

import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvice implements MethodBeforeAdvice {
   /**
    * @param method:目标类的方法
    * args: 目标类的方法入参
    * obj:目标类实例
    *
    */
   @Override
   public void before(Method method, Object[] args, Object target)
           throws Throwable {
       if(target instanceof Target){
           System.out.println("前置日志记录: "  +  ((Target)target).getName() + "调用了" + method.getName() + "方法,传入参数为:" + args[0] );
       }
   }

}
/*------------------分割线---------------------*/
package test.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

public class AfterAdvice implements  AfterReturningAdvice {
   /**
    * @param
    * returnValue 返回值
    * method:目标类的方法
    * args: 目标类的方法入参
    * obj:目标类实例
    *
    */
   @Override
   public void afterReturning(Object returnValue, Method method,
           Object[] args, Object target) throws Throwable {
       if(target instanceof Target){
           System.out.println("后置日志记录: "  +  ((Target)target).getName() + "调用了" + method.getName() + "方法,返回值为:" + returnValue );
       }
   }

}

4. 配置代理对象ProxyFactoryBean

PeoxyFactoryBean是FactoryBean的实现类,我们知道FactoryBean负责初始化Bean,而ProxyFactoryBean则负责为其他Bean创建代理实例,通过在xml中配置后注入使用

<!--  配置被代理对象 -->
<bean id="mytarget" class="test.aop.Target" />
<!-- 配置前置增强  -->
<bean id="myBeforeAdvice" class="test.aop.BeforeAdvice" />
<!-- 配置后置增强 -->
<bean id="myAfterReturnAdvice" class="test.aop.AfterAdvice" />
<!-- 配置代理对象 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean" >
    <!-- 配置代理接口集 -->
    <property name="proxyInterfaces" value="test.aop.ITarget" />
    <!-- 代理目标对象需要实现的接口,可以通过<list>标签设置多个 -->
    <!-- 把通知织入代理对象 -->
    <property name="interceptorNames" >
        <list>
                <idref bean="myBeforeAdvice"/>
                <idref bean="myAfterReturnAdvice"/>
            </list>
    </property><!-- 配置实现了Advice增强接口的Bean,以bean名字进行指定 -->
    <property name="targetName" value="mytarget"></property><!-- 代理的目标对象 -->
</bean>

5. 测试

package test.aop;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAOP {

    private ApplicationContext ac;
    @Before
    public void setup(){
        ac = new ClassPathXmlApplicationContext("classpath:test/aop/aop.xml");
    }

    @Test
    public void test(){
        ITarget iTarget = (ITarget) ac.getBean("proxyFactoryBean");
        iTarget.speak(21);
    }
}

测试打印:

前置日志记录: zenghao调用了speak方法,传入参数为:21
hello I’m 21 years old
后置日志记录: zenghao调用了speak方法,返回值为:I’m return value

环绕增强配置

在前面的基础上,我们新增环绕增强类:

package test.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class AroundAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println(invocation.getArguments()[0] + "——" +
        invocation.getStaticPart() + "——" +
        invocation.getThis().getClass() + "——" +
        invocation.getMethod().getName());
        invocation.proceed();//反射调用目标对象方法
        System.out.println("环绕增强调用结束");
        return "I'm around return value";
    }
}

在aop.xml中类似前置增强增加环绕增强配置,运行测试方法,得到结果:

前置日志记录: zenghao调用了speak方法,传入参数为:21
21——public abstract java.lang.String test.aop.ITarget.speak(java.lang.Integer)——class test.aop.Target——speak
hello I’m 21 years old
环绕增强调用结束
后置日志记录: zenghao调用了speak方法,返回值为:I’m around return value
从以上我们能看到,当同时使用环绕增强和前后置增强时,它们的执行顺序

异常增强配置

常用场景为配置实物管理器,当进行数据库操作的方法发生异常时,可被异常增强捕获,进行失误回滚等操作。一个抛出异常增强的实例如下所示:

package test.aop;

import java.lang.reflect.Method;
import java.sql.SQLException;

import org.springframework.aop.ThrowsAdvice;

public class ExceptionAdvice implements ThrowsAdvice {
    public void AfterThrowing(SQLException e){
        System.out.println(e.getMessage());
    }
    public void AfterThrowing(RuntimeException e){
        System.out.println(e.getMessage());
    }
    public void AfterThrowing(Method method, Object[] args, Object target,SQLException e){
        System.out.println(e.getMessage());
    }
}

其中有几点是需要注意的:
1. 方法名必须为:afterThrowing
2. 三个入参Method method, Object[] args, Object target 要么一起提供,要么都不提供,而最后一个入参必须为Throwable或其子类。当目标对象抛出异常时,增强会调用相似度最高的匹配异常类的方法(在类的继承树上,两个类的距离越近,相似度就越高)

关于引介增强,它可以动态地为类增加新的接口或方法,具有难以想象的动态特性,在后面的学习中我们会再单独提到。

时间: 2024-10-30 21:58:57

spring学习笔记(8)AOP增强(advice)配置与应用的相关文章

Spring学习笔记之aop动态代理(3)

Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. PersonDao.java public interface PersonDao { public void savePerson(); } PersonDaoImpl.java public class PersonDaoImpl implements PersonDao{ public void save

spring学习笔记(21)编程式事务配置,service层概念引入

访问数据库事务导入 在我之前的文章<spring学习笔记(19)mysql读写分离后端AOP控制实例>中模拟数据库读写分离的例子,在访问数据库时使用的方法是: public <E> E add(Object object) { return (E) getSessionFactory().openSession().save(object); } 通过直接开启session而后保存对象.查询数据等操作,是没有事务的.而如果我们的项目规模变大,业务逻辑日益复杂,我们在一个方法中进行大

spring学习笔记(7)AOP前夕[2]CGLib动态代理实例解析

CGLib动态代理基本原理 CGLib--Code Generation Library,它是一个动态字节代码生成库,基于asm.使用CGLib时需要导入asm相关的jar包.而asm又是何方神圣? asm是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能.ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为.Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称

spring学习笔记(22)声明式事务配置,readOnly无效写无异常

在上一节内容中,我们使用了编程式方法来配置事务,这样的优点是我们对每个方法的控制性很强,比如我需要用到什么事务,在什么位置如果出现异常需要回滚等,可以进行非常细粒度的配置.但在实际开发中,我们可能并不需要这样细粒度的配置.另一方面,如果我们的项目很大,service层方法很多,单独为每个方法配置事务也是一件很繁琐的事情.而且也可能会造成大量重复代码的冗杂堆积.面对这些缺点,我们首要想到的就是我们spring中的AOP了.spring声明式事务的实现恰建立在AOP之上. 在这一篇文章中,我们介绍s

spring学习笔记(9)AOP基本概念

通过上一篇文章对AOP的一些实例讲解后,我们在这篇文章中结合一些例子来系统地了解AOP中涉及到的一些基本概念. 1. 增强 上一篇文章我们都在讲增强,但并未谈及它的概念,但通过很多实例分析,我们很容易理解,增强就是为程序执行过程的很多特定地方添加一些新的功能. 2. 连接点 程序执行过程的某个特定位置点,如增强嵌入(织入)处的方法调用前,调用后.抛出异常后等程序执行点,乃至类初始化前后.类销毁前后等,都能称为连接点,它们都带有一定的边界特性. 3. 切点 切点可以理解成"带有目的性"的

spring学习笔记(6)AOP前夕[1]jdk动态代理实例解析

JDK动态代理技术 动态代理最常见应用是AOP(面向切面编程).通过AOP,我们能够地拿到我们的程序运行到某个节点时的方法.对象.入参.返回参数,并动态地在方法调用前后新添一些新的方法逻辑,来满足我们的新需求,比如日志记录等. 动态代理常见有两种方式:基于JDK的反射技术的动态代理和基于CGLib的动态代理. 使用反射技术创建动态代理 JDK创建动态代理的核心是java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类.让我们先分

spring学习笔记(13)基于Schema配置AOP详解

基于Schema配置入门实例 除了基于@AspectJ注解的形式来实现AOP外,我们还可以在IOC容器中配置.先来看看一个常见的应用场景,在我们的web项目中,我们需要为service层配置事务,传统的做法是在每个业务逻辑方法重复下面配置中: Created with Raphaël 2.1.0程序开始1. 获取DAO层封装好的数据库查询API,如HIbernate中的SessionFactory/Session和mybatis中的xxxMapper2. 开启事务3. 根据入参查询数据库完成相应

spring学习笔记(10)@AspectJ研磨分析[1]入门、注解基本介绍

@AspectJ准备 AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. 在使用AspectJ之前,我们需要导入aspectJ相应的jar包,可到我的资源页http://download.csdn.net/detail/qwe6112071/9468329 中下载,而如果使用maven则可直接在pom.xml中加入如下代码: <dependency> <groupId>o

spring学习笔记(19)mysql读写分离后端AOP控制实例

在这里,我们接上一篇文章,利用JNDI访问应用服务器配置的两个数据源来模拟同时操作不同的数据库如同时操作mysql和oracle等.实际上,上个例子可能用来模拟mysql数据库主从配置读写分离更贴切些.既然如此,在本例中,我们就完成读写分离的模拟在web端的配置实例. 续上次的例子,关于JNDI数据源的配置和spring datasource的配置这里不再重复.下面着重加入AOP实现DAO层动态分库调用.可先看上篇文章<spring学习笔记(18)使用JNDI模拟访问应用服务器多数据源实例 >