# SpringAOP

## 简介

### AOP 术语

在我们开始使用 AOP 工作之前，让我们熟悉一下 AOP 概念和术语。这些术语并不特定于 Spring，而是与 AOP 有关的。

| 项             | 描述                                                                      |
| ------------- | ----------------------------------------------------------------------- |
| Aspect        | 一个模块具有一组提供横切需求的 APIs。例如，一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面，这取决于需求。 |
| Join point    | 在你的应用程序中它代表一个点，你可以在插件 AOP 方面。你也能说，它是在实际的应用程序中，其中一个操作将使用 Spring AOP 框架。  |
| Advice        | 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。                     |
| Pointcut      | 这是一组一个或多个连接点，通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。                |
| Introduction  | 引用允许你添加新方法或属性到现有的类中。                                                    |
| Target object | 被一个或者多个方面所通知的对象，这个对象永远是一个被代理对象。也称为被通知对象。                                |
| Weaving       | Weaving 把方面连接到其它的应用程序类型或者对象上，并创建一个被通知的对象。这些可以在编译时，类加载时和运行时完成。           |

### 通知的类型

Spring 方面可以使用下面提到的五种通知工作：

| 通知      | 描述                             |
| ------- | ------------------------------ |
| 前置通知    | 在一个方法执行之前，执行通知。                |
| 后置通知    | 在一个方法执行之后，不考虑其结果，执行通知。         |
| 返回后通知   | 在一个方法执行之后，只有在方法成功完成时，才能执行通知。   |
| 抛出异常后通知 | 在一个方法执行之后，只有在方法退出抛出异常时，才能执行通知。 |
| 环绕通知    | 在建议方法调用之前和之后，执行通知。             |

## 例子

1. 定义一个注解

   ```java
   public @interface AopLogger {
   }
   ```
2. 定义切面

   ```java
   @Order(1)
   @Aspect
   @Component
   @Slf4j
   public class AopTest {
       
       @Pointcut("@annotation(com.example.springiopdemo.aop.AopLogger)")
       public void logger() {
       }

       @Before("logger()")
       public void before(JoinPoint joinPoint) {
           System.out.println("before");
       }

       @Around("logger()")
       public String aRound(ProceedingJoinPoint point) throws Throwable {
           log.info("welcome");
           log.info("调用方法:" + point.getSignature().getName());
           log.info("目标对象：" + point.getTarget());
           log.info("首个参数：" + Arrays.toString(point.getArgs()));
           point.proceed();
           log.info("success");
           return "OK";
       }
   }
   ```
3. 调用的注意事项
   1. AOP的实现有二类，如果是基于接口的，会采用动态代理，生成一个代理类，如果是基于类的，会采用CGLib生成子类，然后在子类中扩展父类中的方法。然后在代理类中对原有方法进行增强。
   2. 同类中的方法直接调用不会走加强的AOP功能，列如

      ```java
      @Service
      public class Service{

      	@AopAnnotation()
      	public viod testA(){
      	}
      	
      	public viod testB(){
      		testA();
      	}
      	
      }

      @Component
      public class Test(){
          
          @Autowired
          public Service service;
          
          public void run(){
              service.testA();
              service,testB();
          }
          
      }
      ```

      1. testA()将触发AOP，testB()调用的testA()，将不会触发AOP，因为调用的方法为this.testA(),并没有调用IOC容器中的service。
   3. **解决方法**
      * 懒加载，防止循环依赖

        ```java
        @Service
        public class Service{

            @Autowired
            @Lazy
            private Service service;
            
        	@AopAnnotation()
        	public viod testA(){
        	}
        	
        	public viod testB(){
        		service.testA();
        	}
        	
        }
        ```
      * 从spring容器中获取

        ```java
        @Service
        public class Service{

            @Autowired
            @Lazy
            private Service service;
            
            @Autowired
            ApplicationContext applicationContext;
            
        	@AopAnnotation()
        	public viod testA(){
        	}
        	
        	public viod testB(){
               applicationContext.getBean(Service .class).testA();
        	}
        	
        }
        ```
      * 从AOP上下文获取**注意：需要启动类上增加注解@EnableAspectjAutoProxy(exposeproxy=true)**；

        ```java
        @Service
        public class Service{

            @Autowired
            @Lazy
            private Service service;
            
        	@AopAnnotation()
        	public viod testA(){
        	}
        	
        	public viod testB(){
                ((Service)Aopcontext.currentProxy()).testA();
        	}
        	
        }
        ```

        **三个都行，看情况吧**
