【微服务】深入解析spring aop原理

news2025/2/24 8:31:37

目录

一、前言

二、AOP 概述

2.1 什么是AOP

2.2 AOP中的一些概念

2.2.1 aop通知类型

2.3 AOP实现原理

2.3.1 aop中的代理实现

2.4 静态代理与动态代理

2.4.1 静态代理实现

三、 jdk动态代理与cglib代理

3.1 jdk动态代理

3.1.1 jdk动态代理模拟实现

3.2 CGLIB 代理

3.2.1 cglib代理实现

3.2.2 cglib代理源码模拟实现

3.2.3 cglib代理补充说明

四、spring aop源码探究

4.1 环境准备

4.1.1 引入aop依赖包

4.1.2 自定义aop配置类

4.1.3 测试方法

4.2 spring aop的切点表达式

4.2.1 语法结构

4.2.2 常见的切点表达式示例

4.3 spring aop源码分析过程

五、写在文末


一、前言

spring 是一个流行的 Java 企业应用程序开发框架,其中的 aop(面向切面编程)是 spring 框架中一个非常重要的概念。可以说在spring框架以及生态体系下,随处可见aop编程思想的运用,借助这一编程思想,在很多特殊的业务场景下,AOP的使用让编码变得易扩展、更优雅、更灵活,同时也能很好的解决通用的业务问题,提升开发效率。本文将详细介绍AOP的核心技术和底层实现原理。

二、AOP 概述

2.1 什么是AOP

AOP,即面向切面编程,AOP是一种编程范式,用于在不修改原始代码的情况下向现有应用程序添加新功能。这种编程方式将应用程序分成许多独立的部分,称为切面。这些切面可以在应用程序的不同位置进行编写和维护,从而提高了应用程序的可重用性和可维护性

AOP是OOP(面向对象编程)的延续,主要用于实现横切关注点,比如:日志记录、性能统计、安全控制、事务处理等方面。简单来说,针对应用程序中那些可以预见的,以及归总为公共的处理业务的场景均可以考虑使用aop的编程实现。

2.2 AOP中的一些概念

为了后续更全面深入的掌握aop的使用,有必要了解下spring aop的相关概念。这些概念能够更好的指导我们在编码中对aop的原理的理解。

切面(Aspect)

AOP核心就是切面,它将多个类的通用行为封装成可重用的模块,该模块含有一组API提供横切功能。比如,一个日志模块可以被称作日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在Spring AOP中,切面通过带有@Aspect注解的类实现。

连接点(Join Point)

哪些方法需要被AOP增强,这些方法就叫做连接点

通知(Advice)

AOP在特定的切入点上执行的增强处理

常用的通知类型有:

  • before,前置通知

  • after,后置通知

  • afterReturning,返回通知

  • afterThrowing,异常通知

  • around,环绕通知

切入点(Pointcut)

实际真正被增强的方法,称为切入点

2.2.1 aop通知类型

通知(advice)是你希望在横切关注点具体的实现方式,即方法之前触发?执行后触发...,aop中的Advice主要有以下5种类型:

  • 前置通知(Before Advice)
    • 在连接点之前执行的Advice,即方法执行前,使用@Before注解使用这个Advice
  • 返回之后通知(After Retuning Advice)
    • 在连接点正常结束之后执行的Advice,即你的方法正常执行完成之后执行,通过 @AfterReturning注解使用它,前提是你的方法没有抛出异常
  • 抛出(异常)后执行通知(After Throwing Advice)
    • 如果方法执行抛异常的时候,这个Advice就会被执行,通过 @AfterThrowing注解来使用
  • 后置通知(After Advice)
    • 无论连接点通过什么方式退出(方法正常返回或者抛出异常)都会执行在结束后执行这些Advice,通过 @After注解使用
  • 围绕通知(Around Advice)
    • 围绕连接点执行的Advice,方法执行前可以拦截参数,方法执行后可以拿到执行结果,属于几种通知中比较灵活的一种,通过@Around注解使用

2.3 AOP实现原理

Spring AOP 的实现原理是基于动态代理字节码操作的。具体来说,

  • 在编译阶段, Spring 会使用 AspectJ 编译器将切面代码编译成字节码文件;

  • 在运行阶段, Spring 会使用 Java 动态代理或 CGLIB 代理生成代理类,这些代理类会在目标对象方法执行前后插入切面代码,从而实现AOP增强的功能;

Spring AOP 主要使用两种代理方式:JDK动态代理和 CGLIB 代理。如果目标对象实现了至少一个接口,则框架使用JDK动态代理;否则,使用 CGLIB 代理。  

2.3.1 aop中的代理实现

基于接口情况下,使用JDK动态代理

JDK动态代理,创建一个接口实现类的代理对象,该接口实现类的代理对象会调用该接口的真实实现,并且在代理对象中调用真实的实现类的方法进行增强

没有接口情况下,使用 CGLIB 动态代理

CGLIB动态代理,创建一个类子类的代理对象,该子类的代理对象会去调用父类中的方法,并且在子类代理对象调用其父类方法后做增强

2.4 静态代理与动态代理

很多同学在理解aop的代理时,很容易对静态代理与动态代理这个概念搞混,顾名思义,静态代理,简单理解就是代理的类或方法定义已经提前定义好了,在需要使用的地方直接调用即可,而动态代理,关键是理解动态这个概念,从程序的角度来说,就是在程序运行过程中,动态生成了目标对象的代理对象。

2.4.1 静态代理实现

定义一个接口

public interface LoginService {
    void login(String userId);
}

接口实现

public class LoginServiceImpl implements LoginService {
    @Override
    public void login(String userId) {
        System.out.println("登录成功,userId:"+userId);
    }
}

实现了LoginService 接口的代理实现类

public class ProxyLoginService implements LoginService {

    private LoginServiceImpl loginServiceImpl;

    public ProxyLoginService(LoginServiceImpl loginServiceImpl){
        this.loginServiceImpl=loginServiceImpl;
    }

    @Override
    public void login(String userId) {
        System.out.println("执行登录之前操作");
        loginServiceImpl.login("user");
        System.out.println("执行登录方法后操作");
    }
}

测试方法

 public static void main(String[] args) {
        //创建电影院(静态代理)
        ProxyLoginService proxyLoginService = new ProxyLoginService(new LoginServiceImpl());
        proxyLoginService.login("user1");
    }

静态代理优点

  • 使得真实角色处理的业务更加纯粹,不再去关注一些公共的事情。

  • 公共的业务由代理来完成---实现业务的分工。

  • 公共业务发生扩展时变得更加集中和方便。

缺点:

  • 每一个代理类都必须实现一遍真实实现类(也就是realMovie)的接口,如果接口增加方法,则代理类也必须跟着修改;

  • 每一个代理类对应一个真实实现类(委托类),如果真实实现(委托类)非常多,则静态代理类就非常臃肿,难以胜任。

三、 jdk动态代理与cglib代理

3.1 jdk动态代理

与静态代理不同,动态代理是根据代理对象,动态创建代理类。这样就可以避免静态代理中代理类接口过多的问题。jdk动态代理是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成。

jdk动态代理的使用步骤如下:

  • 创建一个需要动态代理的接口,即Movie接口;

  • 创建一个需要动态代理接口的真实实现,即RealMovie类;

  • 创建一个动态代理处理器,实现InvocationHandler接口,并重写invoke方法去增强真实实现中的目标方法;

  • 在测试类中,生成动态代理的对象;

自定义一个类,实现InvocationHandler接口,并重写里面的invoke方法


public class ProxyInvocationHandler implements InvocationHandler {

    //需要动态代理接口的真实实现类
    private Object object;

    //通过构造方法去给需要动态代理接口的真实实现类赋值
    public ProxyInvocationHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法增强
        System.out.println("方法执行前操作");
        //object是真实实现,args是调用方法的参数
        //当代理对象调用真实实现的方法,那么这里就会将真实实现和方法参数传递过去,去调用真实实现的方法
        method.invoke(object,args);
        //方法增强
        System.out.println("方法执行后操作");
        return null;
    }
}

编写测试类,使用自定义的handler生成代理对象并调用目标方法

public class DynamicProxyTest {

    public static void main(String[] args) {
        //需要动态代理接口的真实实现
        LoginServiceImpl realMovie = new LoginServiceImpl();
        //动态代理处理类
        ProxyInvocationHandler handler = new ProxyInvocationHandler(realMovie);
        //获取动态代理对象
        //第一个参数:真实实现(被代理对象)的类加载器
        //第二个参数:真实实现类(被代理对象)它所实现的所有接口的数组
        //第三个参数:动态代理处理器
        LoginService loginService = (LoginService) Proxy.newProxyInstance(realMovie.getClass().getClassLoader(),
                realMovie.getClass().getInterfaces(),
                handler);
        loginService.login("user");
    }

}

执行结果

3.1.1 jdk动态代理模拟实现

由于jdk的代理是在运行期间动态产生字节码,所以很难看到运行期间代理产生的源码,我们可以模拟其原理进行一个简单的实现,代码如下:

定义一个接口,作为被代理的对象,实现类也一并贴出

	interface MinuService {
        int addMinu();

        void reduceMinu();
    }

    //目标对象实现类
    static class Target implements MinuService {

        @Override
        public int addMinu() {
            System.out.println("tager do addMinu");
            return 1;
        }

        @Override
        public void reduceMinu() {
            System.out.println("tager do reduceMinu");
        }
    }

定义一个handler,handler的作用可理解为作为代理对象中实际执行方法调用的一个入口

interface MyInvocationHandler {
        Object invoke(Object proxy,Method method, Object[] args);
    }

定义代理类,代理类是核心实现,在代理类中主要做的事情如下:

  • 引用自定义handler;

  • 引用代理类中待执行的目标方法(该方法其实是运行期间动态生成的);

  • 调用handler中的invoke反射调用目标方法,并得到执行结果;

import java.lang.reflect.Method;

public class MyProxy implements JdkInner.MinuService {

    private JdkInner.MyInvocationHandler handler;

    public MyProxy(JdkInner.MyInvocationHandler handler) {
        this.handler = handler;
    }

    static Method addMinu;
    static Method reduceMinu;

    static {
        try {
            addMinu = JdkInner.MinuService.class.getMethod("addMinu");
            reduceMinu = JdkInner.MinuService.class.getMethod("reduceMinu");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    @Override
    public int addMinu() {
        Object invoke = null;
        invoke = handler.invoke(this, addMinu, new Object[0]);
        return Integer.parseInt(invoke.toString());
    }

    @Override
    public void reduceMinu() {
        handler.invoke(this, reduceMinu, new Object[0]);
    }
}

编写测试类,有没有发现这个写法和上述使用jdk的动态代理很像

 public static void main(String[] args) {

        MinuService minuService = new MyProxy(new MyInvocationHandler() {
            @Override
            public Object invoke(Object proxy,Method method, Object[] args) {
                System.out.println("before  invoke method");
                Object result = null;
                try {
                    result = method.invoke(new Target(), args);
                    System.out.println("result :" + result);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                return result;
            }
        });
        System.out.println(minuService.getClass());
        minuService.addMinu();
    }

如果你像查看jdk动态代理时产生的源码,可以考虑使用arthars进行反编译查看

3.2 CGLIB 代理

CGLIB 代理是一个基于字节码操作的代理方式,它是一个强大的、高性能、高质量的 Code 生成类库,可以在运行期扩展 Java 类与实现 Java 接口,可以为没有实现接口的类创建代理对象。CGLIB 代理会在运行时生成一个目标对象的子类,并覆盖其中的方法,以实现AOP的功能。下面是 CGLIB 代理的实现代码:

public class CglibAopProxy implements AopProxy {
 
    private final AdvisedSupport advised;
 
    public CglibAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }
 
    @Override
    public Object getProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(advised.getTargetSource().getTargetClass());
        enhancer.setCallback(new DynamicAdvisedInterceptor(advised));
        return enhancer.create();
    }
 
    private static class DynamicAdvisedInterceptor implements MethodInterceptor {
 
        private final AdvisedSupport advised;
 
        public DynamicAdvisedInterceptor(AdvisedSupport advised) {
            this.advised = advised;
        }
 
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            MethodInvocation methodInvocation = new CglibMethodInvocation(
                    advised.getTargetSource().getTarget(),
                    method,
                    args,
                    proxy,
                    advised.getMethodInterceptor(),
                    advised.getTargetSource().getTargetClass()
            );
            return methodInvocation.proceed();
        }
    }
}

3.2.1 cglib代理实现

下面是一段使用cglib进行代理的代码

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibTest {

    static class Target {
        public void get() {
            System.out.println("target get");
        }
    }

    public static void main(String[] args) {
        Target target = new Target();
        //拿到代理的类
        Target proxyTarget = (Target)Enhancer.create(Target.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object proxyClass, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before....");
                Object result = method.invoke(target, args);
                System.out.println("after...");
                return result;
            }
        });
        //执行代理类的方法
        proxyTarget.get();
    }

}

结果看起来和使用jdk代理类似

与jdk代理不同的是,jdk的代理仅能针对接口代理,而cglib生成的代理对象是一个子类,所以需要注意,使用cglib进行代理的时候,父类不能是final的,并且目标类中的代理方法也不能是final的。

3.2.2 cglib代理源码模拟实现

上面的案例了解了如何使用cglib进行代理以及代码的实现,下面来模拟一下其底层源码的实现

定义一个目标类

public class CglibTarget {

    public void add(){
        System.out.println("add()");
    }

    public void add(int num){
        System.out.println("add() :" + num);
    }

    public void reduce(int count){
        System.out.println("reduce() :" + count);
    }
}

定义代理类

通过上面的介绍了解到,cglib的代理对象是通过生成目标对象的子类实现的,所以代理类需要继承目标类

import org.springframework.cglib.proxy.MethodInterceptor;

import java.lang.reflect.Method;

public class CglibProxy extends CglibTarget {

    private MethodInterceptor methodInterceptor;

    public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
        this.methodInterceptor = methodInterceptor;
    }

    static Method add_1;
    static Method add_2;

    static {
        try {
            add_1 = CglibTarget.class.getMethod("add");
            add_2 = CglibTarget.class.getMethod("add",int.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void add() {
        try {
            methodInterceptor.intercept(this,add_1,new Object[0],null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    public void add(int num){
        try {
            methodInterceptor.intercept(this,add_2,new Object[]{num},null);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    public void reduce(int count){
        super.reduce(count);
    }

}

测类类

import com.congge.aop.cglib.CglibProxy;
import com.congge.aop.cglib.CglibTarget;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibTest {

    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        CglibTarget target = new CglibTarget();
        proxy.setMethodInterceptor(new MethodInterceptor() {
            @Override
            public Object intercept(Object targetObj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before handle");
                Object result = method.invoke(target, args);
                System.out.println("after handle");
                return result;
            }
        });
        proxy.add(10);
    }
}

运行代码,得到如下效果

 

3.2.3 cglib代理补充说明

在使用cglib代码编码实现中,注意到在intercept方法参数中有一个Method的参数,这个参数是做什么用的呢?

不妨将代码修改成下面这样

public static void main(String[] args) {
        Target target = new Target();
        //拿到代理的类
        Target proxyTarget = (Target)Enhancer.create(Target.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object proxyClass, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before....");
                //Object result = method.invoke(target, args);
                Object result = methodProxy.invoke(target, args);
                System.out.println("after...");
                return result;
            }
        });
        //执行代理类的方法
        proxyTarget.get();
    }

再次执行,发现仍然可以得到正确的结果,为什么会这样呢?

cglib代理底层通过这种方式为调用者提供了多一种选择,当选择使用methodProxy的invoke方法时,将不反射,而是退化为直接使用原始目标对象去调用方法,某些情况下,可以获得比反射更高的性能。

四、spring aop源码探究

spring底层在aop的代理上是怎么处理的呢,接下来让我们通过源码一探究竟。

4.1 环境准备

4.1.1 引入aop依赖包

只需要引入aspectjweaver即可

		<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.3</version>
        </dependency>

4.1.2 自定义aop配置类

定义一个切面的配置类,这里扫描的是某个包路径下的所有方法,并且使用了环绕通知,在通知方法里,输出了方法实际执行耗时

@Component
@Aspect
public class AspectConfig {

    @Pointcut("execution(* com.congge.service.aop.*.*(..)))")
    private void pointcut(){

    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long start = System.currentTimeMillis();
        Object proceed = point.proceed();
        long end = System.currentTimeMillis();
        System.out.println("消耗时间:" + (end-start));
        return proceed;
    }

}

4.1.3 测试方法

编写测试方法,验证上述aop切面通知是否生效

@ComponentScan("com.congge.service.aop")
@EnableAspectJAutoProxy
public class SpringApp {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringApp.class);
        UserService userService = context.getBean(UserService.class);
        userService.getUser();
    }

}

运行之后效果如下,说明aop的环绕通知配置生效了

4.2 spring aop的切点表达式

在实际开发中,aop编程的一般步骤是,定义切面类,然后定义具体的切点表达式,然后再在具体的通知方法上面引入切点表达式即可。下面列举几种常用的几种自定义切点表达式的写法。

4.2.1 语法结构

例1:对扫描包中的某个类的方法增强

execution(* com.xxx.xxxClass.方法名(..))

例2:对扫描包中的某个类的所有方法增强

execution(* com.xxx.xxxClass.* (..))

例3:对扫描包中所有类,类中所有方法增强

execution(* com.xxx.service.*.* (..))

例4:所有带有某个注解的方法或类

@annotation(com.xxx.annotation.xxxAnnotation)

上面举的例子都是去切入具体的某个方法、类,也可以切到某个包下所有的方法,也可以去切某包下带有某注解的方法等等。

4.2.2 常见的切点表达式示例

下面列举常用的几种切点表达式示例,便于后续的参考

所有方法执行

execution(public * *(..))

名以"XXX"开头的所有方法执行

execution(* XXX*(..))

XXX接口中所有方法执行

execution(* com.xxx.XXX.*(..))

service包下所有方法执行

execution(* com.xxx.service..*.*(..))

service包下的所有连接点

(仅在Spring AOP中执行方法)

within(com.xxx.service..*)

代理实现XXX接口的任何连接点

(仅在Spring AOP中执行方法)

this(com.xxx.service.XXXService)

所有带有@XXX注解的方法或类

@annotation(com.xxx.annotation.XXX)

4.3 spring aop源码分析过程

通过断点,在真正执行userService.getUser()之前,我们可以看到,userService已经是一个代理对象,而且不难看出,这是一个jdk动态代理产生的对象(userService接口存在实现类)

于是可以断定,在真正执行目标方法的时候,是代理对象在帮我们执行了,那么代理对象在哪里产生的呢?代理对象是什么时候产生的呢?这就是接下来需要搞清的重点所在。

从getBean入手,一路往下走,通过getBean方法,最终获取到了代理对象

public <T> T getBean(Class<T> requiredType) throws BeansException {
        this.assertBeanFactoryActive();
        return this.getBeanFactory().getBean(requiredType);
    }

通过getBean方法走到下面这个doGetBean方法里,在这个方法中,注意有一个关键的位置:getSingleton,很多同学在看spring源码的时候发现调用栈非常深,经常陷入一种不知道从何看起的状态,这里说过小技巧,你只需要关注你期望得到的目标即可。比如在doGetBean这里,为了快速获取到userService,可以在此处设置条件断点,如下:

以上述getSingleton为例来说,通过这个方法,可能会得到代理对象,需不需要进去看呢,可以先走一步,如果得到的是代理对象,那么说明getSingleton这个方法中是产生代理对象的地方,按照这个思路,我们走一步看看,发现此刻得到的对象就是一个代理对象,说明确实是在getSingleton这个方法中产生了代理对象;

以getSingleton继续深入,进入该方法之后发现,从singletonObjects中拿到的就是代理对象了,这里不禁冒出一个问题来了,singletonObjects中是什么时候将代理对象放进去的呢?

这里不得不说另一个源码的调试和阅读技巧,就是直接看debug中的调用栈,从下面的调用栈可以发现,上一步singletonObjects中存储的这个代理对象就是在其中的某一步产生并放到容器中的;

这样一来,至少可以说明,产生代理对象的时机在当前这个getSingleton之前,从spring的bean的生命周期来看,要经历解析,创建,初始化,实例化等一些步骤,所以可以将目光定位到创建的过程,即createBean阶段,于是从getBean方法入手作为突破点即可,再次断点进入,来到getBean方法中

通过getBean一路来到getSingleton方法中,该方法即创建初始的userService的核心代码,而且第一次进入的时候发现userService的bean对象还未创建出来,一直来到该方法的如下位置,利用上面的调试技巧发现就是在这个singletonFactory.getObject()方法得到了代理对象

继续进入这个singletonFactory.getObject()方法,就来到创建bean的方法中,即createBean

沿着该方法继续往下走,当走到doCreateBean这里时,发现这个方法执行结束后就成了代理对象

继续进入doCreateBean方法中,当走完initializeBean方法之后,发现就产生了代理对象

那么initializeBean里面发生了什么呢?可以断定这个方法的某个地方最终产生了代理对象,于是来到下面这个方法

进入到applyBeanPostProcessorsAfterInitialization方法中

来到postProcessAfterInitialization里面之后,沿着wrapIfNecessary继续进入

历经千辛万苦,最终来到这里,这个方法就是最终产生代理对象的地方,核心创建代理对象的代码就是下面这段

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            } else {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;
            }
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    }

我们重点关注方法中createProxy这个方法即可

跳过中间的步骤,最终来到createAopProxy方法中,在这个方法中,展示了如何创建代理对象,以及采用哪种方式创建代理对象,即使用jdk动态代理呢?还是cglib方式呢?

从这段代码不难看出,这里做了一个判断,如果目标对象是接口,将采用jdk动态代理,如果目标对象是类,则使用cglib的代理,由于在前面的代码中目标对象是普通的类,所以将会产生一个cglib的代理对象

补充说明:

在上面判断使用哪种代理方式时,有一个很重要的参数,即判断下面的这个参数的布尔值,通过源码点击进去发现,默认情况下该参数初始值为false

config.isProxyTargetClass()

该参数的意义在于,开发者可以通过强制指定这个参数的值,从而改变代理的方式强制使用cglib,如何修改呢,只需要将下面的注解中改为true即可

五、写在文末

本文通过较大的篇幅详细结束了AOP的原理,并通过debug的方式完整走了一遍底层的源码,AOP不仅是spring体系中的一个重要思想,同时在日常的开发中也有较多的应用,希望对看到的同学有用,本篇到此结束,感谢观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2304298.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pikachu靶场搭建教程

需要的东西 phpStudy&#xff1a; 链接&#xff1a; https://pan.baidu.com/s/1fJ-5TNtdDZGUf5FhTm245g 提取码&#xff1a;0278 pikachu-master&#xff1a; Github链接&#xff1a;https://github.com/zhuifengshaonianhanlu/pikachu 链接&#xff1a; https://pan.baidu.c…

游戏引擎学习第119天

仓库:https://gitee.com/mrxiao_com/2d_game_3 上一集回顾和今天的议程 如果你们还记得昨天的进展&#xff0c;我们刚刚完成了优化工作&#xff0c;目标是让某个程序能够尽可能快速地运行。我觉得现在可以说它已经快速运行了。虽然可能还没有达到最快的速度&#xff0c;但我们…

从零开始制作一个漂亮的悬浮按钮

0.1血版 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head> &l…

Win11更新系统c盘爆满处理

1.打开磁盘管理 2.右击c盘选择属性&#xff0c;进行磁盘管理&#xff0c;选择详细信息。 3.选择以前安装的文件删除即可释放c盘空间。

从零实现机器人自主避障

1. 编译工具安装 sudo apt update sudo apt install python3-catkin-pkg python3-rosdep python3-rosinstall-generator python3-wstool python3-rosinstall build-essential sudo rosdep init rosdep update2. 构建节点 mkdir -p ~/ros2_ws/src cd ~/ros2_ws ros2 pkg creat…

【Nacos】从零开始启动Nacos服务(windows/linux)

文章目录 前言前置条件官方网址一、Nacos下载1.1 选择Nacos版本1.2 下载 二、解压2.1 解压到某个文件夹 三、 启动3.1 方式一&#xff1a;直接使用命令启动3.1.1 进入bin文件夹3.1.2 进入命令行工具3.1.3 执行命令 3.2 方式二&#xff1a;修改配置文件后启动3.2.1 修改启动脚本…

即插即用Transformer、扩散模型、机器人规划、长文本检索增强生成 | Big Model Weekly 第57期...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 01 ProTransformer: Robustify Transformers via Plug-and-Play Paradigm 近年来&#xff0c;基于Transformer的架构在机器学习的各个领域占据了主导地位。本文介绍了一种新颖的鲁棒性注意力机制&#xff0c;旨…

FPGA DSP:Vivado 中带有 DDS 的 FIR 滤波器

本文使用 DDS 生成三个信号&#xff0c;并在 Vivado 中实现低通滤波器。低通滤波器将滤除相关信号。 介绍 用DDS生成三个信号&#xff0c;并在Vivado中实现低通滤波器。低通滤波器将滤除较快的信号。 本文分为几个主要部分&#xff1a; 信号生成&#xff1a;展示如何使用DDS&am…

DeepSeek各模型现有版本对比分析

文章目录 一、基础模型系列&#xff1a;V1 到 V3 的演进二、专用模型系列&#xff1a;推理与多模态三、版本选型与商业化趋势 DeepSeek作为最近特别火爆的模型&#xff0c;本文将对DeepSeek现有的主要版本进行对比分析,涵盖参数规模、训练数据、功能改进、应用场景和性能表现等…

ASP.NET Core Clean Architecture

文章目录 项目地址一、项目主体1. CQRS1.1 Repository数据库接口1.2 GetEventDetail 完整的Query流程1.3 创建CreateEventCommand并使用validation 2. EFcore层2.1 BaseRepository2.2 CategoryRepository2.3 OrderRepository 3. Email/Excel导出3.1 Email1. Email接口层 4. 定义…

紫光同创开发板使用教程(二):sbit文件下载

sbit文件相当于zynq里面的bit文件&#xff0c;紫光的fpga工程编译完成后会自动生成sbit文件&#xff0c;因工程编译比较简单&#xff0c;这里不在讲解工程编译&#xff0c;所以我这里直接下载sbit文件。 1.工程编译完成后&#xff0c;可以看到Flow列表里面没有报错&#xff0c…

1. 自定义组件基础

相关资源&#xff1a; &#x1f4ce;day10 图片素材.zip 1. 自定义组件基础 概念&#xff1a;在ArkUI中由框架直接提供的称为系统组件 -> Column&#xff0c;Button等&#xff0c;由开发者定义的称为自定义组件 作用&#xff1a;自定义组件可以对 UI和业务逻辑进行封装&…

MySQL MHA 部署全攻略:从零搭建高可用数据库架构

文章目录 1.MHA介绍2.MHA组件介绍3.集群规划4.服务器初始化5.MySQL集群部署5.1 安装MySQL集群5.2 配置一主两从5.3 测试MySQL主从5.4 赋予MHA用户连接权限 6.安装MHA环境6.1 安装MHA Node6.2 安装MHA Manager 7.配置MHA环境8.MySQL MHA高可用集群测试8.1 通过VIP连接MySQL8.2模…

Spring Boot3+Vue2极速整合:10分钟搭建DeepSeek AI对话系统

前言 在生成式AI技术蓬勃发展的今天&#xff0c;大语言模型已成为企业智能化转型和个人效率提升的核心驱动力。作为国产大模型的优秀代表&#xff0c;DeepSeek凭借其卓越的中文语义理解能力和开发者友好的API生态&#xff0c;正在成为构建本土化AI应用的首选平台。 本文将以S…

浅谈 Redis 主从复制原理(二)

大家好&#xff0c;我是此林。 【浅谈 Redis 主从集群原理&#xff08;一&#xff09; 】 上一篇文章中&#xff0c;说到了 Redis 主从复制的全量同步和增量同步&#xff0c;repl_baklog 复制缓冲区&#xff0c;以及 slave 挂掉之后数据同步的措施。 下面介绍的上一篇遗留问…

elf_loader:一个使用Rust编写的ELF加载器

本文介绍一个使用Rust实现的ELF加载器。 下面是elf_loader的仓库链接&#xff1a; github&#xff1a; https://github.com/weizhiao/elf_loaderhttps://github.com/weizhiao/elf_loader crates.io&#xff1a; https://crates.io/crates/elf_loaderhttps://crates.io/cra…

连接Sql Server时报错无法通过使用安全套接字层加密与 SQL Server 建立安全连接

文章目录 一. 前言二. 解决方案 方案1方案2 三. 总结 一. 前言 在《数据库原理》这门课的实验上&#xff0c;需要使用SQL Server&#xff0c;然后使用jdbc连接sql server突然报错为&#xff1a;SQLServerException: “Encrypt”属性设置为“true”且 “trustServerCertific…

Qt常用控件之日历QCalendarWidget

日历QCalendarWidget QCalendarWidget 是一个日历控件。 QCalendarWidget属性 属性说明selectDate当前选中日期。minimumDate最小日期。maximumDate最大日期。firstDayOfWeek设置每周的第一天是周几&#xff08;影响日历的第一列是周几&#xff09;。gridVisible是否显示日历…

超级详细Spring AI运用Ollama大模型

大模型工具Ollama 官网:https://ollama.com/ Ollama是一个用于部署和运行各种开源大模型的工具; 它能够帮助用户快速在本地运行各种大模型&#xff0c;极大地简化了大模型在本地运行的过程。用户通过执行几条命令就能在本地运行开源大模型&#xff0c;如Lama 2等; 综上&#x…

0083.基于springboot+uni-app的社区车位租赁系统小程序+论文

一、系统说明 基于springbootuni-app的社区车位租赁系统小程序,系统功能齐全, 代码简洁易懂&#xff0c;适合小白学编程。 现如今&#xff0c;信息种类变得越来越多&#xff0c;信息的容量也变得越来越大&#xff0c;这就是信息时代的标志。近些年&#xff0c;计算机科学发展…