乐趣区

关于java:动力节点Spring框架学习笔记王鹤二AOP面向切面编程

二、AOP 面向切面编程

官网下载地址

能源节点 spring 材料

视频观看地址

https://www.bilibili.com/video/BV1nz4y1d7uy

2.1 概述

AOP(Aspect Orient Programming)。面向切面编程是从动静角度思考程序运行过程

  • AOP 底层,就是采纳动静代理模式实现的。采纳了两种代理:JDK 的动静代理,与 CGLIB 的动静代理,AOP 就是动静代理的规范化,把动静代理的实现步骤,形式都定义好了,让开发人员用一种对立的形式,应用动静代理
  • Aspect: 切面,给你的指标类减少的性能,就是切面。像下面用的日志,事务都是切面。切面的特点: 个别都是非业务办法,独立应用的
  • Orient:面向,对着
  • Programming:编程

2.2 相干术语

1. Aspect: 切面,示意加强的性能,就是一堆代码,实现某个一个性能。非业务性能,常见的切面性能有日志,事务,统计信息,参数查看,权限验证,切面用于组织多个 Advice,Advice 放在切面中定义,理论就是对主业务逻辑的一种加强

2. JoinPoint: 连接点,连贯业务办法和切面的地位。就某类中的业务办法,程序执行过程中明确的点,如办法的调用,或者异样的抛出。在 Spring AOP 中,连接点总是办法的调用

3. Pointcut: 切入点,指多个连接点办法的汇合。多个办法。能够插入加强解决的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被增加加强解决,该连接点也就变成了切入点

4. Advice:AOP 框架在特定的切入点执行的加强解决。解决有 ”around”、”before” 和 ”after” 等类型,能示意切面性能执行的工夫,切入点定义切入的地位,告诉定义切入的工夫

5. Target: 指标对象,目 标 对 象 指 将 要 被 增 强 的 对 象。即 包 含 主 业 务 逻 辑 的 类 的 对 象

2.3 AspectJ

2.3.1 概述

AspectJ 是一个基于 Java 语言的 AOP 框架,提供了弱小的 AOP 性能, 其次要包含两个局部:

  • 一个局部定义了如何表白、定义 AOP 编程中的语法标准;
  • 另一个局部是工具局部,包含编译、调试工具等

aspectJ 框架实现 aop 的两种形式:

  1. 应用 xml 的配置文件:配置全局事务
  2. 应用注解,咱们在我的项目中要做 aop 性能,个别都应用注解,aspectj 有 5 个注解
  • @Before
  • @AfterReturning
  • @Around
  • @AfterThrowing
  • @After

2.3.2 AspectJ 的切入点表达式

表达式原型:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

![/upload-images.jianshu.io/upload_images/27220553-f998cc2391d3017d.gif?imageMogr2/auto-orient/strip)

相干解释:

  • modifiers-pattern? 拜访权限类型
  • ret-type-pattern 返回值类型
  • declaring-type-pattern? 包名类名
  • name-pattern(param-pattern) 办法名 (参数类型和参数个数)
  • throws-pattern 抛出异样类型
  • ? 示意可选的局部

以上表达式一共 4 个局部

execution(拜访权限 办法返回值 办法申明 ( 参数) 异样类型 )

符号 意义
* 0 至多个任意字符
. . 用在办法参数中,示意任意多个参数;用在包名后,示意以后包与子包门路
+ 用在类名后,示意以后类及其子类;用在接口后,示意以后接口及其实现类

相干实例:

  • execution(public (..)): 任意公共办法
  • execution( set(..)): 任何一个以“set”开始的办法
  • execution( com.xyz.service..*(..)): 定义在 service 包里的任意类的任意办法
  • execution( com.xyz.service...(..)): 定义在 service 包或者子包里的任意类的任意办法。“..”呈现在类名中时,前面必须跟“”,示意包、子包下的所有类
  • execution( ..service..(..)): 指定所有包下的 serivce 子包下所有类(接口)中所有办法为切入点
  • execution( com.xyz.service.IAccountService+.(..)):IAccountService 若为接口,则为接口中的任意办法及其所有实现类中的任意办法;若为类,则为该类及其子类中的任意办法
  • execution(* joke(String,int))): 所有的 joke(String,int) 办法,且 joke() 办法的第一个参数是 String,第二个参数是 int; 如果办法中的参数类型是 java.lang 包下的类,能够间接应用类名,否则必须应用全限定类名,如 joke( java.util.List, int)

2.3.3 前置告诉:@Before

1. 办法的定义要求:

  • 公共办法 public
  • 办法没有返回值
  • 办法名称自定义
  • 办法能够有参数,也能够没有参数

2.@Before: 前置告诉注解

属性:value,是切入点表达式,示意切面的性能执行的地位

地位:在办法的下面

1. 配置依赖

<!--spring 依赖 -->

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-context</artifactId>

      <version>5.2.5.RELEASE</version>

    </dependency>

    <!--aspectj 依赖 -->

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-aspects</artifactId>

      <version>5.2.5.RELEASE</version>

    </dependency>

2. 创立业务接口与实现类对象

Service.interface

public interface Service {public void doSome();

}

ServiceImpl.java

@org.springframework.stereotype.Service("myService")

public class ServiceImpl implements Service {

    @Override

    public void doSome() {System.out.println("这是我的业务办法!!!");

    }

}

3. 创立切面类:MyAspect.java

@Aspect

@Component("myAspect")

public class MyAspect {

    /**

    * 指定告诉办法中的参数:JoinPoint

    *

    */

    @Before(value = "execution(void *..doSome(..))")

    public void before(){System.out.println("这是前置告诉");

    }

}

4. 配置 applicationContext.xml 文件

    <!-- 扫描文件 -->

    <context:component-scan base-package="com.jjh.*"/>

    <!-- 申明主动代理生成器 -->

    <aop:aspectj-autoproxy/>

5. 测试类调用

@org.junit.Test

    public void demo01(){

        String config = "applicationContext.xml";

        ApplicationContext app = new ClassPathXmlApplicationContext(config);

        Service proxy = (Service) app.getBean("myService");

        // 输入以后类的信息:com.sun.proxy.$Proxy17

        // 证实其应用的是 JDK 动静代理

        System.out.println(proxy.getClass().getName());

        proxy.doSome();}

2.3.4 JoinPoint

  • 指定告诉办法中的参数:JoinPoint
  • JoinPoint:业务办法,要退出切面性能的业务办法
  • 作用:能够在告诉办法中获取办法执行时的信息,例如办法名称,办法的实参
  • 如果须要切面性能中办法的信息,就退出 JoinPoint
  • JoinPoint 参数的值是由框架赋予,必须是第一个地位的参数
  • 不止前置告诉的办法,能够蕴含一个 JoinPoint 类型参数,所有的告诉办法均可蕴含该参数

MyAspect.java

@Aspect

@Component("myAspect")

public class MyAspect {@Before(value = "execution(void *..doSome(..))")

    public void before(JoinPoint joinPoint){

        // 获取办法的定义

        System.out.println("办法的签名(定义):" + joinPoint.getSignature());

        System.out.println("办法的名称:" + joinPoint.getSignature().getName());

        // 获取办法的实参

        Object[] args = joinPoint.getArgs();

        for (Object arg : args) {System.out.println("参数:" + arg);

        }

        System.out.println("这是前置告诉!!!");

    }

}

测试类

    @org.junit.Test

    public void demo01(){

        String config = "applicationContext.xml";

        ApplicationContext app = new ClassPathXmlApplicationContext(config);

        Service proxy = (Service) app.getBean("myService");

        proxy.doSome("张三",20);

    }

打印后果

2.3.5 后置告诉:@AfterReturning

  • 在指标办法执行之后执行
  • 因为是指标办法之后执行,所以能够获取到指标办法的返回值
  • 该注解的 returning 属性就是用于指定接管办法返回值的变量名的
  • 所以,被注解为后置告诉的办法,除了能够蕴含 JoinPoint 参数外,还能够蕴含用于接管返回值的变量
  • 该变量最好为 Object 类型,因为指标办法的返回值可能是任何类型

业务办法

@Component("myService2")

public class SomeServiceImpl implements SomeService {

    @Override

    public void doSome() {System.out.println("这是业务办法!!!");

    }

    @Override

    public String doOther(String str, int i) {return "业务办法 doOther 的返回值!!!";}

}

后置告诉

1. 该注解的 returning 属性就是用于指定接管办法返回值的变量名的

2. 除了能够蕴含 JoinPoint 参数外,还能够蕴含用于接管返回值的变量

3. 该变量最好为 Object 类型,因为指标办法的返回值可能是任何类型

4. 办法的定义要求:

  • 公共办法 public
  • 办法没有返回值
  • 办法名称自定义
  • 办法有参数的, 举荐是 Object,参数名自定义

5.@AfterReturning: 后置告诉

  • value:切入点表达式
  • returning:自定义的变量,示意指标办法的返回值的, 自定义变量名必须和告诉办法的形参名一样
  • 能够依据业务办法的返回值做出相应的操作
@AfterReturning(value = "execution(String *..doOther(..))",returning = "res")

    public void myAfterReturning(JoinPoint joinPoint,Object res){

        // 获取办法的签属(定义)System.out.println(joinPoint.getSignature());

        // 获取办法的参数

        Object[] args = joinPoint.getArgs();

        for (Object arg : args) {System.out.println("指标办法参数:" + arg);

        }

        // 指标办法的返回值

        System.out.println(res);

        // 后置告诉

        System.out.println("后置告诉!!!");

    }

测试类

    @Test

    public void demo02(){

        String config = "applicationContext.xml";

        ApplicationContext app = new ClassPathXmlApplicationContext(config);

        SomeService proxy = (SomeService)app.getBean("myService2");

        proxy.doOther("Dick",20);

    }

2.3.6 盘绕告诉:@Around

在指标办法执行之前之后执行。被注解为盘绕加强的办法要有返回值

被注解为盘绕加强的办法要有返回值,Object 类型。并且办法能够蕴含一个 ProceedingJoinPoint 类型的参数

接口 ProceedingJoinPoint 其中有一个 proceed() 办法,用于执行指标办法

若指标办法有返回值,则该办法的返回值就是指标办法的返回值。最初,盘绕加强办法将其返回值返回。该加强办法理论是拦挡了指标办法的执行

  • 切面类

1. 盘绕告诉办法的定义格局:

  • public
  • 必须有一个返回值,举荐应用 Object
  • 办法名称自定义
  • 办法有参数,固定的参数 ProceedingJoinPoint

2. 特点

  • 在指标办法的前和后都能加强性能
  • 控制目标办法是否被调用执行
  • 批改原来的指标办法的执行后果,影响最初的调用后果
  • 它是性能最强的告诉

3. 盘绕告诉等同于 jdk 动静代理的 InvocationHandler 接口

4. 参数:ProceedingJoinPoint 就等同于 Method,用于执行指标办法

5. 返回值:就是指标办法的执行后果,能够被批改

6. 盘绕告诉:常常做事务,在指标办法之前开启事务,执行指标办法,在指标办法之后提交事务

@Around(value = "execution(String *..do(..))")

    public Object myAround(ProceedingJoinPoint p) throws Throwable {

        Object result = null;

        // 前置性能加强

        System.out.println("前置性能加强!!");

        // 等同于 method.invoke(); Object result = doFirst();

        result = p.proceed();

        // 后置性能加强

        System.out.println("后置性能加强");

        return result;

    }

测试类

    @Test

    public void demo01(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

        SomeService proxy = (SomeService)app.getBean("Service3");

        String result = proxy.doFirst("Dick", 99);

        // 输入返回后果

        System.out.println(result);

    }

2.3.7 异样告诉:@AfterThrowing

在指标办法抛出异样后执行

该注解的 throwing 属性用于指定所产生的异样类对象

被注解为异样告诉的办法能够蕴含一个参数 Throwable,参数名称为 throwing 指定的名称,示意产生的异样对象

  • 业务办法
 @Override

    public void doSecond() {System.out.println("执行业务办法 doSecond()" + (10/0));

    }

  • 切面类

1. 异样告诉办法的定义格局:

  • 拜访权限 public
  • 没有返回值
  • 办法名称自定义
  • 办法有个一个 Exception,也能够应用 JoinPoint

2. @AfterThrowing: 异样告诉

  • 属性:

value 切入点表达式

throwinng 自定义的变量,示意指标办法抛出的异样对象,变量名必须和办法的参数名一样

  • 特点:

在指标办法抛出异样时执行的

能够做异样的监控程序,监控指标办法执行时是不是有异样,如果有异样,能够发送邮件,短信进行告诉

    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))",

            throwing = "ex")

    public void myAfterThrowing(Exception ex) {System.out.println("异样告诉:办法产生异样时,执行:"+ex.getMessage());

        // 发送邮件,短信,告诉开发人员

    }

2.3.8 最终告诉:@After

无论指标办法是否抛出异样,该加强均会被执行

  • 业务办法
    @Override

    public void doThird() {System.out.println("执行业务办法 doThird()");

    }

切面类

1. 最终告诉的定义格局:

  • 拜访权限 public
  • 没有返回值
  • 办法名称自定义
  • 办法没有参数,然而能够应用 JoinPoint

2. @After : 最终告诉特点

  • 总是会执行
  • 在指标办法之后执行
       // 等同以下执行形式

       try{SomeServiceImpl.doThird(..)

       }catch(Exception e){ }finally{myAfter()

       }

    @After(value = "execution(* *..SomeServiceImpl.doThird(..))")

    public  void  myAfter(){System.out.println("执行最终告诉,总是会被执行的代码");

        // 个别做资源革除工作的。}

2.3.9 @Pointcut 定义切入点

当较多的告诉加强办法应用雷同的 execution 切入点表达式时,编写、保护均较为麻烦;AspectJ 提供了 @Pointcut 注解,用于定义 execution 切入点表达式

将 @Pointcut 注解在一个办法之上,当前所有的 execution 的 value 属性值均可应用该办法名作为切入点

代表的就是 @Pointcut 定义的切入点。这个应用 @Pointcut 注解的办法个别应用 private 的标识办法,即没有理论作用的办法

  • 切面类

1.@Pointcut: 定义和治理切入点,如果你的我的项目中有多个切入点表达式是反复的,能够复用的。

2. 特点:

当应用 @Pointcut 定义在一个办法的下面,此时这个办法的名称就是切入点表达式的别名

其它的告诉中,value 属性就能够应用这个办法名称,代替切入点表达式了

    @After(value = "mypt()")

    public  void  myAfter(){System.out.println("执行最终告诉,总是会被执行的代码");

        // 个别做资源革除工作的。}

    @Before(value = "mypt()")

    public  void  myBefore(){System.out.println("前置告诉,在指标办法之前先执行的");

    }

    @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))" )

    private void mypt(){// 无需代码,}

2.4 代理形式更换

如果指标类有接口,默认应用 jdk 动静代理,如果指标类没有接口,则应用 CGlib 动静代理

如果想让具备接口的指标类应用 CGlib 的代理形式,须要以下配置文件

<aop:aspectj-autoproxy proxy-target-class="true"/>

退出移动版