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

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

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

  • Spring通知是java编写的,在运行时通知对象,Spring只支持方法级别的连接点,如超过方法需求要用第四种AOP支持
  • Spring 的 AspectJ 自动代理仅仅使用@AspectJ 作为创建切面的知道,切面依然是基于代理的。本质上,它依然是基于代理的切面。

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


  • 在AOP编程中有五种类型的通知:
  1. 前置通知 (@Before) 方法执行之前执行
  2. 返回通知 (@AfterReturning) 方法return之后执行
  3. 异常通知 (@AfterThrowing) 方法出现异常之后执行
  4. 后置通知 (@After) : 又称之为最终通知(finally)
  5. 环绕通知 (@Around) :重点掌握
其结构如下:
try{
   @Before
   //核心业务
   @AfterReturning
}catch(Exception e){
   // ….
   @AfterThrowing
}finally{
   //….
   @After
}

 


  • 实例:看表演

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

正常思路是调用方法。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

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.测试

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;

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

6.运行结果

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

 

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

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

java.lang.ArithmeticException: / by zero

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值