aop的实现原理
AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能
除此以外,aspectj 提供了两种另外的 AOP 底层实现:
-  第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中 
-  第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能 
-  作为对比,之前学习的代理是运行时生成新的字节码 
简单比较的话:
-  aspectj 在编译和加载时,修改目标字节码,性能较高 
-  aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强 
-  但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行 
ajc 编译器实现增强
编写类,并在其中准备要增强的方法
@Service
public class MyService {
    private static final Logger log = LoggerFactory.getLogger(MyService.class);
    public static void foo() {
        log.debug("foo()");
    }
}编写切面类
@Aspect // ⬅️注意此切面并未被 Spring 管理
public class MyAspect {
    private static final Logger log = LoggerFactory.getLogger(MyAspect.class);
    @Before("execution(* com.sky.service.MyService.foo())")
    public void before() {
        log.debug("before()");
    }
}在pom文件中添加插件
<plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.14.0</version>
                <configuration>
                    <complianceLevel>1.8</complianceLevel>
                    <source>8</source>
                    <target>8</target>
                    <showWeaveInfo>true</showWeaveInfo>
                    <verbose>true</verbose>
                    <Xlint>ignore</Xlint>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <!-- use this goal to weave all your main classes -->
                            <goal>compile</goal>
                            <!-- use this goal to weave all your test classes -->
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
此时通过这个插件能在编译时能修改 class 文件实现增强,并没有使用代理对象
编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强
注意
-  目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16 
-  一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器 
学到了什么
             1. aop 的原理并非代理一种, 编译器也能玩出花样 
agent 类加载实现增强
修改要被增强的类(基于代理实现的话并不会增强目标方法调用的方法)
@Service
public class MyService {
    private static final Logger log = LoggerFactory.getLogger(MyService.class);
    final public void foo() {
        log.debug("foo()");
        this.bar();
    }
    public void bar() {
        log.debug("bar()");
    }
}  注意几点
     1. 目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
     2. 运行时需要在 VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
         把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址
编写启动类
@SpringBootApplication
public class Spring09Application {
    private static final Logger log = LoggerFactory.getLogger(A10.class);
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Spring09Application.class, args);
        MyService service = context.getBean(MyService.class);
        // ⬇️MyService 并非代理, 但 foo 方法也被增强了, 做增强的 java agent, 在加载类时, 修改了 class 字节码
        log.debug("service class: {}", service.getClass());
        service.foo();
        context.close();
    }
}此时agent类加载器会在类加载期间修改class的字节码,增强了方法
 学到了什么
             1. aop 的原理并非代理一种, agent 也能, 只要字节码变了, 行为就变了
  
AOP 实现之 proxy
jdk 动态代理
jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系
编写代码
public class JdkProxyDemo {
    interface Foo {
        void foo();
    }
    static class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }
    }
    public static void main(String[] param) {
                // 目标对象
        Target target = new Target();
        ClassLoader loader = JdkProxyDemo.class.getClassLoader(); // 用来加载在运行期间动态生成的字节码
        Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, (p, method, args) -> {
            System.out.println("before...");
            // 目标.方法(参数)
            // 方法.invoke(目标, 参数);
            Object result = method.invoke(target, args);
            System.out.println("after....");
                    return result;
                });
        // 调用代理
        proxy.foo();
    }
}在运行期间修改字节码文件,可以发现基于jdk实现代理增强功能实现了

cglib 代理
-  cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系 
-  限制⛔:根据上述分析 final 类无法被 cglib 增强 
此时需要引入aop起步依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>编写代码
public class CglibProxyDemo {
    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }
    public static void main(String[] param) {
        // 目标对象
        Target target = new Target();
        // 代理对象
        Target proxy = (Target) Enhancer.create(Target.class,
                (MethodInterceptor) (p, method, args, methodProxy) -> {
            System.out.println("before...");
//            Object result = method.invoke(target, args); // 用方法反射调用目标
            // methodProxy 它可以避免反射调用
//            Object result = methodProxy.invoke(target, args); // 内部没有用反射, 需要目标 (spring)
            Object result = methodProxy.invokeSuper(p, args); // 内部没有用反射, 需要代理
            System.out.println("after...");
                    return result;
                });
        // 调用代理
        proxy.foo();
    }
}运行结果和jdk代理一样
注: methodProxy.invoke和methodProxy.invokeSuper不是基于反射原理实现的



















