AOP详解

作者: 学编程的小屁孩 | 来源:发表于2020-02-01 14:39 被阅读0次

1、什么是AOP?

AOP(Aspect Oriented Programming):面向切面编程,降低业务逻辑各部分之间的耦合度,提高程序可重用性,提高开发效率。

2、AOP能干什么?

日志记录、性能统计、性能调优、安全控制、权限管理、事务处理、异常处理、资源池管理、缓存处理、同步持久化等。

3、我太难了!看了没懂!

好吧!以记录日志为例。

原始时代:在做日志处理的时候,在每个方法中添加日志处理。

没工具自己搞

石器时代:为了代码复用,把日志处理抽离成一个新的方法。仍然手动插入这些方法。

有工具也污染

牛逼时代:通过动态代理,将一些横向的功能抽离成一个独立的模块,在指定位置插入这些功能。在指定位置执行对应流程。这样的思想,亦即AOP。

存绿色无污染

4、这么牛逼怎么玩耍的?

一言不合丢代码。。。。。。

POM需要依赖

Spring需要引入,SpringBoot不需要

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
    <version>${spring.version}</version>
  </dependency>
  • Spring使用XML装配方式

  1. 首先定义注解。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckVersion {
}
  1. 保存订单业务逻辑,添加注解。
public class OrderService {
    // 使用CheckVersion注解,saveOrder是切点。
    @CheckVersion
    public OrderVO saveOrder(OrderVO order) {
      ...
    }
}
  1. AOP增强方法。
/**
 * 版本号检查增强方法
 */
public class CheckVersionAdvice {

    @Autowired
    private OrderDAO orderDAO;

    /**
     * 保存之前做订单版本校验,不一致不能保存
     * @param param
     */
    public void checkVersion(OrderVO param) {
        if (StringUtils.isBlank(param.getOrderId())) {
            return;
        }
        Optional<Order> optional = orderDAO.findById(param.getOrderId());
        Order order = optional.get();
        if (!order.getVersion().equals(param.getVersion())) {
            throw new BusinessException(getConstantValue("不是最新订单!"));
        }
    }
}
  1. spring-aop.xml可以参照如下:
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--可以使用注解自动装配-->
    <aop:aspectj-autoproxy/>

    <!--如下XML配置AOP,如果类中使用@Aspect如下配置可以省略 -->
    <bean id="checkVersionAdvice" class="com.xyz.demo.CheckVersionAdvice"/>

    <aop:config>
      <!--AOP通知增强方法-->
      <aop:aspect id="versionCheck" ref="checkVersionAdvice" order="1">
        <!--配置AOP切点及增强类型-->
        <aop:before method="checkVersion"
            pointcut="execution(* com.xyz.demo..service..*Service.*(..)) 
              and args(param) and @annotation(com.xyz.demo.CheckVersion)" />
      </aop:aspect>
    </aop:config>

</beans>
  • SpringBoot 用注解自动装配

  1. 测试类:TestController
@RestController
public class TestController {
  @RequestMapping("/{msg}")
  public String hello(@PathVariable("msg") String message) {
     System.out.println(message);
     return message;
  }
}

启动SpringBoot,在浏览器执行http://localhost:8080/Hello,SpringBoot!
返回结果:

Hello,SpringBoot!
  1. 切面类:LogAspect
@Component 
@Aspect  
public class LogAspect {  
  @Pointcut("execution(* com.example.demo.controller..*(..))")  
  private void pointCut() { }    

  @Before("pointCut()")    
  public void before(JoinPoint joinPoint) {  
    String className = joinPoint.getTarget().getClass().getName();   
    String methodName = joinPoint.getSignature().getName();     
    System.out.println(className + "." + methodName + "() 开始!");   
  }  

 @After("pointCut()") 
 public void after(JoinPoint joinPoint) {  
   String className = joinPoint.getTarget().getClass().getName();  
   String methodName = joinPoint.getSignature().getName();  
   System.out.println(className + "." + methodName + "() 结束!");  
  }
}

加人AOP输出结果:

com.example.demo.controller.TestController.hello() 开始!
Hello,SpringBoot!
com.example.demo.controller.TestController.hello() 结束!

哇塞!不用配置XML喽!我的Controller里一点日志代码也没加耶!!!

5、这些注解都是什么鬼?

让我们先了解下边这些名词:


  • TargetObject目标对象
    指被切入的对象。
    例子中,TestController是目标对象。
    例子中,LogAspect就是切面。
  • JoinPoint连接点
    程序执行中的某个点、某个位置。
    例子中,hello()是连接点。
  • PointCut切点
    切面匹配连接点的点,与切点表达式相关,切面如何切点。
    例子中,@PointCut注解就是切点表达式,匹配对应的连接点。
  • Aspect切面
    一个关注点的模块。
  • Advice通知
    指在切面的某个特定的连接点上执行的动作。
    例子中,before()与after()方法中的代码。
  • Weave织入
    将Advice作用在JoinPoint的过程。

看着有点蒙圈,这么名词直接有啥关系呢?

连接点切点通知 AOP详细图解

6、怎么才能玩的溜?

要想玩的溜,继续往下看!

Aop核心类大致分为三类:
  • advisorCreator:默认情况下只使用一种代理机制,主要用来扫描获取advisor。
  • advisor顾问:切面的体现形式,封装AOP切点和通知。LogAspect中的@Pointcut、@before和@after就是Advisor。
  • advice通知:AOP中增强的方法。LogAspect中before()和after()方法是通知。
切面提供 5 种通知类型:

Before:在方法调用之前调用通知。
After:在方法完成之后调用通知,无论方法执行成功与否。
After-returning:在方法执行成功之后调用通知。
After-throwing:在方法抛出异常后进行通知。
Around:包裹了被通知方法,在方法调用之前和调用之后执行自定义的行为。


切点表达式分类:
指示器 描述
arg () 限制连接点的指定参数为指定类型的执行方法
@args () 限制连接点匹配参数由指定注解标注的执行方法
execution() 用于匹配连接点的执行方法
this () 限制连接点匹配 AOP 代理的 Bean 引用为指定类型的类
target() 限制连接点匹配特定执行对象,这些对象对应类要具备指定类型注解
within() 限制连接点匹配指定类型
@within() 限制连接点匹配指定注释所标注的类型
@annotation 限制匹配带有指定注释的连接点
让你牛掰让你飞
execution指示器简介
指示器结构
  1. 开头* 号标识了我们不关心的方法返回值的类型。
  2. 后我们指定了类名和方法名。
  3. (..)标识切点选择任意的 play( ) 方法,无论入参是什么。
execution指示器进阶
复杂应用场景

假设仅匹配 com.Springinaction.springidol 包。
可以使用 within()注意 && 是将 execution()和 within()连接起来,形成的 and 关系。同理也可以使用 || 或关系、!非关系。

7、我要飞得更高

获取参数的值和方法名称

AspectJ使用JoinPoint接口表示目标类连接点对象,如果是环绕增强时,使用ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口。
我们先来了解一下这两个接口的主要方法:

  1. JoinPoint
  • Object[] getArgs():获取连接点方法运行时的入参列表;
  • Signature getSignature() :获取连接点的方法签名对象;
  • Object getTarget() :获取连接点所在的目标对象;
  • Object getThis() :获取代理对象本身;
  1. ProceedingJoinPoint
    继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法:
  • Object proceed() throws Throwable:通过反射执行目标对象的连接点处的方法;
  • Object proceed(Object[] args) throws Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。
切点表达式
execute表达式
  • 拦截任意公共方法
    execution(public * *(..))
  • 拦截以set开头的任意方法
    execution(* set*(..))
  • 拦截类或者接口中的方法
    execution(* com.xyz.service.AccountService.*(..))
    拦截AccountService(类、接口)中定义的所有方法
  • 拦截包中定义的方法,不包含子包中的方法
    execution(* com.xyz.service..(..))
    拦截com.xyz.service包中所有类中任意方法,不包含子包中的类
  • 拦截包或者子包中定义的方法
    execution(* com.xyz.service...(..))
    拦截com.xyz.service包或者子包中定义的所有方法
within表达式
  • 拦截包中任意方法,不包含子包中的方法
    within(com.xyz.service.*)
    拦截service包中任意类的任意方法
  • 拦截包或者子包中定义的方法
    within(com.xyz.service..*)

拦截service包及子包中任意类的任意方法

this表达式

代理对象为指定的类型会被拦截
this(com.xyz.service.AccountService)
被spring代理之后生成的对象必须为ServiceImpl才会被拦截。

target表达式

目标对象为指定的类型被拦截
target(com.xyz.service.AccountService)
目标对象为AccountService类型的会被代理。

args 表达式

匹配方法中的参数

  • 匹配只有一个参数,且类型UserModel
    @Pointcut("args(com.xyz.demo.UserModel)")
  • 匹配多个参数
    args(type1,type2,typeN)
  • 匹配任意多个参数
    @Pointcut("args(com.xyz.demo.UserModel,..)")
    匹配第一个参数类型为UserModel的所有方法, .. 表示任意个参数。
@target表达式

匹配的目标对象的类有一个指定的注解
@target(com.xyz.demo.Annotation1)
目标对象中包含Annotation1注解,调用该目标对象的任意方法都会被拦截。

@within表达式

指定匹配必须包含某个注解的类里的所有连接点
@within(com.xyz.demo.Annotation1)
声明有Annotation1注解的类中的所有方法都会被拦截。

@annotation表达式

匹配有指定注解的方法
@annotation(com.xyz.demo.Annotation1)
被调用的方法包含指定的注解。

@args表达式

方法参数所属的类型上有指定的注解,被匹配
注意:是方法参数所属的类型上有指定的注解,不是方法参数中有注解

8、修仙秘籍

Spring AOP的流程
  1. Spring加载自动代理器AnnotationAwareAspectJAutoProxyCreator,当作一个系统组件。spring aop 开启注解方式之后,该类会扫描所有@Aspect()标准的类,生成对应的adviosr。目前SpringBoot框架中默认支持的方式,自动配置。
  2. 当一个bean加载到Spring中时,会触发自动代理器中的bean后置处理
  3. bean后置处理,会先扫描bean中所有的Advisor
  4. 然后用这些Adviosr和其他参数构建ProxyFactory
  5. ProxyFactory会根据配置和目标对象的类型寻找代理的方式(JDK动态代理或CGLIG代理)
  6. 然后代理出来的对象放回context中,完成Spring AOP代理。
AOP源码分析

Spring AOP 实现机制

相关文章

  • Spring Boot AOP 拦截Get请求

    前置内容:请查看参考文章关于Spring AOP 的讲解 细说Spring——AOP详解(AOP概览) 补充说明S...

  • Spring AOP详解及简单实现

    Spring AOP详解及简单实现# AOP 说明:本文转载至 http://www.cnblogs.com/xr...

  • 03 AOP学习之五种通知

    Spring AOP五种通知详解 spring aop通知(advice)分成五类: 前置通知Before adv...

  • java-spring-2

    一 学习大纲 1. 动态代理设计模式(JDK和cglib) 2. AOP详解 3. AOP中几种...

  • 向您图文并茂生动讲解Spring AOP 源码(1)

    前言 在Spring AOP - 注解方式使用介绍(长文详解)中,作者介绍了Spring AOP 注解方式的使用方...

  • 细说Spring——AOP详解(使用CGLIB实现AOP)

    一、动态代理实现AOP的缺陷 在上一篇文章细说Spring——AOP详解(动态代理实现AOP)中讲解了如何使用动态...

  • 2018-10-08

    时间总是很快,来深5.5月,到10.16就是6个月,半年啊,好快。 aop注解详解 aop注解11

  • Spring AOP详解

    Spring AOP详解 一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了...

  • AOP详解

    1、什么是AOP? AOP(Aspect Oriented Programming):面向切面编程,降低业务逻辑各...

  • 深入理解spring AOP

    Spring AOP详解 一.前言在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一...

网友评论

      本文标题:AOP详解

      本文链接:https://www.haomeiwen.com/subject/zpfxxhtx.html