Spring4.3使用注解创建切面

Posted Java全栈从0到1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring4.3使用注解创建切面相关的知识,希望对你有一定的参考价值。

AspectJ5之前,编写AspectJ切面需要学习一种Java语言的扩展。

AspectJ5引入了使用注解来创建切面的关键特性,AspectJ面向注解的模型可以非常简便地通过注解把任意类转变为切面。

1、定义切面

@Aspect
public class Audience{
    //表演前
    @Before("execution(** concert.Performance.perform(..))")
    public void silenceCellPhones(){
        System.out.println("Silencing cell phones");
    }
    //表演前
    @Before("execution(** concert.Performance.perform(..))")
    public void takeSeats(){
        System.out.println("Taking seats");
    }
    //表演后
    @AfterReturning("execution(** concert.Performance.perform(..))")
    public void applause(){
        System.out.println("Clap!!");
    }
    //表演失败后
    @AfterThrowing("execution(** concert.Performance.perform(..))")
    public void demandRefund(){
        System.out.println("Refund!!");
    }

}

@Aspect注解表示Audience不仅是一个POJO,还是一个切面。

@Before,@AfterReturning等注解,用来声明通知方法。

使用@Pointcut注解定义可重用切点

@Aspect
public class Audience{
    //可重用切点
    @Pointcut("excution(** concert.performance.perform(..))")
    public void performance(){}
    //表演前
    @Before("performance()")
    public void silenceCellPhones(){
        System.out.println("Silencing cell phones");
    }
    //表演前
    @Before("performance()")
    public void takeSeats(){
        System.out.println("Taking seats");
}

 使用一个空的方法,作为标记,使用@Pointcut注解定义成一个可重用的节点,然后通过“方法名()”来进行引用

 启用自动代理

以上仅仅是定义了切面,要启动切面功能,需要启动切面的代理

Java配置方法

@Configuration
@EnableAspectJAutoProxy//启动AspectJ自动代理
@ComponentScan
public class ConcertConfig{
    @Bean
    Public Audience audience(){//声明Audience bean
        return new Audience();
    }
}

 

 XML配置方法(使用aop命名空间)

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

    <context:conponent-scan base-package="concert" />

    <!-- 启用AspectJ自动代理 -->
    <aop:aspectj-autoproxy />
    
    <!-- 声明Audience bean -->
    <bean class="concert.Audience" />
    
</beans>

 

代理的作用

使用上述2种方法,会给Concert bean创建一个代理,Audence类中的通知方法会在perform()调用前后执行。

注意!Spring的AspectJ自动代理仅仅使用@AspectJ作为创建切面的知道,切面本质上还是Spring基于代理的切面,仅局限于代理方法的调用。

如果想要使用AspectJ的所有能力,必须运行时使用AspectJ并且不依赖Spring。

2、创建环绕通知

@Aspect
public class Audience{
    //可重用切点
    @Pointcut("excution(** concert.performance.perform(..))")
    public void performance(){}

    //环绕通知方法
    @Around("performance()")
    public void watchPerformance(ProceedingJoinPoint jp){
        try{
            System.out.println("Silencing cell phones");
            System.out.println("Taking seats");
            jp.proceed();
            System.out.println("CLAP!!");
        }catch(Throwable e){
            System.out.println("refund!!");
        }
    }
}

 可以将前置和后置通知写在一个方法中,并使用ProceedingJoinPoint对象来进行对目标方法的调用。

3、处理通知中的参数

@Aspect
public class TrackCounter{
    private Map<Interger,Integer> trackCounts = new HashMap<Integer,Integer>();

    @Pointcut(
        "execution(* soundsystem.CompactDisc.playTrack(int))" +
        "&& args(trackNumber)")
    public  void trackPlayed(int trackNumber){}

    @Before("trackPlayed(trackNumber)")
    public void countTrrack(int trackNumber){
        int currentCount = getPlayCount(trackNumber);
        trackCounts.put(trackNumber,currentCount + 1);
    }

    public int getPlayCount(int trackNumber){
        return trackCounts.containsKey(trackNumber)
                ? trackCounts.get(trackNumber) : 0;
    }
}

4、通过注解引入新功能

假设情景:有一个类,希望让其以及其实例实现某个接口,但这个类是不可以修改的(如没有源码)。我们可以通过AOP为这个类引入新的方法,实现该接口。

例如

我们有一个类concert.Performance

希望能通过AOP实现接口

public interface Encoreable{
    void performEncore();
}

 切面

@Aspect
public class EncoreableIntroducer{
    @DeclareParents(value="concert.performance+",defaultImpl=DefaultEncoreable.class)
    public static Encoreable encoreable;
}

 通过声明一个切面,使用@DeclareParents注解,讲Encoreable接口引入到Performance bean中。

@DeclareParents的组成部分

1、Value属性指定哪种类型bean要引入该接口。本例中,指所有实现Performance的类型。(加号表示是Performance的所有子类型,而不是Performance本身。)

2、defaultImpl属性指定为引入功能提供实现的类。在这里我们指定的是DefaultEncoreable提供实现。

3、@DeclareParents注解所标注的惊天属性知名了要引入的接口。在这里我们引入的是Encoreable接口。

 

以上是关于Spring4.3使用注解创建切面的主要内容,如果未能解决你的问题,请参考以下文章

java怎么运用切面编程生成日志

Sping——使用注解创建切面

AOP面向切面编程(使用注解和使用配置文件)

SpringBoot基于注解切面监听事件

springboot 面向切面编程之使用自定义注解

Spring面向切面编程AOP(around)实战