• Spring Boot


    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+" args="+stringBuilder.toString()

      +"startTimeMillis="+startTimeMillis.get()+" Thread="+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+" args="+stringBuilder.toString()

      +"executingTimeMillis="+executingTimeMillis+" Thread="+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+" password="+password+" sleep="+sleep+" Thread="

      +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

         

         

  • 相关阅读:
    3.30作业
    3.30课堂
    3.29作业
    3,29课堂
    3。26作业
    3.26课堂
    3.25作业
    3.25课堂
    55、DOM与BOM的操作及事件的简介
    54、js的数据类型及对象
  • 原文地址:https://www.cnblogs.com/li3807/p/9002681.html
Copyright © 2020-2023  润新知