美文网首页
AOP(Aspect Oriented Programming)

AOP(Aspect Oriented Programming)

作者: 码而优则仕 | 来源:发表于2020-07-07 20:17 被阅读0次

关注点分离原则 Concern Separation

不同的问题交给不同的部分去解决,每部分专注解决自己的问题

  • Aspect Oriented Programming 就是其中一种关注点分离的技术

  • 通用化功能的代码实现即切面 Aspect

  • Aspect 之于 AOP,就相当于Class 之于 OOP,Bean之于Spring


    AOP与OOP关系图.png
  • 切面Aspect:将横切关注点逻辑进行模块化封装的实体对象

  • 通知 Advice:好比Class里面的方法,还定义了织入逻辑的时机

  • 连接点JoinPoint:允许使用 Advice 的地方

  • Spring AOP 默认只支持方法级别的 JoinPoint

  • Pointcut:定义一系列规则对 JoinPoint 进行筛选

  • 目标对象Target:符合Pointcut条件,要被织入横切逻辑的对象

Advice 的种类

  • BeforeAdvice:在JoinPoint 前被执行的 Advice

    虽然 BeforeAdvice 在JointPoint 前 被执行,但是 其不能阻止 JointPoint 的执行,除非在 BeforeAdvice 中显式的抛出异常。才能阻止被代理方法的执行。不能在没有发生异常的时候 人为的决定是否继续执行 JointPoint 中的代码

  • AfterAdvice:好比 try..catch..finaly 里面的 finaly 不管被代理方法是否正常执行完毕,这个 AfterAdvice 都会被执行

  • AfterReturningAdvice:在 JointPoint 执行流程正常返回后被执行。

    如果被代理方法抛出异常,这个 AfterReturningAdvice就不会被执行

  • AfterThrowingAdvice:在 JointPoint 执行过程中抛出异常才会触发。

  • AroundAdvice:在 JointPoint 前后都执行,最常用的 Advice

单个Aspect的执行顺序

1 AroundAdvice的前置执行方法

2 BeforeAdvice

—上面是入操作

3 被代理方法(JoinPoint逻辑)

—下面是出操作

4 AroundAdvice的后置执行方法

5 AfterAdvice

6 正常结束 AfterReturningAdvice,异常结束 AfterThrowingAdvice


单个Aspect的Advice执行顺序.png

入操作执行完成后,才会执行出操作,中间是 JointPoint逻辑,多个Aspect 通过指定执行顺序,但是大逻辑还是入操作执行完成后才能执行出操作。

多个Aspect的执行顺序

多个Aspect的Advice执行顺序.png

示例代码如下:

public interface HiService {

   void sayHi();

   String justWantToSayHi();
}
public interface HelloService {

   void sayHello();

   void justWantToThrowException();

}
@Service
public class HiServiceImpl implements HiService {
   @Override
   public void sayHi() {
      try {
         Thread.sleep(2000);
      } catch (Exception e) {
         e.printStackTrace();
      }
      System.out.println("Hi everyOne");
   }

   @Override
   public String justWantToSayHi() {
      return "justWantToSayHi";
   }
}
@Service
public class HelloServiceImpl implements HelloService {
   @Override
   public void sayHello() {
      try {
         Thread.sleep(2000);
      } catch (Exception e) {
         e.printStackTrace();
      }
      System.out.println("Hello everyBody");
   }

   @Override
   public void justWantToThrowException() {
      throw new RuntimeException("hello exception");
   }
}
@Controller
public class HiController {

   @Autowired
   private HiService hiService;

   public void handleRequest() {
      hiService.sayHi();
      hiService.justWantToSayHi();
   }
}
@Controller
public class HelloController {

   @Autowired
   private HelloService helloService;

   public void handleRequest(){
      helloService.sayHello();
      helloService.justWantToThrowException();
   }
}
@Aspect
@Component
public class ServiceAspect {

   /**
    * 告诉 Spring AOP 往 哪里织入 aspect 里面的逻辑
    * * cn.com.yuns.service..*.*(..)
    * 第一个 * 表示 任何返回值类型的方法(任意返回值)都会触发  cn.com.yuns.service 基础包名
    * .. 基础包名下的以及子包下面的,* 所有的类;.* 类里面的所有方法
    * (..)不管方法传入多少参数或没有参数都是被织入的对象
    */
   @Pointcut("execution(* cn.com.yuns.service..*.*(..))")
   public void embed() {
   }

   /**
    * Advice
    * Service 包里的类的任何一个方法 执行之前,嵌入相关逻辑
    */
   @Before("embed()")
   public void before(JoinPoint joinPoint) {
      System.out.println("开始调用:" + joinPoint);
   }

   /**
    * Advice
    * Service 包里的类的任何一个方法 执行之后,嵌入相关逻辑
    */
   @After("embed()")
   public void after(JoinPoint joinPoint) {
      System.out.println("开始调用:" + joinPoint);
   }

   /**
    * Advice
    * Service 包里的类的任何一个方法 执行前后,嵌入相关逻辑
    */
   @Around("embed()")
   public Object around(JoinPoint joinPoint) throws Throwable {
      Long startTime = System.currentTimeMillis();
      Object returnValue = null;
      System.out.println("开始记时:" + joinPoint);
      returnValue = ((ProceedingJoinPoint) joinPoint).proceed();
      System.out.println("执行成功 结束记时");
      Long endTime = System.currentTimeMillis();
      System.out.println("耗时:" + (endTime - startTime) + "ms");
      return returnValue;

   }

   /**
    * Advice
    *
    * @param joinPoint
    * @param returnValue
    */
   @AfterReturning(pointcut = "embed()", returning = "returnValue")
   public void afterReturning(JoinPoint joinPoint, Object returnValue) {
      System.out.println("无论是空还是有值都返回 " + joinPoint + ",返回值[" + returnValue + "]");
   }

   /**
    * Advice
    *
    * @param joinPoint
    * @param exception
    */
   @AfterThrowing(pointcut = "embed()", throwing = "exception")
   public void afterThrowing(JoinPoint joinPoint, Throwable exception) {
      System.out.println("抛出异常通知 " + joinPoint + " " + exception.getMessage());
   }
/**
 * 注解引入
 *
 * @param args
 */
public static void main(String args[]) {
   System.out.println("Spring 项目成功编译!!!!走注解使用Spring");
   ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Entrance.class);
   System.out.println("轮到 AOP 登场了!!!!");
   HiController hiController = (HiController) applicationContext.getBean("hiController");
   HelloController helloController = (HelloController) applicationContext.getBean("helloController");
   helloController.handleRequest();
   hiController.handleRequest();

}

输出:
轮到 AOP 登场了!!!!
开始记时:execution(void cn.com.yuns.service.HelloService.sayHello())
开始调用:execution(void cn.com.yuns.service.HelloService.sayHello())
Hello everyBody
执行成功 结束记时
耗时:2003ms
开始调用:execution(void cn.com.yuns.service.HelloService.sayHello())
无论是空还是有值都返回 execution(void cn.com.yuns.service.HelloService.sayHello()),返回值[null]
开始记时:execution(void cn.com.yuns.service.HelloService.justWantToThrowException())
开始调用:execution(void cn.com.yuns.service.HelloService.justWantToThrowException())
开始调用:execution(void cn.com.yuns.service.HelloService.justWantToThrowException())
抛出异常通知 execution(void cn.com.yuns.service.HelloService.justWantToThrowException()) hello exception
Exception in thread "main" java.lang.RuntimeException: hello exception
at cn.com.yuns.service.impl.HelloServiceImpl.justWantToThrowException(HelloServiceImpl.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
at cn.com.yuns.aspect.ServiceAspect.around(ServiceAspect.java:56)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy37.justWantToThrowException(Unknown Source)
at cn.com.yuns.controller.HelloController.handleRequest(HelloController.java:19)
at cn.com.yuns.demo.Entrance.main(Entrance.java:67)

Task :springdemo:Entrance.main() FAILED

补充:

Introduction-引入型Advice

  • 为目标类引入新接口,而不需要目标类做任何实现。

  • 使得目标类在使用的过程中转型成新接口对象,调用新的接口方法。

示例代码:

/**
 * 包下的所有类
 * 让包下的所有类 变身为 LittleUniverse
 */
@DeclareParents(value = "cn.com.yuns.controller..*",defaultImpl =cn.com.yuns.Introduction.LittleUniverseImpl.class)
public LittleUniverse littleUniverse;
/**
 * 注解引入
 *
 * @param args
 */
public static void main(String args[]) {
   System.out.println("Spring 项目成功编译!!!!走注解使用Spring");
   ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Entrance.class);
   System.out.println("轮到 AOP 登场了!!!!");
   HiController hiController = (HiController) applicationContext.getBean("hiController");
   LittleUniverse littleUniverse = (LittleUniverse) hiController;
   littleUniverse.burningup();
}

输出:

轮到 AOP 登场了!!!!
燃烧吧!!!!!

BUILD SUCCESSFUL in 23s

AOP需要OOP理解自己的语义,所以并不像单独使用那么灵活

织入:将Aspect 的横切关注点集成到OOP中

织入器:完成织入过程的执行者,(AspectJ 就有一个ajc编译器,完成AspectJ 对程序的织入,Spring AOP则会使用一组类来作为织入器来完成最终的织入操作)

Spring默认只支持方法级别的连接点,

如果有需要将成员变量,构造函数作为 JoinPoint,Spring不支持的功能的话,可以使用AspectJ来满足

相关文章

网友评论

      本文标题:AOP(Aspect Oriented Programming)

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