资讯专栏INFORMATION COLUMN

Spring之面向切面

Olivia / 2102人阅读

摘要:面向切面的面向切面编程的基本原理通过创建切面使用注解为切面注入依赖定义术语通知前置通知在目标方法被调用之前调用通知功能后置通知在目标方法完成之后调用通知,此时不会关心方法的输出是什么返回通知在目标方法成功执行之后调用通知异常通知在目标方

面向切面的Spring

面向切面编程的基本原理

通过POJO创建切面

使用@AspectJ注解

为AspectJ切面注入依赖

定义AOP术语

通知(Advice)

前置通知(Before):在目标方法被调用之前调用通知功能

后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么

返回通知(After-returning):在目标方法成功执行之后调用通知

异常通知(After-throwing):在目标方法抛出异常后调用通知

环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

连接点(Join point),是在应用执行过程中能够插入切面的一个点

切点(Poincut),利用正则表达式定义所匹配的类和方法名称来指定切点

切面(Aspect),通知和切点的结合,它是什么,在何时何处完成其功能

引入(Introduction):允许向现有的类添加新方法或属性,不影响现有的类

织入(Weaving):

把切面应用到目标对象并创建新的代理对象的过程

在指定的连接点被织入到目标对象中

在目标对象的生命周期里有多个点可以进行织入:

编译期:切面在目标类编译时被织入,如AspectJ

类加载期:切面在目标类加载到JVM时被织入,如AspectJ 5的加载时织入(load-time weaving,LTW)

运行期:切面在应用运行的某个时刻被织入,如Spring AOP

Spring对AOP的支持

基于代理的经典Spring AOP

纯POJO切面

@AspectJ注解驱动的切面

注入式AspectJ切面(适用于Spring各版本)

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;

/**
 * 是一个切面,没有提供前置、后置或环绕通知,而是使用了@DeclareParents注解
 * @DeclareParents注解详解:
 * value属性指定了哪种类型的bean要引入的接口,"+"表示Performance的所有子类型
 * defaultImpl属性指定了引入功能提供实现的类
 * @DeclareParents注解所标注的静态属性指明了要引入了接口
 * 
 * 在Spring应用中把EncoreableIntroducer声明为一个bean,当Spring发现@Aspect注解时会创建一个代理,然后将调用委托给被代理的bean或被引入的实现
 * 
 */
@Aspect
public class EncoreableIntroducer {

    @DeclareParents(value="com.leaf.u_spring.chapter04.Performance+",
            defaultImpl=DefaultEncoreable.class)
    public static Encoreable encoreable;
    
    
    
}
通过切点来选择连接点

AspectJ的切点表达式语言

AspectJ指示器和描述

arg(),限制连接点匹配参数为指定类型的执行方法

@args(),限制连接点匹配参数由指定注解标注的执行方法

execution(),用于匹配是连接点的执行方法

this(),限制连接点匹配AOP代理的bean引用为指定类型的类

target(),限制连接点匹配目标对象为指定类型的类

@target(),限制连接点匹配特定的执行对象,这些对象对应的类要具有指定类型的注解

within(),限制连接点匹配指定的类型

@within(),限制连接点匹配指定注解所标注的类型(当使用Sping AOP时,方法定义在由指定的注解所标注的类里)

@annotation,限定匹配带有指定注解的连接点

编写切点 Performance

import java.util.Map;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import com.google.common.collect.Maps;

/**
 * 使用参数化的通知来记录磁道播放的次数
 */
@Aspect
public class TrackCounter {

    private Map trackCounters = Maps.newHashMap();
    
    /**
     * 通知playTrack()方法
     * 
     * 
     * playTrack(int)指定int类型参数
     * && args(trackNumber)指定参数
     * 
     * 
     * @param trackNumber
     */
    @Pointcut("execution(* com.leaf.u_spring.chapter02.CompactDisc.playTrack(int)) && args(trackNumber)")
    public void trackPlayed(int trackNumber){}
    
    /**
     * 播放前为磁道计数
     * @param trackNumber
     */
    @Before("trackPlayed(trackNumber)")
    public void countTrack(int trackNumber){
        int currentCount = getPlayCount(trackNumber);
        trackCounters.put(trackNumber, currentCount + 1);
    }

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

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import com.google.common.collect.Lists;
import com.leaf.u_spring.chapter02.CompactDisc;
import com.leaf.u_spring.chapter03.BlankDisc;


/**
 * 将BlankDisc和TrackCounter定义为bean,并启用AspectJ自动代理
 *
 */
@Configuration
@EnableAspectJAutoProxy
public class TrackCounterConfig {

    @Bean
    public CompactDisc sgtPeppers(){
        BlankDisc cd = new BlankDisc();
        cd.setTitle("阳光总在风雨后");
        cd.setArtist("许美静");
        
        List tracks = Lists.newArrayList();
        tracks.add("阳光总在风雨后");
        tracks.add("成都");
        tracks.add("一生所爱");
        tracks.add("我的中国心");
        tracks.add("Alone Yet Not Alone");
        cd.setTracks(tracks);
        
        return cd;
    }
    
    @Bean
    public TrackCounter trackCounter(){
        return new TrackCounter();
    }
    
}
使用注解创建切面

Spring使用AspectJ注解来声明通知方法

@After 通知方法会在目标方法返回或抛出异常后调用

@AfterReturning 通知方法会在目标方法返回后调用

@AfterThrowing 通知方法会在目标方法抛出异常后调用

@Around 通知方法会将目标方法封装起来

@Before 通知方法会在目标方法调用之前执行

环绕通知是最为强大的通知类型

Java不是动态语言,编译之后很难再添加新的功能,但是引入AOP,切面可以为Spring bean添加新方法,例EncoreableIntroducer

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * 演出效果 
 * 
 * @Aspect注解表明Audience类不仅仅是个POJO,还是一个切面
 * Audience类中的方法都使用注解来定义切面的具体行为
 */
@Aspect
public class Audience {

    /**
     * 表演之前
     */
    @Before("execution(** com.leaf.u_spring.chapter04.Performance.perform(..))")
    public void silenceCellPhones(){
        System.out.println("silence Cell Phones");
    }
    
    /**
     * 表演之前
     */
    @Before("execution(** com.leaf.u_spring.chapter04.Performance.perform(..))")
    public void takeSeates(){
        System.out.println("taking seates");
    }
    
    
    /**
     * 表演之后
     */
    @AfterReturning("execution(** com.leaf.u_spring.chapter04.Performance.perform(..))")
    public void applause(){
        System.out.println("CLAP CLAP CLAP!!!");
    }
    
    /**
     * 表演失败之后
     */
    @AfterThrowing("execution(** com.leaf.u_spring.chapter04.Performance.perform(..))")
    public void demandRefund(){
        System.out.println("Demanding a refund");
    }
    
}

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 演出效果 
 * 相同的切点表达式重复了4遍
 * @Ponitcut注解能够在一个@AspectJ切面内定义可重用的切点
 * 
 */
@Aspect
public class Audience2 {

    /**
     * 定义命名的切点
     */
    @Pointcut("execution(** com.leaf.u_spring.chapter04.Performance.perform(..))")
    public void performance(){}
    
    /**
     * 表演之前
     */
    @Before("performance())")
    public void silenceCellPhones(){
        System.out.println("silence Cell Phones");
    }
    
    /**
     * 表演之前
     */
    @Before("performance())")
    public void takeSeates(){
        System.out.println("taking seates");
    }
    
    
    /**
     * 表演之后
     */
    @AfterReturning("performance())")
    public void applause(){
        System.out.println("CLAP CLAP CLAP!!!");
    }
    
    /**
     * 表演失败之后
     */
    @AfterThrowing("performance())")
    public void demandRefund(){
        System.out.println("Demanding a refund");
    }
    
}
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 演出效果 
 * 创建环绕通知
 * 
 */
@Aspect
public class Audience3 {

    /**
     * 定义命名的切点
     */
    @Pointcut("execution(** com.leaf.u_spring.chapter04.Performance.perform(..))")
    public void performance(){}
    
    /**
     * 环绕通知方法
     */
    @Around("performance())")
    public void watchPerformance(ProceedingJoinPoint jp){
        try {
            System.out.println("silence Cell Phones");
            System.out.println("taking seates");
            //不调用proceed方法,通知会阻塞对被通知方法的调用
            jp.proceed();
            System.out.println("CLAP CLAP CLAP!!!");
        } catch (Throwable e) {
            System.out.println("Demanding a refund");
        }
    }
    
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy            //启用AspectJ自动代理    XMl-->   
@ComponentScan
public class ConcertConfig {

    /**
     * 声明Audience bean
     * @return
     */
    @Bean
    public Audience audience(){
        return new Audience();
    }
}
在XML中声明注解

原则:基于注解的配置优于基于Java的配置,基于Java的配置要优于基于XML的配置

需要声明切面,又不能为通知类添加注解时,须转向XML的配置

Spring的AOP配置能够以非侵入式的方式声明切面:

:定义AOP通知器

:定义AOP后置通知(不管被通知的方法是否执行成功)

:定义AOP返回通知

:定义AOP异常通知

:定义AOP环绕通知

:定义一个切面

:启用@AspectJ注解驱动的切面

:定义一个AOP前置通知

:顶层的AOP配置元素,大多数的元素必须包含在元素内

:以透明的方式为被通知的对象引入额外的接口

:定义一个切点

引用:《Spring In Action 4》第4章

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71165.html

相关文章

  • Spring框架我见(三)——IOC、AOP

    摘要:模块负责的所有面向切面的功能。总结的统一管理,降低了对象之间的耦合对主流的框架提供了很好的集成支持提供众多组件,事务管理,等具有高度可开放性,开发者可以自由选择部分或全部主要使用工厂模式和代理模式。 聊完了Spring框架中最重要的两种设计模式,我们来看一下Spring框架的模块和结构图。 Spring框架的结构 下图是Spring官方给出的Spring框架的结构图。 showImg(...

    khs1994 评论0 收藏0
  • Spring理论基础-面向切面编程

    摘要:序是的缩写,中文翻译是面向切面编程。首先面向切面编程这个名称很容易让人想起面向对象编程来。这种动态地将代码织入到类的指定方法指定位置上的编程思想就是面向切面编程。概念面向切面编程具体的一些概念。 序 AOP是Aspect-Oriented Programming的缩写,中文翻译是面向切面编程。作为Spring的特征之一,是要好好学习的。 首先面向切面编程这个名称很容易让人想起面向对象编...

    voyagelab 评论0 收藏0
  • 慕课网_《Spring入门篇》学习总结

    摘要:入门篇学习总结时间年月日星期三说明本文部分内容均来自慕课网。主要的功能是日志记录,性能统计,安全控制,事务处理,异常处理等等。 《Spring入门篇》学习总结 时间:2017年1月18日星期三说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:https://github.com/zccodere/s...个人学习源码:https://git...

    Ververica 评论0 收藏0
  • Spring核心 面向切面 AOP

    摘要:下图展示了这些概念的关联方式通知切面的工作被称为通知。切面在指定的连接点被织入到目标对象中。该注解表明不仅仅是一个,还是一个切面。 在软件开发中,散布于应用中多处的功能被称为横切关注点(crosscutting concern)。通常来讲,这些横切关注点从概念上是与应用的业务逻辑相分离的(但是往往会直接嵌入到应用的业务逻辑之中)。把这些横切关注点与业务逻辑相分离正是面向切面编程(AOP...

    Winer 评论0 收藏0
  • Spring旅第七站:面向切面编程(AOP)

    摘要:面向切面的本章主要内容面向切面编程的基本原理通过创建切面使用注解为切面注入依赖。什么是面向切面编程切面能够帮我们模块化横切关注点。在使用面向切面编程时,我们仍然在一个地方定义通知功能,而无需修改受影响的类。切面切面是通知和切点的结合。 面向切面的Spring 本章主要内容: 面向切面编程的基本原理 通过POJO创建切面 使用@Aspect注解 为AspectJ切面注入依赖。 说明 ...

    赵连江 评论0 收藏0

发表评论

0条评论

Olivia

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<