AOP(Aspect Oriented Programming,面向切面编程)是OOPs(面向对象编程)的补充
可以做: 事务管理、权限、日志、安全等。
AOP让你可以使用简单可插拔的配置,在实际逻辑执行之前、之后或周围动态添加横切关注点。这让代码在当下和将来都变得易于维护

方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象

连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
JoinPoint: 程序在执行流程中经过的一个个时间点,这个时间点可以是方法调用时,
或者是执行方法中异常抛出时,也可以是属性被修改时等时机,
在这些时间点上你的切面代码是可以(注意是可以但未必)被注入的

Pointcut:
JoinPoints 只是切面代码可以被织入的地方,但我并不想对所有的 JoinPoint 进行织入,
这就需要某些条件来筛选出那些需要被织入的 JoinPoint,
Pointcut 就是通过一组规则(使用 AspectJ pointcut expression language 来描述)
来定位到匹配的 joinpoint

通知(Advice):
在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、
“before”和“throws”通知,切面代码真正被执行的地方,主要有五个织入时机
Advice: 代码织入(也叫增强),Pointcut 通过其规则指定了哪些 joinpoint 可以被织入,
而 Advice 则指定了这些 joinpoint 被织入(或者增强)的具体时机与逻辑,是切面代码真正被执行的地方,
主要有五个织入时机
1 Before Advice: 在 JoinPoints 执行前织入
2 After Advice: 在 JoinPoints 执行后织入(不管是否抛出异常都会织入)
3 After returning advice: 在 JoinPoints 执行正常退出后织入(抛出异常则不会被织入)
4 After throwing advice: 方法执行过程中抛出异常后织入
5 Around Advice: 这是所有 Advice 中最强大的,它在 JoinPoints 前后都可织入切面代码,
可以选择是否执行原有正常的逻辑,如果不执行原有流程,它甚至可以用自己的返回值代替原有的返回值,
甚至抛出异常

列子:

@Aspect
@Component
public class TestAdvice {
   // 1. 定义 PointCut
   @Pointcut("execution(* com.example.demo.api.TestServiceImpl.eatCarrot())")
   private void eatCarrot(){}

   // 2. 定义应用于 JoinPoint 中所有满足 PointCut 条件的 advice, 这里我们使用 around advice,在其中织入增强逻辑
   @Around("eatCarrot()")
   public void handlerRpcResult(ProceedingJoinPoint point) throws Throwable {
       System.out.println("吃萝卜前洗手");
       //  原来的 TestServiceImpl.eatCarrot 逻辑,可视情况决定是否执行
       point.proceed();
       System.out.println("吃萝后买单");
   }
}

说到 PointCut 的 AspectJ pointcut expression language 声明式表达式,
这个表达式支持的类型比较全面,可以用正则,注解等来指定满足条件的 joinpoint ,
比如类名后加 .*(..) 这样的正则表达式就代表这个类里面的所有方法都会被织入,
使用 @annotation 的方式也可以指定对标有这类注解的方法织入代码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GlobalErrorCatch {

}

//下面这个方法需要加上try catch(){} 可以通过注解加进去 对代码无侵入

public class TestServiceImpl implements TestService {
   @Override
   @GlobalErrorCatch
   public ServiceResultTO<Boolean> test() {
        // 此处写服务里的执行逻辑
        boolean result = xxx;
        return ServiceResultTO.buildSuccess(result);
   }
}
//只要配置了AOP 的pointcut
@Aspect
@Component
public class TestAdvice {

   // 1. 定义所有带有 GlobalErrorCatch 的注解的方法为 Pointcut  (这个注解是自定义的哈)
   @Pointcut("@annotation(com.example.demo.annotation.GlobalErrorCatch)")
   private void globalCatch(){}

   // 2. 将 around advice 作用于 globalCatch(){} 此 PointCut
   @Around("globalCatch()")
   public Object handlerGlobalResult(ProceedingJoinPoint point) throws Throwable {
       try {
           return point.proceed(); //业务代码执行的语句 可以在前后加上东西 如try catch(){}
       } catch (Exception e) {
           System.out.println("执行错误" + e);
           return ServiceResultTO.buildFailed("系统错误");
       }
   }

}

// AOP原理

@SpringBootApplication
@EnableAspectJ***
public class DemoApplication {
   public static void main(String[] args) {
       ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
       TestService testService = context.getBean(TestService.class);
 // 这个 bean 的 class 居然不是 TestServiceImpl!而是com.example.demo.impl.TestServiceImplEnhancerBySpringCGLIB$$705c68c7!
       System.out.println("testService = " + testService.getClass());
   }
}

静态代理动态代理区别
Java 源代码经过编译生成字节码,然后再由 JVM 经过类加载,连接,初始化成 Java 类型,
可以看到字节码是关键,静态和动态的区别就在于字节码生成的时机

静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。在编译时已经将接口,
被代理类(委托类),代理类等确定下来,在程序运行前代理类的.class文件就已经存在了

动态代理:在程序运行后通过反射创建生成字节码再由 JVM 加载而成

JDK静态代理实现:
缺点 代码量多 多个实例需要多个代理对象

    public interface Subject {
       public void request();
    }
    public class RealSubject implements Subject {
       @Override
       public void request() {
           // 卖房
           System.out.println("卖房");
       }
    }
 public class Proxy implements Subject {

       private RealSubject realSubject;

       public Proxy(RealSubject subject) {
           this.realSubject = subject;
       }
       @Override
       public void request() {
           // 执行代理逻辑
           System.out.println("卖房前");

           // 执行目标对象方法
           realSubject.request();

           // 执行代理逻辑
           System.out.println("卖房后");
 }

   public static void main(String[] args) {
       // 被代理对象
       RealSubject subject = new RealSubject();

       // 代理
       Proxy proxy = new Proxy(subject);

       // 代理请求
       proxy.request();
   }
}

动态代理(JDK)

// 委托类
public class RealSubject implements Subject {
   @Override
   public void request() {
       // 卖房
       System.out.println("卖房");
   }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

   private Object target;// 维护一个目标对象

   public ProxyFactory(Object target) {
       this.target = target;
   }

   // 为目标对象生成代理对象
   public Object getProxyInstance() {
       return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
               new InvocationHandler() {

                   //在 invoke() 方法里我们可以加入任何需要增强的逻辑 主要是根据委托类的接口等通过反射生成的
                   @Override
                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       System.out.println("计算开始时间");
                       // 执行目标对象方法
                       method.invoke(target, args);
                       System.out.println("计算结束时间");
                       return null;
                   }
               });
   }

   public static void main(String[] args) {
       RealSubject realSubject = new RealSubject();
       System.out.println(realSubject.getClass());
       Subject subject = (Subject) new ProxyFactory(realSubject).getProxyInstance();
       System.out.println(subject.getClass());
       subject.request();
   }
   打印结果如下:
```shell
原始类:class com.example.demo.proxy.staticproxy.RealSubject
代理类:class com.sun.proxy.$Proxy0
计算开始时间
卖房
计算结束时间

InvocationHandler:
        委托对象所有接口方法调用都会转发到 InvocationHandler.invoke(),
        在 invoke() 方法里我们可以加入任何需要增强的逻辑 主要是根据委托类的接口等通过反射生成的
}

CGLib动态代理实现
CGlib 动态代理也提供了类似的 Enhance 类,增强逻辑写在 MethodInterceptor.intercept() 中,
也就是说所有委托类的非 final 方法都会被方法拦截器拦截,在说它的原理之前首先来看看它怎么用的

//定义
public class MyMethodInterceptor implements MethodInterceptor {
   @Override
   public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
       //System.out.println("目标类增强前!!!");

       //注意这里的方法调用,不是用反射哦!!!
       Object object = proxy.invokeSuper(obj, args);

       //System.out.println("目标类增强后!!!");
       return object;
   }
}
// 使用
public class CGlibProxy {
   public static void main(String[] args) {
       //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
       Enhancer enhancer = new Enhancer();

       //设置目标类的字节码文件
       enhancer.setSuperclass(RealSubject.class);

       //设置回调函数
       enhancer.setCallback(new MyMethodInterceptor());

       //这里的creat方法就是正式创建代理类
       RealSubject proxyDog = (RealSubject) enhancer.create();

       //调用代理类的eat方法
       proxyDog.request();
   }
}
代理类:class com.example.demo.proxy.staticproxy.RealSubject$$EnhancerByCGLIB$$889898c5
目标类增强前!!!
卖房
目标类增强后!!!

可以看到它并不要求委托类实现任何接口,而且 CGLIB 是高效的代码生成包,
底层依靠 ASM(开源的 java 字节码编辑类库)操作字节码实现的,性能比 JDK 强,
所以 Spring AOP 最终使用了 CGlib 来生成动态代理