spring原理(第十天)

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 接口, 用来执行方法的匹配
 

 

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值