第十五讲-Spring选择代理

第十五讲-Spring选择代理

在Spring中,存在两个切面,一个是aspect,另一个是advisor,advisor相比于aspect是更细粒度的切面,切面类包含一个通知和切点。而一个切面是由通知和切点组成,

通知表现为一个方法,也就是增强的方法;切点决定了将来目标的哪些方法需要增强。一般为AspectJ表达式。对于一个切面,可以包含多个通知

例如下面编写的切面类:

@Aspect
class MyAspect{


    @Before("execution(* foo())")  // 将来任意包的任意类,只要有foo()方法调用就会前置增强
    public void before(){
        System.out.println("前置增强");
    }

    @After("execution(* foo())") // 将来任意包的任意类,只要有foo()方法调用就会后置增强	
    public void after(){
        System.out.println("后置增强");
    }
}

我们现在手动手动编写一个AOP示例:

package com.cherry.chapter1.a15;

import ch.qos.logback.classic.joran.action.LoggerAction;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

public class A15 {

    private static final Logger log = LoggerFactory.getLogger(A15.class);

    public static void main(String[] args) {
        // 1.准备好切点对象
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        // 设置切点变道时 -- 匹配foo无法的方法
        pointcut.setExpression("execution(* foo())");

        // 2.准备一个通知
        MethodInterceptor advise = new MethodInterceptor() {
            @Override   // 可以调用目标
            public Object invoke(MethodInvocation invocation) throws Throwable {
                log.debug("前置增强...");
                Object result = invocation.proceed();// 调用目标
                log.debug("后置增强...");
                return result;
            }
        };

        // 3.准备一个切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advise);

        // 4. 创建代理(使用Spring提供的代理工厂创建代理对象)
        ProxyFactory proxyFactory = new ProxyFactory();
        // 准备一个目标对象
        Target1 target1 = new Target1();
        proxyFactory.setTarget(target1);
        proxyFactory.addAdvisor(advisor);
        // 创建代理对象
        I1 proxy = (I1)proxyFactory.getProxy();
        log.debug("ProxyFactory创建出来的代理类的类型为:{}",proxy.getClass());
        proxy.foo();
        proxy.bar();
    }
}

interface I1{
    void foo();
    void bar();
}

class Target1 implements I1{
    private static final Logger log = LoggerFactory.getLogger(Target1.class);

    @Override
    public void foo() {
        log.debug("target1 foo()...");
    }

    @Override
    public void bar() {
        log.debug("tar1 bar()...");
    }
}

运行结果如下:

18:19:55.733 [main] DEBUG com.cherry.chapter1.a15.A15 - ProxyFactory创建出来的代理类的类型为:class com.cherry.chapter1.a15.Target1$$EnhancerBySpringCGLIB$$9d838066
18:19:55.750 [main] DEBUG com.cherry.chapter1.a15.A15 - 前置增强...
18:19:55.779 [main] DEBUG com.cherry.chapter1.a15.Target1 - target1 foo()...
18:19:55.779 [main] DEBUG com.cherry.chapter1.a15.A15 - 后置增强...
18:19:55.780 [main] DEBUG com.cherry.chapter1.a15.Target1 - tar1 bar()...

我们发现,成功完成了代理,并且我们还发现到:Spring内部的代理工厂创建代理对象使用的是Cglib,那么现在有一个问题:Spring在什么情况下使用JDK的动态代理呢?什么时候使用Cglib的动态代理呢?

这里给出结论,ProxyFactory首先会读取一个属性proxyTargetClass代理目标类

  • proxyTargetClass = false, 且目标类实现了接口,就会采用JDK来生成代理对象
  • proxyTargetClass = false, 但目标没有实现接口,就会采用Cglib来生成代理对象
  • proxyTargetClass = true, 不管目标有没有实现接口,总是会采用Cglib来生成代理对象

这里就有人问了:为什么上面的代理工厂明明目标类实现了接口,为什么还是使用Cglib作为代理生成代理对象呢?原因是因为我们在为ProxyFactory这是了目标类和切面类,ProxFactory并不知道目标类到底有没有实现了接口,这一点ProxFactory判断不出来,因此总是用Cglib实现,因此呢,我们可以为ProxFactory手动的设置目标类实现了哪个接口,例如下面:把目标类实现了哪个接口手动的告诉代理工厂,这样代理工厂才会使用JDK来生成代理对象

proxyFactory.setInterfaces(Target1.class.getInterfaces());
18:36:03.055 [main] DEBUG com.cherry.chapter1.a15.A15 - ProxyFactory创建出来的代理类的类型为:class com.cherry.chapter1.a15.$Proxy0
18:36:03.226 [main] DEBUG com.cherry.chapter1.a15.A15 - 前置增强...
18:36:03.226 [main] DEBUG com.cherry.chapter1.a15.Target1 - target1 foo()...
18:36:03.226 [main] DEBUG com.cherry.chapter1.a15.A15 - 后置增强...
18:36:03.227 [main] DEBUG com.cherry.chapter1.a15.Target1 - tar1 bar()...

此外,如果我们手动的将proxyTargetClass属性手动的改为true, 这样,不管目标类有没有实现接口,其ProxyFactory在创建代理对象时都会使用Cglib作为实现:

proxyFactory.setProxyTargetClass(true);
18:38:55.487 [main] DEBUG com.cherry.chapter1.a15.A15 - ProxyFactory创建出来的代理类的类型为:class com.cherry.chapter1.a15.Target1$$EnhancerBySpringCGLIB$$5d6a49f1
18:38:55.494 [main] DEBUG com.cherry.chapter1.a15.A15 - 前置增强...
18:38:55.509 [main] DEBUG com.cherry.chapter1.a15.Target1 - target1 foo()...
18:38:55.509 [main] DEBUG com.cherry.chapter1.a15.A15 - 后置增强...
18:38:55.509 [main] DEBUG com.cherry.chapter1.a15.Target1 - tar1 bar()...
posted @   LilyFlower  阅读(2)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示