Spring AOP原理

本文介绍了Spring框架中的AOP(面向切面编程)技术,并通过示例代码详细展示了如何使用纯POJO切面进行方法拦截,包括前置通知、后置通知、环绕通知等。

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

一、AOP(Aspect Oriented Programming)是Spring框架的核心技术之一,要理解AOP,先理解一些基本概念。

*横切关注点(Cross-cutting concerns):*在软件开发中,分布于应用中多处的相同功能,例如:日志、安全和事务管理等,它们都是贯穿在软件的各个功能模块的,但又不是各功能模块的核心功能。

*切面(Aspect):*横切关注点可以被模块化成各个不同的类,这些类就叫着切面,切面主要是由通知和切点组成。

*通知(Advice):*切面要完成的目标工作就叫着通知,它定义切面在“何时”要“做什么”。通知的“何时”又分为如下几种:

Before:在方法被调用之前调用通知

After:在方法调用之后调用通知,无论方式是否执行成功

After-returning:在方法成功执行之后调用通知

After-throwing:在方法抛出异常之后调用通知

Around:通知包裹了方法,通知在方法调用之前和之后执行自定义的行为

*连接点(JoinPoint):*应用执行过程中能够插入切面的点就叫着连接点,在Spring的AOP中主要是指一些方法。

*切点(Pointcut):*切点定义了通知在“何处”被执行,它实际上可以理解为连接点的一个子集。

*引入(Introduction):*引入允许我们向现有的类添加新方法和属性,在不改变现有类的情况下,让其具有新的行为和状态。

*织入(Weaving):*将切面应用到目标对象来创建新的代理对象的过程就叫着织入,在目标对象的生命周期里面,有多个时间点可以织入切面,如:

编译期------ 切面在目标类编译期被织入,这种方式需要特殊的编译器,如AspectJ
类加载期------ 切面在目标类家在到JVM时被织入,这种方式需要特殊的类加载器(ClassLoader)
运行期------ 切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的,使用CGLib实现

二、Spring提供了4中AOP支持:

1、基于代理的经典AOP

2、纯POJO切面

3、@AspectJ注解驱动的切面

4、注入式AspectJ切面

Spring的AOP都是对方法的拦截(前三种),如果AOP需求超过了简单的方法拦截,那就应该引入AspectJ,利用Spring的DI将Spring bean注入到AspectJ切面中(第四种)。

由于Spring的经典AOP不怎么样,这里也就略过了,下面直接上Spring AOP中的纯POJO切面。

package lucien;

public interface Performer {
	void perform();
}


package lucien;

import org.aspectj.lang.ProceedingJoinPoint;

public class Audience {
	public void takeSeats() {
		System.out.println("观众入座");
	}

	public void turnOffCellphones() {
		System.out.println("观众关闭手机");
	}

	public void applaud() {
		System.out.println("观众鼓掌");
	}

	public void demandRefund() {
		System.out.println("观众要求赔偿");
	}

	public void watchPerformance(ProceedingJoinPoint joinPoint) {
		try {
			System.out.println("观众入座2");
			System.out.println("观众关闭手机2");

			long start = System.currentTimeMillis();

			joinPoint.proceed();

			long end = System.currentTimeMillis();

			System.out.println("观众鼓掌2");
			System.out.println("表演耗时:" + (end - start) + "秒");
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


<bean id="audience" class="lucien.Audience"/><aop:config>

<!-- 定义切面 -->
<aop:aspect ref="audience">
<!-- 定义切点 -->
<aop:pointcut id="performance" expression="execution(* lucien.Performer.perform(..))"/>
<!-- 定义通知 -->
<aop:before pointcut-ref="performance" method="takeSeats"/>
<aop:before pointcut-ref="performance" method="turnOffCellphones"/>
<aop:after-returning pointcut-ref="performance" method="applaud"/>
<aop:after-throwing pointcut-ref="performance" method="demandRefund"/>
<aop:around pointcut-ref="performance" method="watchPerformance"/>
</aop:aspect>
</aop:config>
<!-- 向通知中添加参数 -->
<bean id="magician" class="lucien.Magician"/>
<bean id="thinker" class="lucien.Volunteer"/>
<aop:config><aop:aspect ref="magician">
<aop:pointcut id="thinking" expression="execution(* lucien.Thinker.thinkOfSomething(String)) and args(thoughts)"/>
<aop:before pointcut-ref="thinking" method="interceptThoughts" arg-names="thoughts"/>
</aop:aspect>
</aop:config>
<!-- AOP给现有对象引入新功能 -->
<aop:config><aop:aspect>
				<aop:declare-parents
					types-matching="lucien.Performer+"
					implement-interface="lucien.Contestant"
					default-impl="lucien.GraciousContestant"/>
			</aop:aspect></aop:config>


package lucien;

public interface Contestant {
	void receiveAward();
}


package lucien;

public class GraciousContestant implements Contestant {

	@Override
	public void receiveAward() {
		// TODO Auto-generated method stub
		System.out.println("获奖");
	}

}


package lucien;

public interface Thinker {
	void thinkOfSomething(String thoughts);
}


package lucien;

public class Volunteer implements Thinker {

	@Override
	public void thinkOfSomething(String thoughts) {
		// TODO Auto-generated method stub

	}

}


package lucien;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ApplicationContext ctx = new ClassPathXmlApplicationContext("ConfigFile.xml");
		Performer performer = (Performer) ctx.getBean("poeticDuke");
		performer.perform();
		((Contestant) performer).receiveAward();

		Thinker thinker = (Thinker) ctx.getBean("thinker");
		thinker.thinkOfSomething("心事");

		MindReader magician = (MindReader) ctx.getBean("magician");

		System.out.println("心事".equals(magician.getThoughts()));
	}

}

<think>好的,用户想了解Spring AOP原理,特别是代理模式、动态代理和静态代理。首先,我需要回顾一下代理模式的基本概念,然后区分静态代理和动态代理的不同,再联系到Spring AOP是如何应用这些技术的。 首先,代理模式的定义。根据引用[1],代理模式允许在不修改被代理对象的情况下,通过代理类进行功能增强。这里的关键是代理类分为静态和动态两种。静态代理需要手动创建代理类,而动态代理在运行时生成。用户可能想知道这两种代理在Spring AOP中的应用场景。 接下来,静态代理的细节。引用[1]提到静态代理的类在运行前已经存在,通常由程序员手动编写或工具生成。例如,假设有一个业务接口Subject和实现类RealSubject,静态代理类Proxy会实现Subject并持有RealSubject的引用,在方法调用前后添加逻辑。这种情况的缺点是每个被代理类都需要一个对应的代理类,导致代码冗余。 然后是动态代理,Spring AOP主要使用JDK动态代理和CGLIB。引用[2]指出JDK代理通过Proxy类生成代理对象,而CGLIB通过Enhancer创建。JDK动态代理要求目标类实现接口,利用InvocationHandler的invoke方法进行增强。CGLIB则通过继承目标类,重写方法来实现代理,适用于没有接口的情况。需要解释这两种方式的区别以及Spring如何选择使用哪一种。 还要结合Spring AOP的源码,如引用[2]提到的AOP源码剖析部分。Spring在运行时决定使用哪种代理方式,如果目标类实现了接口,默认使用JDK代理,否则使用CGLIB。这涉及到AOP的配置和底层机制。 用户可能还关心AOP的应用场景,比如事务管理、日志记录等,这些通过代理模式实现,无需修改原有代码。需要举例说明,比如在方法执行前后加入事务控制,动态代理如何拦截方法调用并添加额外操作。 另外,需要确保回答结构清晰,分点说明原理、代理类型、实现方式以及Spring的选择策略。同时,生成的相关问题要围绕用户可能进一步询问的点,比如性能比较、配置方法或实际应用例子。 最后,检查引用是否正确应用,如引用[1]关于代理模式的分类,引用[2]关于Spring AOP的具体实现,引用[3]关于代理模式的角色。确保每个技术点都有对应的引用支持,增加回答的可信度。</think>### Spring AOP原理与代理模式解析 #### 一、代理模式的核心作用 代理模式通过代理对象控制对真实对象的访问,在**不修改原始代码**的前提下实现功能增强。其核心角色包括: - **Subject**:定义业务接口(可以是接口或抽象类)[^3] - **RealSubject**:具体业务实现类(被代理对象) - **Proxy**:代理类,负责增强和控制对RealSubject的访问 #### 二、静态代理的实现方式 静态代理需**手动编写代理类**,代理关系在编译时确定。例如: ```java // 业务接口 interface UserService { void save(); } // 真实对象 class UserServiceImpl implements UserService { public void save() { /* 业务逻辑 */ } } // 静态代理类 class UserServiceProxy implements UserService { private UserService target; public UserServiceProxy(UserService target) { this.target = target; } public void save() { System.out.println("前置增强"); target.save(); // 调用真实对象方法 System.out.println("后置增强"); } } ``` **缺点**:每个业务类需对应一个代理类,代码冗余度高[^1]。 #### 三、动态代理的实现机制 Spring AOP主要采用两种动态代理技术: 1. **JDK动态代理**(基于接口) - 要求目标类必须实现至少一个接口 - 通过`java.lang.reflect.Proxy`生成代理对象 - 核心类`InvocationHandler`实现方法拦截: ```java class JdkProxyHandler implements InvocationHandler { private Object target; public Object getProxy(Object target) { this.target = target; return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) { System.out.println("前置增强"); Object result = method.invoke(target, args); System.out.println("后置增强"); return result; } } ``` 2. **CGLIB代理**(基于继承) - 通过生成目标类的子类实现代理 - 使用`MethodInterceptor`接口: ```java class CglibInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) { System.out.println("前置增强"); Object result = proxy.invokeSuper(obj, args); System.out.println("后置增强"); return result; } } // 通过Enhancer创建代理 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TargetClass.class); enhancer.setCallback(new CglibInterceptor()); TargetClass proxy = (TargetClass)enhancer.create(); ``` - **优势**:可代理无接口的类[^2] #### 四、Spring AOP的代理选择策略 | 条件 | 使用的代理方式 | |--------------------------|----------------| | 目标类实现了至少一个接口 | JDK动态代理 | | 目标类未实现任何接口 | CGLIB代理 | **底层实现**: - 通过`DefaultAopProxyFactory`类判断代理方式 - Spring Boot 2.x后默认强制使用CGLIB(可通过`spring.aop.proxy-target-class=true`配置) #### 五、AOP核心流程 1. 解析切面定义(@Aspect) 2. 匹配连接点(Pointcut) 3. 生成代理对象 4. 编织增强逻辑(Advice) 5. 通过责任链模式执行拦截器链 #### 六、性能对比 - **JDK动态代理**:生成速度快,执行效率稍低 - **CGLIB**:生成速度慢,执行效率高(现代JVM差距已不明显)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值