Spring之AOP原理详解

本文详细解析了Spring AOP的工作原理,特别是基于JDK动态代理实现的方式。介绍了如何利用Proxy类创建代理对象,并深入探讨了Spring配置文件中定义代理Bean的方法及其实现流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接上一篇:spring框架组件分析

概念

AOP

spring用代理类包裹切面,把他们织入到Spring管理的bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。

实现原理

好,现在我们说说实现原理,其实很简单。

AOP

要了解 Spring 的 AOP 就必须先了解的动态代理的原理,因为 AOP 就是基于动态代理实现的。动态代理还要从 JDK 本身说起。

动态代理的实现原理

在 Jdk 的 java.lang.reflect 包下有个 Proxy 类,它正是构造代理类的入口。这个类的结构如下:

这里写图片描述

从上图发现绿色小锁的四个是公有方法。而第二个方法 newProxyInstance 就是创建代理对象的方法。这个方法的源码如下:

Proxy. newProxyInstance

   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

三个参数:

  • ClassLoader,用于加载代理类的 Loader 类,通常这个 Loader 和被代理的类是同一个 Loader 类。
  • Interfaces,是要被代理的那些那些接口。
  • InvocationHandler,就是用于执行除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。

下面还是看看 Proxy 如何产生代理类的过程,他构造出来的代理类到底是什么样子?

这里写图片描述

其实从上图中可以发现正在构造代理类的是在 ProxyGenerator 的 generateProxyClass 的方法中。ProxyGenerator 类在 sun.misc 包下,感兴趣的话可以看看他的源码。

假如有这样一个接口,如下:
SimpleProx

public interface SimpleProxy { 

    public void simpleMethod1(); 

    public void simpleMethod2(); 

}

生成的类结构如下:
$Proxy2 类

public class $Proxy2 extends java.lang.reflect.Proxy implements SimpleProxy{ 
    java.lang.reflect.Method m0; 
    java.lang.reflect.Method m1; 
    java.lang.reflect.Method m2; 
    java.lang.reflect.Method m3; 
    java.lang.reflect.Method m4; 

    int hashCode(); 
    boolean equals(java.lang.Object); 
    java.lang.String toString(); 
    void simpleMethod1(); 
    void simpleMethod2(); 
}

这个类中的方法里面将会是调用 InvocationHandler 的 invoke 方法,而每个方法也将对应一个属性变量,这个属性变量 m 也将传给 invoke 方法中的 Method 参数。整个代理就是这样实现的。

实现AOP

从前面代理的原理我们知道,代理的目的是调用目标方法时我们可以转而执行 InvocationHandler 类的 invoke 方法,所以如何在 InvocationHandler 上做文章就是 Spring 实现 Aop 的关键所在。

Spring 的 Aop 实现是遵守 Aop 联盟的约定。同时 Spring 又扩展了它,增加了如 Pointcut、Advisor 等一些接口使得更加灵活。

下面是 Jdk 动态代理的类图:

这里写图片描述

上图清楚的显示了 Spring 引用了 Aop Alliance 定义的接口。姑且不讨论 Spring 如何扩展 Aop Alliance,先看看 Spring 如何实现代理类的,要实现代理类在 Spring 的配置文件中通常是这样定一个 Bean 的,如下:

配置代理类 Bean

<bean id="testBeanSingleton" 
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces">
        <value>
            org.springframework.aop.framework.PrototypeTargetTests$TestBean
        </value>
    </property>
    <property name="target"><ref local="testBeanTarget"></ref> </property>
    <property name="singleton"><value>true</value></property>
    <property name="interceptorNames">
        <list>
            <value>testInterceptor</value>
            <value>testInterceptor2</value>
        </list>
    </property>
</bean>

配置上看到要设置被代理的接口,和接口的实现类也就是目标类,以及拦截器也就在执行目标方法之前被调用,这里 Spring 中定义的各种各样的拦截器,可以选择使用。

下面看看 Spring 如何完成了代理以及是如何调用拦截器的。

前面提到 Spring Aop 也是实现其自身的扩展点来完成这个特性的,从这个代理类可以看出它正是继承了 FactoryBean 的 ProxyFactoryBean,FactoryBean 之所以特别就在它可以让你自定义对象的创建方法。当然代理对象要通过 Proxy 类来动态生成。

下面是 Spring 创建的代理对象的时序图:

这里写图片描述

Spring 创建了代理对象后,当你调用目标对象上的方法时,将都会被代理到 InvocationHandler 类的 invoke 方法中执行。在这里 JdkDynamicAopProxy 类实现了 InvocationHandler 接口。

下面再看看 Spring 是如何调用拦截器的,下面是这个过程的时序图:

这里写图片描述

以上所说的都是 Jdk 动态代理,Spring 还支持一种 CGLIB 类代理,下次再去学习。


学习自《深入分析 Java Web 技术内幕》,还望各位多多指导。

### Spring IOC 和 AOP 原理解析 #### 一、Spring IOC(控制反转) IOC 是 Inversion of Control 的缩写,中文译作“控制反转”。它是一种设计模式,用于降低程序间的耦合度。在传统的程序设计中,通常由程序员手动创建对象并管理依赖关系。而在使用 IOC 后,这种职责交给了容器来完成。 - **基本概念** 在 Spring 中,IOC 容器负责实例化、配置和组装对象。这些被管理的对象被称为 Bean[^1]。Bean 的定义可以通过 XML 配置文件或者注解的方式实现。 - **工作原理** 当应用程序启动时,Spring 容器会读取配置元数据(如 XML 文件或注解),并通过反射机制动态地创建对象并将它们之间的依赖关系注入到位。这种方式使得组件之间更加松散耦合,并提高了系统的可测试性和扩展性[^2]。 - **两种主要实现方式** - **基于构造函数的依赖注入 (Constructor-based Dependency Injection)** 使用此方法时,所有的必需参数都通过构造函数传递给目标类。这有助于强制实施不可变性和线程安全性[^3]。 - **基于 Setter 方法的依赖注入 (Setter-based Dependency Injection)** 对于非必要属性,则可通过提供 setter 方法来进行设置。相比前者更为灵活,但也可能带来一些潜在风险,比如未初始化的情况发生。 ```java public class UserService { private UserRepository userRepository; @Autowired // 注解形式自动装配 public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } } ``` --- #### 二、Spring AOP(面向切面编程) AOP 即 Aspect-Oriented Programming,意为“面向切面编程”,旨在解决业务逻辑之外横切关注点的问题。例如日志记录、事务处理等功能往往贯穿多个模块,如果直接嵌套到具体功能代码里会造成重复冗余且难以维护的局面。而采用 AOP 技术就可以把这些通用需求抽取出来集中处理。 - **核心术语解释** - **连接点 (Join Point):** 应用程序运行过程中能够插入额外行为的位置称为连接点,在 Java 中一般指方法调用前/后等时机。 - **切入点 (Pointcut):** 明确指出哪些具体的连接点会被织入特定的行为规则集合即构成切入点描述语句。 - **通知 (Advice):** 实际执行的动作本身叫做通知,它可以是在某个操作之前、之后或者是围绕该操作展开的一系列指令序列。 - **切面 (Aspect):** 将上述三者结合起来形成完整的解决方案单元就叫切面。 - **实现过程概述** Spring AOP 主要是借助 JDK 动态代理 或 CGLIB 字节码生成技术达成目的。对于实现了接口的服务类,默认会选择前者;而对于那些没有父类型的纯 POJO 类型则倾向于后者。 ```java @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint){ System.out.println("Method Name : "+joinPoint.getSignature().getName()); } } ``` --- #### 总结说明 综上所述,Spring 框架利用 IOC 来简化复杂环境下的对象管理和依赖注入问题,同时引入 AOP 提供了一种优雅的方式来应对跨领域关心事项带来的挑战。两者共同作用下极大增强了现代企业级应用架构的设计质量与运维效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值