Spring实战05——AOP通知之注解配置

Spring提供了4种类型的AOP支持:
* 1.基于代理的经典SpringAOP
* 2.纯POJO切面
* 3.@AspectJ注解驱动的切面
* 4.注入式AspectJ切面

* 通知:需要调用的方法
* 连接点:可以定义调用通知的所有位置
* 切点:连接点的子集,需要自定义匹配的位置
* 切面:通知和切点共同定义了切面的全部内容
* 引入:允许我们向现有的类添加新方法或属性
* 织入:把切面应用到目标对象并创建新的代理对象的过程,在目标对象的声明周期种有多个点可以织入:编译器,类加载器,运行期。

* Spring通知是java编写的,在运行时通知对象,Spring只支持方法级别的连接点,如超过方法需求要用第四种AOP支持

* Spring 的 AspectJ 自动代理仅仅使用@AspectJ 作为创建切面的知道,切面依然是基于代理的。本质上,它依然是基于代理的切面。

本文使用@AspectJ注解驱动的切面


实例:看表演

看表演前观众需要关手机,找座位,看表演后鼓掌,若表演失败了观众要求重演。

正常思路是调用方法。AOP思想是声明通知,把通知接入切点中成为切面。即重点是看表演,在看表演的前后,自动调用方法(通知)。

1.接口

package com.qhf.aop.example01;

public interface Performance {
    public void perform();
}

2.实现接口的表演类

package com.qhf.aop.example01;

import org.springframework.stereotype.Component;

@Component
public class PerformanceImpl implements Performance{

    @Override
    public void perform() {
        //int a = 1/0;
        System.out.println("表演过程中...");
    }
}

3.配置类,配置文件xml

扫描二维码关注公众号,回复: 6141169 查看本文章
package com.qhf.aop.example01;

import org.springframework.context.annotation.*;

/**
 * 使用AspectJ自动代理两种方式:二选一
 * 1.配置类注解@EnableAspectJAutoProxy 启动AspectJ自动代理
 * 2.使用配置文件需要在配置类注解@ImportResource("classpath:aop/aopxml.xml"),xml中<aop:aspectj-autoproxy/>
 */
@Configuration//作为配置文件之一
//@ImportResource("classpath:aop/aopxml.xml")
@EnableAspectJAutoProxy
@ComponentScan //自动扫描同包下@Component注解的实现类
public class AOPConfig {

}
<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:aspectj-autoproxy/>
</beans>

4.声明切面:@Aspect 注解使用AspectJ定义切面,同时此观众类是一个POJO,它是可以被正常地自动装配,并且切面自动代理是通过调用方法的,所以还是要使用@Component 注解作为组件类,以便被自动装配为bean。

切点表达式execution(* com.qhf.aop.example01.Performance.perform(..)):*表示可以是任何类型的返回值,com.qhf.aop.example01 是包名,Performance 类名或接口,perform是方法,(..)表示任何参数,所以运行时匹配这个规则的执行都会嵌入这个切面。

@Before("execution(* com.qhf.aop.example01.Performance.perform(..)) && !bean(performanceImpl)") 表示并且不是id为performanceImpl实例运行

通知@Before在执行前;@AfterReturning在执行返回后;@AfterThrowing在执行错误后;@Around会将目标方法封装起来,通过ProceedingJoinPoint 的proceed() 方法自定义通知在目标方法前后;@After 在目标方法返回或抛出后调用

package com.qhf.aop.example01;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class Audience {

    /**
     * 当切点表达式使用频繁的时候,"execution(* com.qhf.aop.example01.Performance.perform(..))"
     * 可以通过@Pointcut注解声明频繁使用的切点表达式,然后在其他方法注解调用此切点
     */
    @Pointcut("execution(* com.qhf.aop.example01.Performance.perform(..))")
    public void pointcut(){

    }

    //表演前
    @Before("pointcut()")
    //@Before("execution(* com.qhf.aop.example01.Performance.perform(..))")
    public void silencePhones(){
        System.out.println("关手机...");
    }

    @Before("pointcut()")
    //@Before("execution(* com.qhf.aop.example01.Performance.perform(..))")
    public void takeSeats(){
        System.out.println("找座位...");
    }

    //表演后
    @AfterReturning("pointcut()")
    //@Before("execution(* com.qhf.aop.example01.Performance.perform(..))")
    public void applause(){
        System.out.println("鼓掌...");
    }

    //出现异常后
    @AfterThrowing("pointcut()")
    //@Before("execution(* com.qhf.aop.example01.Performance.perform(..))")
    public void demandRefund(){
        System.out.println("要求重演...");
    }
}

或者使用@Around 环绕通知 

@Aspect
@Component
public class Audience {

    @Pointcut("execution(* com.qhf.aop.example02.Performance.perform(..))")
    public void pointcut(){

    }

    @Around("pointcut()")
    public void watchPerformance(ProceedingJoinPoint pj){
        try {
            silencePhones();
            takeSeats();

            pj.proceed();//运行方法

            applause();
        } catch (Throwable t){
            demandRefund();
            t.printStackTrace();
        }
    }

    //表演前
    public void silencePhones(){
        System.out.println("关手机...");
    }

    //表演前
    public void takeSeats(){
        System.out.println("找座位...");
    }

    //表演后
    public void applause(){
        System.out.println("鼓掌...");
    }

    //出现异常后
    public void demandRefund(){
        System.out.println("要求重演...");
    }

}

5.测试,切面自动代理会创建表演类的实例,调用它的方法,所以我们不需要使用@Autowired 注解装配表演类bean

package com.qhf.aop.example01;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= AOPConfig.class)
public class Test {

    @Autowired
    private Performance performance;//即便这里名字是performance,符合这个接口的实现类是PerformanceImpl,所以bean的id是performanceImpl

    @org.junit.Test
    public void test(){
        performance.perform();
    }
}

6.运行结果

关手机...
找座位...
表演过程中...
鼓掌...

若是表演类中执行 int a = 1/0;抛错,结果为:

关手机...
找座位...
要求重演...

java.lang.ArithmeticException: / by zero

猜你喜欢

转载自blog.csdn.net/pigqhf/article/details/88964988
今日推荐