Spring AOP增强(Advice)

作者: Real_man | 来源:发表于2018-07-01 23:25 被阅读30次

Sring AOP通过PointCut来指定在那些类的那些方法上织入横切逻辑,通过Advice来指定在切点上具体做什么事情。如方法前做什么,方法后做什么,抛出异常做什么。

再来看一下图


定义PointCut

Spring中有两种方式定义Pointcut:

  • XML文件
  • 注解

在xml文件中,入门中做了的演示:


<aop:config>
        <!-- 定义切点 -->
        <aop:pointcut id="hello" expression="execution(public * * (..))"></aop:pointcut>
       ...
</aop:config>

注解的方式:

@Component
@Aspect
public class HelloWorld {

    @Pointcut("execution(public * * (..))")
    public void sayHello(){
        System.out.println("hello");
    }
    
    ...
}

XML与注解方式类似,学会了一种,另外一种无难度上手,我们使用XML演示。

增强(Advice)

通过Idea,看一下Advice接口的接口继承类:
[图片上传失败...(image-d49d25-1530458748566)]

主要可分为5类增强:

  • MthodBeforeAdvice:目标方法实施前增强

  • AfterReturningAdvice:目标方法实施后增强

  • ThrowsAdvice 异常抛出增强

  • IntroductionAdvice 引介增强,为目标类添加新的属性和方法。可以构建组合对象来实现多继承

  • MethodInterceptor 方法拦截器,环绕增强,在方法的前后实施操作

AfterAdvice,BeforeAdvice当前是作为标记使用,内部无接口方法,为后来扩展使用。
Interceptor接口也不是直接使用,同样作为标记类,可使用其子接口。

前置增强

前置增强主要在匹配到的切点运行之前执行,在XML配置中使用<aop:before>,相应的接口为MethodBeforeAdvice,其方法为

    /**
     * @param method 要被激发的方法
     * @param args 方法的参数
     * @param target 目标对象,可能为null
     * @throws Throwable 如果这个对象想要终止调用,那么抛出异常,异常会返回给方法调用者,否则异常会被包装为运行时异常
     */
void before(Method method, Object[] args, Object target) throws Throwable;

当一个Bean对象实现了MethodBeforeAdvice,在XML配置文件中指定这个bean为advice,Spring会自动在切点方法执行前执行MethodBeforeAdvice的接口。

    <bean id="helloworld" class="me.aihe.exam.controller.HelloWorld" />
    
    <!-- timelog实现了MethodBeforeAdvice接口 -->
    <bean id="timeLog" class="me.aihe.exam.controller.TimeLoggingAop" />
    <aop:config>
        <aop:pointcut id="hello" expression="execution(public * * (..))"></aop:pointcut>
        
        <!-- advisor的作用为将Pointcut和Advice组装起来 -->
        <aop:advisor
                id="timelogAdvisor"
                advice-ref="timeLog"
                pointcut-ref="hello"
        />
    </aop:config>

在调用相应的切点方法之前,前置增强都会生效。

后置增强

弄明白了前置增强,后置增强也是同一个道理,不过后置增强是在切点运行后执行。接口为AfterReturningAdvice,方法为

/**
     * 当方法成功返回之后运行
     * @param returnValue 如果方法有返回值,那么returnValue就是返回值
     * @param method 激发的方法
     * @param args 方法的参数
     * @param target 目标对象
     * @throws Throwable 同上
     */
    void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;

在切点方法运行之后,后置增强会生效。

异常抛出增强

当切点方法抛出异常时,异常抛出增强才会被执行。其接口为ThrowsAdvice,接口没有指定方法,实现这个接口的对象是通过反射来调用其增强方法的。

// 异常增强方法格式如下
// 前面的三个参数是可选的,ThrowableSubclass为Throwable的子类
void afterThrowing([Method, args, target], ThrowableSubclass)

//案例如下
public void afterThrowing(Exception ex)
public void afterThrowing(RemoteException)
public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
public void afterThrowing(Method method, Object[] args, Object target, ServletException ex)

增强案例

根据前面的前置,后置,异常抛出增强,看一个完整的案例:

// TimeLoggingAop实现了前置增强,后置增强,异常环绕增强
public class TimeLoggingAop implements MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice {
    private long startTime = 0;

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        startTime = System.nanoTime();
    }


    @Override
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
        long spentTime = System.nanoTime() - startTime;
        String clazzName = target.getClass().getCanonicalName();
        String methodName = method.getName();
        System.out.println("执行" + clazzName + "#" + methodName + "消耗" + new BigDecimal(spentTime).divide(new BigDecimal(1000000)) + "毫秒");
    }

    public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
        System.out.println("执行" + method.getName() + "出现异常," + "异常消息为:" + ex.getMessage());
    }
}
// 普通的HelloWorld对象
public class HelloWorld {
    
    public void sayHello(){
        System.out.println("hello");
    }

    public void sayHelloWithException(){
        System.out.println("hello");
        throw new RuntimeException("Hello World运行时出了一点问题");
    }
}

  public static void main(String[] args) {
        //普通对象
        HelloWorld helloWorld = new HelloWorld();
        //增强对象
        BeforeAdvice beforeAdvice = new TimeLoggingAop();
        //组装普通对象和增强对象
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(helloWorld);
        proxyFactory.addAdvice(beforeAdvice);

        //获取组装后的代理对象
        HelloWorld proxyHelloWorld = (HelloWorld) proxyFactory.getProxy();
        //运行
        proxyHelloWorld.sayHello();
        proxyHelloWorld.sayHelloWithException();
    }

//运行结果
hello
执行me.aihe.exam.controller.HelloWorld#sayHello消耗29.392268毫秒

hello
执行sayHelloWithException出现异常,异常消息为:Hello World运行时出了一点问题

通过上面这个案例,我们大体知道什么是增强,字面意思:
在原本方法之上增加一些额外的东西,原本的功能增强了,所以叫增强,这是中文翻译过来的增强。
英文名为Advice,建议,在方法周围建议方法做什么事情,然后真的做了...

环绕增强

环绕增强可以理解为前置增强,后置增强,异常抛出增强的结合体,只有一个接口MethodInterceptor,其方法为:

// MethodInvocation封装了目标对象和参数数组,调用它的invocation.proceed()方法即激发目标对象的方法。
Object invoke(MethodInvocation invocation) throws Throwable;

一个用法案例:

    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("前置增强执行");
        try {

            Object result = invocation.proceed();
            
            System.out.println("后置增强执行");
            
            return result;
        }catch (Exception e){
            System.out.println("异常抛出增强执行");
            //做其它的动作
        }
        
        return null;
    }

增强顺序

当多个增强作用与同一个切点的时候,具体哪一个增强会先执行呢?

Spring根据@Order注解或者实现了Ordered接口的增强类来进行判断。

public interface Ordered {
    int HIGHEST_PRECEDENCE = -2147483648;
    int LOWEST_PRECEDENCE = 2147483647;
    int getOrder();
}

注解相关增强

增强的概念一样,在使用方式上稍微有点区别,增强的相关注解有:
@Before
@AfterReturning
@AfterThrowing(
@After //相当于try-catch-finally中的final,一般用于释放资源
@Around

使用上与接口差不多

最后

增强是AOP中核心概念之一,本文在增强上稍作一些探索,深入还要靠大家,最后希望能帮到大家

参考

  • 《Spring Reference》
  • 《精通Spring 4.x 企业级应用开发实战》

相关文章

  • Spring AOP 代理

    Spring AOP 代理 1. Spring AOP 增强类型 AOP 联盟为通知 Advice 定义了 org...

  • Spring AOP

    Spring AOP 一、面向切面编程 1. AOP术语 1.1 ADVICE(增强) Spring提供了以下几种...

  • Spring AOP增强(Advice)

    Sring AOP通过PointCut来指定在那些类的那些方法上织入横切逻辑,通过Advice来指定在切点上具体做...

  • Spring与设计模式

    适配器模式 在Spring的Aop中,使用的Advice(通知)来增强被代理类的功能。每个类型Advice都有对应...

  • Spring之AOP

    Spring之AOP 第二步:增强(advice) 第三:在applicationContext.xml中进行配置...

  • Spring一般切面

    Spring一般切面 Spring 实现了AOP联盟Advice这个接口=>org.aopalliance.aop...

  • Sping - AOP - Advice

    原文地址:https://mkyong.com/spring/spring-aop-examples-advice...

  • 03 AOP学习之五种通知

    Spring AOP五种通知详解 spring aop通知(advice)分成五类: 前置通知Before adv...

  • AOP的基本术语

    通知、增强(Advice) 通知就是你想要的功能,在特定的连接点,AOP框架执行的动作。Spring切面可以应用5...

  • 笔试题 计算机

    spring aop通知(advice)分成五类:前置通知[Before advice]:在连接点前面执行,前置通...

网友评论

    本文标题:Spring AOP增强(Advice)

    本文链接:https://www.haomeiwen.com/subject/mczhuftx.html