jdk 和 cglib 在 Spring 中的统一
Spring 中对切点、通知、切面的抽象如下
-  切点:接口 Pointcut,典型实现 AspectJExpressionPointcut 
-  通知:典型接口为 MethodInterceptor 代表环绕通知 
-  切面:Advisor,包含一个 Advice 通知,PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut 
两个切面概念
             aspect =
                 通知1(advice) +  切点1(pointcut)
                 通知2(advice) +  切点2(pointcut)
                 通知3(advice) +  切点3(pointcut)
                 ...
             advisor = 更细粒度的切面,包含一个通知和切点
模拟advisor底层实现
1.引入依赖
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>2.备好类(其中target1实现了I1接口)
 interface I1 {
        void foo();
        void bar();
    }
    static class Target1 implements I1 {
        public void foo() {
            System.out.println("target1 foo");
        }
        public void bar() {
            System.out.println("target1 bar");
        }
    }
    static class Target2 {
        public void foo() {
            System.out.println("target2 foo");
        }
        public void bar() {
            System.out.println("target2 bar");
        }
    }
3.创建切面
       //创建切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        //创建通知
        MethodInterceptor methodInterceptor = invocation -> {
            System.out.println("before.....");
            Object result = invocation.proceed();
            System.out.println("after.....");
            return result;
        };
        //创建切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, methodInterceptor);
        //创建代理
        Target1 target = new Target1();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisor(advisor);
        factory.setInterfaces(target.getClass().getInterfaces());
        factory.setProxyTargetClass(false);
        I1 proxy = (I1) factory.getProxy();
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();
解释:
首先我们需要准备切点用pointcut子接口来创建,创建好了要设置他的切点表达式,用来指定,
其次我们需要创建通知,利用methodintercepter创建,他与之前我们创建代理是的名称一样,但包不一样,这个通知实际上是一个环绕通知。
准备好前两个了,我们就需要开始准备切面, 利用DefaultPointcutAdvisor创建该切面
最后创建代理对象用来增强,我们使用一个代理工厂来创建代理,他会根据条件自动选择代理模式
先创建出目标类对象,把切面添加到工厂等等,获取代理对象,最后调用方法

可以看到代理工厂选择了jdk代理,
创建代理有三种情况:
                a. proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
                 b. proxyTargetClass = false,  目标没有实现接口, 用 cglib 实现
                 c. proxyTargetClass = true, 总是使用 cglib 实现
我们演示第二种,只需把目标类改成target2 ,以及生成的代理对象也改成target2

可以看到代理对象选择了cglib实现
-  AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现 
-  AopProxy 通过 getProxy 创建代理对象 
-  图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得) 
-  调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor 
学到了什么
                 a. Spring 的代理选择规则
                 b. 底层的切点实现
                 c. 底层的通知实现
                 d. ProxyFactory 是用来创建代理的核心实现, 用 AopProxyFactory 选择具体代理实现
                     - JdkDynamicAopProxy
                     - ObjenesisCglibAopProxy
切点匹配
1.准备类(在某些方法和类上添加注解,模拟切点如何匹配注解)
  static class T1 {
        @Transactional
        public void foo() {
        }
        public void bar() {
        }
    }
    @Transactional
    static class T2 {
        public void foo() {
        }
    }
    @Transactional
    interface I3 {
        void foo();
    }
    static class T3 implements I3 {
        public void foo() {
        }
    }
2.通过AspectJExpressionPointcut创建切点,并设置切点表达,一开始我们设置成根据全类名
3.利用matches方法判断是否匹配成功
代码如下:
        AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
        pt1.setExpression("execution(* bar())");
        System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));可以看到bar方法匹配成功
 
 
根据注解来设置切点表达,判断是否匹配
        AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
        pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
        System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));可以看到添加了注解的foo方法成功匹配
 
 
自定义切点匹配
利用StaticMethodMatcherPointcut来创建切点,重写里面的方法实现自定匹配规则(MergedAnnotations)
        StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                // 检查方法上是否加了 Transactional 注解
                MergedAnnotations annotations = MergedAnnotations.from(method);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                // 查看类上是否加了 Transactional 注解
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                return false;
            }
        };
        System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
        System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
        System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));通过设置MergedAnnotations.SearchStrategy.TYPE_HIERARCHY,可以实现实现的接口上添加了注解也能匹配
 
 
可以看到只有t1类中bar方法没有添加注解,所以没有匹配成功
学到了什么
                 a. 底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法
                 b. 比较关键的是它实现了 MethodMatcher 接口, 用来执行方法的匹配
  



















