AOP 全称 Aspect Oriented Programming(面向切面),AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。其与设计模式完成的任务差不多,是提供另一种角度来思考程序的结构,来弥补面向对象编程的不足。

通俗点讲就是提供一个为一个业务实现提供切面注入的机制,通过这种方式,在业务运行中将定义好的切面通过切入点绑定到业务中,以实现将一些特殊的逻辑绑定到此业务中。

比如,若是需要一个记录日志的功能,首先想到的是在方法中通过日志框架来进行记录日志,但写下来发现一个问题,在整个业务中其实核心的业务代码并没有多少,都是一些记录日志或其他辅助性的一些代码。而且很多业务有需要相同的功能,比如都需要记录日志,这时候又需要将这些记录日志的功能复制一遍,即使是封装成框架,也是需要调用之类的。

AOP 的基本概念

  • Aspect:切面,一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。在Spring AOP中,切面可以使用基于模式或者基于@Aspect 注解的方式来实现。
  • Joinpoint:连接点,在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。
  • Advice:通知,在切面的某个特定的连接点上执行的动作。其中包括了"Around"、"Before"和"After"等不同类型的通知(通知的类型将在后面部分进行讨论)。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
  • Pointcut:切入点,匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring 默认使用AspectJ切入点语法。
  • Introduction:引入,用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制。
  • Target Object:目标对象,被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。既然 Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
  • AOP Proxy:AOP代理,AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
  • Weaving:织入,把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时(例如使用AspectJ编译器),类加载时和运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

 
 

快速入门

我们通过实现记录方法执行时间的跟踪日志来演示 Spring AOP,示例如下:

  • 创建项目

    创建 Spring Boot 项目,命名为 spring-aop,增加 spring-boot-starter-aop 依赖开启 AOP 支持,pom.xml 文件内容如下:

    <?xmlversion="1.0"encoding="UTF-8"?>

    <projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

     
     

    <groupId>org.lixue</groupId>

    <artifactId>spring-aop</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <packaging>jar</packaging>

     
     

    <name>spring-aop</name>

     
     

    <parent>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-parent</artifactId>

    <version>1.5.12.RELEASE</version>

    <relativePath/><!--lookupparentfromrepository-->

    </parent>

     
     

    <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

    <java.version>1.8</java.version>

    </properties>

     
     

    <dependencies>

    <dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-aop</artifactId>

    </dependency>

     
     

    <dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-test</artifactId>

    <scope>test</scope>

    </dependency>

    </dependencies>

     
     

    <build>

    <plugins>

    <plugin>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-maven-plugin</artifactId>

    </plugin>

    </plugins>

    </build>

    </project>

     
     

  • 创建注解

    我们通过注解来标注切面,因此我们需要创建注解类,使用 @Target 注解使用在方法中,使用 @Retention 注解用来声明注解的保留策略,代码如下:

    package org.lixue;

     
     

    import java.lang.annotation.*;

     
     

    @Target(value={ElementType.METHOD,ElementType.TYPE})

    @Retention(RetentionPolicy.RUNTIME)

    @Documented

    public @interface EanbleLogTrace{

    String name() default "";

    }

     
     

  • 创建切面类

    创建切面类 TraceLogAspect,用于定义跟踪日志的切入点(被 org.lixue.EnableLogTrace 注解标注的方法和类为切入点),和通知方法,代码如下:

    package org.lixue;

     
     

    import org.aspectj.lang.JoinPoint;

    import org.aspectj.lang.annotation.After;

    import org.aspectj.lang.annotation.Aspect;

    import org.aspectj.lang.annotation.Before;

    import org.aspectj.lang.annotation.Pointcut;

    import org.springframework.stereotype.Component;

     
     

    @Aspect

    @Component

    public class TraceLogAspect{

    private ThreadLocal<Long> startTimeMillis;

    public TraceLogAspect(){

    startTimeMillis=new ThreadLocal<>();

    }

     
     

    /**

    *定义切入点,使用注解方式,声明了被org.lixue.EnableLogTrace注解标注的方法和类为切入点

    */

    @Pointcut("@annotation(org.lixue.EnableLogTrace) || within(@org.lixue.EnableLogTrace *)")

    private void pointcut(){

     
     

    }

     
     

    /**

    *定义通知before(方法执行前),切面使用pointcut()定义的

    *

    *@param joinPoint

    */

    @Before("pointcut()")

    public void before(JoinPointjoinPoint){

    Object[] args=joinPoint.getArgs();

    String methodName=joinPoint.getSignature().getName();

    startTimeMillis.set(System.currentTimeMillis());

    StringBuilder stringBuilder=new StringBuilder();

    for(inti=0;i<args.length;i++){

    stringBuilder.append(args[i]+"");

    }

     
     

    System.out.println("beforename="+methodName+"\targs="+stringBuilder.toString()

    +"startTimeMillis="+startTimeMillis.get()+"\tThread="+Thread.currentThread().getId());

    }

     
     

    /**

    *定义通知After(方法执行后),切面使用pointcut()定义的

    *

    *@param joinPoint

    */

    @After("pointcut()")

    public void after(JoinPointjoinPoint){

    Object[] args=joinPoint.getArgs();

    String methodName=joinPoint.getSignature().getName();

    try{

    long executingTimeMillis=System.currentTimeMillis()-startTimeMillis.get();

    StringBuilder stringBuilder=newStringBuilder();

    for(inti=0;i<args.length;i++){

    stringBuilder.append(args[i]+"");

    }

     
     

    System.out.println("aftername="+methodName+"\targs="+stringBuilder.toString()

    +"executingTimeMillis="+executingTimeMillis+"\tThread="+Thread.currentThread().getId());

    }finally{

    startTimeMillis.remove();

    }

    }

    }

     
     

  • 创建示例类

    创建示例类,使用注解 @EnableLogTrace 标注类或者方法

    package org.lixue;

     
     

    import org.springframework.stereotype.Service;

     
     

    import java.util.Random;

     
     

    @EnableLogTrace(name="trace")

    @Service

    public class AccountService{

    private Random random;

     
     

    publicAccountService(){

    random = new Random();

    }

     
     

    public boolean login(Stringname,Stringpassword){

    int sleep;

    try{

    sleep=random.nextInt(100);

    Thread.sleep(sleep);

    System.out.println("loginname="+name+"\tpassword="+password+"\tsleep="+sleep+"\tThread="

    +Thread.currentThread().getId());

    return true;

    }catch(InterruptedExceptione){

    e.printStackTrace();

    return false;

    }

    }

    }

     
     

  • 测试验证

    编写单元测试类,通过自动注入 AccountService 类,来调用 login 方法,看日志输入:

    package org.lixue;

     
     

    import org.junit.Test;

    import org.junit.runner.RunWith;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.boot.test.context.SpringBootTest;

    import org.springframework.test.context.junit4.SpringRunner;

     
     

    import static org.junit.Assert.*;

     
     

    @RunWith(SpringRunner.class)

    @SpringBootTest

    public class AccountServiceTest{

    @Autowired

    private AccountService accountService;

     
     

    @Test

    public void login() throws Exception{

    accountService.login("lixue","123123");

    }

    }

    执行单元测试输出如下:

    before name=login        args=lixue 123123 startTimeMillis=1525590400031        Thread=1

    login name=lixue        password=123123        sleep=86        Thread=1

    after name=login        args=lixue 123123 executingTimeMillis=114        Thread=1

     
     

     
     

Spring Boot - AOP(面向切面)的更多相关文章

  1. spring总结————AOP面向切面总结

    spring总结————AOP面向切面 一.spring aop概念 spring aop面向切面编程,java是面向对象的语言. 真正的service层代码 业务逻辑层再处理业务之前和之后都要进行一 ...

  2. Spring:AOP面向切面编程

    AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果. AOP是软件开发思想阶段性的产物,我们比较熟悉面向过程O ...

  3. Spring 08: AOP面向切面编程 + 手写AOP框架

    核心解读 AOP:Aspect Oriented Programming,面向切面编程 核心1:将公共的,通用的,重复的代码单独开发,在需要时反织回去 核心2:面向接口编程,即设置接口类型的变量,传入 ...

  4. Spring框架 AOP面向切面编程(转)

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  5. 【spring源码学习】spring的AOP面向切面编程的实现解析

    一:Advice(通知)(1)定义在连接点做什么,为切面增强提供织入接口.在spring aop中主要描述围绕方法调用而注入的切面行为.(2)spring定义了几个时刻织入增强行为的接口  => ...

  6. Spring的AOP面向切面编程

    什么是AOP? 1.AOP概念介绍 所谓AOP,即Aspect orientied program,就是面向方面(切面)的编程. 功能: 让关注点代码与业务代码分离! 关注点: 重复代码就叫做关注点: ...

  7. spring:AOP面向切面编程02

    参考: https://blog.csdn.net/jeffleo/article/details/54136904 一.AOP的核心概念AOP(Aspect Oriented Programming ...

  8. Spring注解 - AOP 面向切面编程

    基本概念: AOP:Aspect Oriented Programming,即面向切面编程 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式 前置通知(@Before):在目标 ...

  9. Spring框架——AOP面向切面编程

    简介 AOP练习 使用动态代理解决问题 Spring AOP 用AspectJ注解声明切面 前置后置通知 利用方法签名编写AspectJ切入点表达式 指定切面的优先级 基于XML的配置声明切面 Spr ...

  10. Spring之AOP(面向切面编程)_入门Demo

    AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可 ...

随机推荐

  1. springboot - websocket实现及原理

    本文章包括websocket面试相关问题以及spring boot如何整合webSocket. 参考文档 https://blog.csdn.net/prayallforyou/article/det ...

  2. codeforces 1140D(区间dp/思维题)

    D. Minimum Triangulation time limit per test 2 seconds memory limit per test 256 megabytes input sta ...

  3. python之django母板页面

    其实就是利用{% block xxx %}   {% endblock %}的方式定义一个块,相当于占位.存放在某个html中,比如base.html 然后在需要实现这些块的文件中,使用继承{% ex ...

  4. OpenGL绘制一个四边形

    学习自:https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/ OpenGL没有直接绘制四边形的a ...

  5. 关于Hibernate级联更新插入信息时提示主键不为空的问题“org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1 ”

    org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual ...

  6. windows下postgreSQL安装与启动

    转:https://www.yiibai.com/postgresql/install-postgresql.html https://blog.csdn.net/irainreally/articl ...

  7. python安装代码包提示缺少 VC++ 14 控件的处理方式

    下载如下文件,然后安装 http://go.microsoft.com/fwlink/?LinkId=691126

  8. Struts2 action 跳转到web-inf下,

    初学,这个问题困扰了1天.刚开始以为代码或者是配置文件哪里写错了,仔细检查了好几遍确定代码没有错,实在没办法了,准备发帖求助.就在我截图的时候,突然发现我的@Action中有点别扭,即使他没有报错. ...

  9. sqlite比较时间起始1天的0点

    当天时间的0:00:00 strftime('%Y-%m-%d %H:%M:%S','now','localtime','start of day')

  10. Python全站之路----常用模块----configparser模块

    config:配置    parser:解析 此模块用于生成和修改常见配置文档,当前模块的名称在 python 3.x 版本中变更为 configparser,在 python 2.x 里名字为 Co ...