征服Spring AOP—— Schema

本文通过实战案例详细解析Spring AOP的各种增强方式,包括BeforeAdvice、AfterAdvice、AroundAdvice等,并介绍了如何利用Introduction增强实现类的功能。

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

自从开始使用Spring,就接触到AOP,但一直未能深入,沉淀一段时间后,开始全面整理! :D
这里针对一个接口中各个方法做为切面,通过Hello接口实现的例子来诠释Spring AOP 2.0 的特性。

[b]
相关内容:
[url=http://snowolf.iteye.com/blog/236264]征服Spring AOP—— Schema[/url]
[url=http://snowolf.iteye.com/blog/1481442]征服Spring AOP—— @AspectJ[/url]
[/b]

定义一个接口Hello,定义可触发BeforeAdvice、AfterAdvice、AroundAdvice、ThrowsAdvice、Introduction的方法。
Hello接口

public interface Hello {

/**
* 前置增强
*/
void sayHelloBefore();

/**
* 后置增强
*/
void sayHelloAfter();

/**
* 后置返回增强
*
* @return
*/
String sayHelloAfterReturning();

/**
* 环绕增强
*/
void sayHelloAround();

/**
* 介入增强
*/
void sayHelloIntroduction();

/**
* 异常抛出增强
*/
void sayHelloThrows();
}

针对Hello接口做具体实现,注意sayHelloThrows方法实现中,刻意抛出异常,用于触发ThrowsAdvice。
SayHello类,对Hello做具体实现。

public class SayHello implements Hello {
/*
* (non-Javadoc)
*
* @see org.zlex.aop.Hello#sayHelloBefore()
*/
public void sayHelloBefore() {
System.out.println("Say Hello Before!");
}

/*
* (non-Javadoc)
*
* @see org.zlex.aop.Hello#sayHelloAfter()
*/
public void sayHelloAfter() {
System.out.println("Say Hello After!");
}

/*
* (non-Javadoc)
*
* @see org.zlex.aop.Hello#sayHelloAfterRunning()
*/
@Override
public String sayHelloAfterReturning() {
System.out.println("Say Hello After Returning!");
// 返回值
return "Hello";
}

/*
* (non-Javadoc)
*
* @see org.zlex.aop.Hello#sayHelloAround()
*/
public void sayHelloAround() {
System.out.println("Say Hello Around!");
}

/*
* (non-Javadoc)
*
* @see org.zlex.aop.Hello#sayHelloAfterThrowing()
*/
@Override
public void sayHelloAfterThrowing() {
System.out.println("Say Hello Throws!");
// 强制抛出异常,触发AfterThrowingAdvice
throw new RuntimeException("Hello Excetion");
}

/*
* (non-Javadoc)
*
* @see org.zlex.aop.Hello#sayHelloIntroduction()
*/
public void sayHelloIntroduction() {
System.out.println("Say Hello Introduction!");
}

}


先来个测试代码:

public class AllTest {
private ApplicationContext app;
private Hello hello;

/**
* @throws java.lang.Exception
*/
@Before
public void before() throws Exception {
app = new ClassPathXmlApplicationContext("applicationContext.xml");
hello = (Hello) app.getBean("hello");
}

@Test
public void testBefore() {
System.out.println("=====Before Advice Begin=====");
hello.sayHelloBefore();
System.out.println("=====Before Advice End=====");
}

@Test
public void testAfter() {
System.out.println("=====After Advice Begin=====");
hello.sayHelloAfter();
System.out.println("=====After Advice End=====");
}

@Test
public void testAfterReturning() {
System.out.println("=====After Returning Advice Begin=====");
String value = hello.sayHelloAfterReturning();
// AfterReturning获得返回值,但不修改值内容!
assertEquals("Hello", value);
System.out.println("=====After Returning Advice End=====");
}

@Test
public void testAround() {
System.out.println("=====Around Advice Begin=====");
hello.sayHelloAround();
System.out.println("=====Around Advice End=====");
}

@Test
public void testAfterThrowing() {
System.out.println("=====After Throwing Advice Begin=====");
try {
hello.sayHelloAfterThrowing();
} catch (Exception e) {
assertNotNull(e);
}
System.out.println("=====After Throwing Advice End=====");
}

@Test
public final void testIntroduction() {
System.out.println("=====Introduction Begin=====");
// 由于对Hello接口进行了引入,使得实现了Hello接口的类可以具备Ok接口的功能
hello.sayHelloIntroduction();
((Ok) hello).sayOk();
System.out.println("=====Introduction End=====");
}

}


Spring配置:

<bean
id="hello"
class="org.zlex.aop.SayHello" />
<bean
id="advice"
class="org.zlex.aop.Advice" />


这里的[b]org.zlex.aop.Advice[/b]用于各个Advice的具体实现,以下是该类中各个方法的诠释。

对上述SayHello类的方法做具体Advice实现:
BeforeAdvice

/**
* Before
*
* @param joinPoint
*/
public void before(JoinPoint joinPoint) {
System.out.println("Before: " + joinPoint.getSignature().getName());
}


Spring配置:

<!-- before -->
<aop:config>
<aop:pointcut
id="beforePoint"
expression="execution(* org.zlex.aop.Hello.sayHelloBefore(..))" />
<aop:aspect
id="beforeAspect"
ref="advice"
>
<aop:before
method="before"
pointcut-ref="beforePoint" />
</aop:aspect>
</aop:config>


控制台输出:
[quote]
=====Before Advice Begin=====
Before: sayHelloBefore
Say Hello Before!
=====Before Advice End=====
[/quote]
BeforeAdvice在sayHelloBefore方法执行前调用Before类的invoke方法。

AfterAdvice

public void after(JoinPoint joinPoint) {
System.out.println("After: " + joinPoint.getSignature().getName());
}

Spring配置:

<!-- after -->
<aop:config>
<aop:pointcut
id="afterPoint"
expression="execution(* org.zlex.aop.Hello.sayHelloAfter(..))" />
<aop:aspect
id="afterAspect"
ref="advice"
>
<aop:after
method="after"
pointcut-ref="afterPoint" />
</aop:aspect>
</aop:config>


控制台输出:
[quote]
=====After Advice Begin=====
Say Hello After!
After: sayHelloAfter
=====After Advice End=====
[/quote]
BeforeAdvice和AfterAdvice在实现上没有差异,其差别只是触发时机而已。

AfterAdvice只是在目标方法执行后触发,但无法获得目标方法的返回值,对于这点可以通过AfterReturningAdvice增强实现。

AfterReturningAdvice

public void afterReturning(JoinPoint joinPoint, String retVal) {
// 返回值参数名称(retVal)必须与XML配置文件中的'returning="retVal"'保持一致
System.out.println("After: " + joinPoint.getSignature().getName());
System.out.println("Return Value: " + retVal);
}

Spring配置:

<!-- afterReturning -->
<aop:config>
<aop:pointcut
id="afterReturningPoint"
expression="execution(* org.zlex.aop.Hello.sayHelloAfterReturning(..))" />
<aop:aspect
id="afterAspect"
ref="advice"
>
<aop:after-returning
method="after"
pointcut-ref="afterReturningPoint"
returning="retVal"
/>
</aop:aspect>
</aop:config>


控制台输出:
[quote]
=====After Returning Advice Begin=====
Say Hello After Returning!
After: sayHelloAfterReturning
Return Value: Hello
=====After Returning Advice End=====
[/quote]

AroundAdvice

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around: " + joinPoint.getSignature().getName());
System.out.println("Before");
Object obj = joinPoint.proceed();
System.out.println("End");
return obj;
}


Spring配置:

<!-- around -->
<aop:config>
<aop:pointcut
id="aroundPoint"
expression="execution(* org.zlex.aop.Hello.sayHelloAround(..))" />
<aop:aspect
id="aroundAspect"
ref="advice"
>
<aop:around
method="around"
pointcut-ref="aroundPoint" />
</aop:aspect>
</aop:config>


控制台输出:
[quote]
=====Around Advice Begin=====
Around: sayHelloAround
Before
Say Hello Around!
End
=====Around Advice End=====
[/quote]
AroundAdvice是BeforeAdvice和AfterAdvice的综合体。可以,在方法触发前、后分别进行操作。

如果方法执行过程中产生异常,就需要ThrowsAdvice。
AfterThrowingAdvice

public void afterThrowing(JoinPoint joinPoint, Exception e) {
// 异常参数名称(e)必须与XML配置文件中的'throwing="e"'保持一致
System.out.println("AfterThrowing: "
+ joinPoint.getSignature().getName());
System.out.println("Exception Message: " + e.getMessage());
}


Spring配置:

<!-- afterThrowing -->
<aop:config>
<aop:pointcut
id="afterThrowingPoint"
expression="execution(* org.zlex.aop.Hello.sayHelloAfterThrowing(..))" />
<aop:aspect
id="afterThrowingAspect"
ref="advice"
>
<aop:after-throwing
method="afterThrowing"
pointcut-ref="afterThrowingPoint"
throwing="e"
/>
</aop:aspect>
</aop:config>

控制台输出:
[quote]
=====After Throwing Advice Begin=====
Say Hello Throws!
AfterThrowing: sayHelloAfterThrowing
Exception Message: Hello Exception
=====After Throwing Advice End=====
[/quote]

AfterThrowingAdvice是Spring事务处理的核心触发环节。当事务提交产生异常时,将直接触发AfterThrowingAdvice,产生数据库回滚等动作。

除了上述常规增强实现外,还可以通过IntroductionInterceptor构建一个原本不存在的实现。
Introduction

public Object introduction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out
.println("Introduction: " + joinPoint.getSignature().getName());
return joinPoint.proceed();
}


Introduction 可以对一个类的代码在不做任何修改(非入侵式),而使得该类拥有另一套方法,或者说具备另一套本领。
现在,我们就让这个实现了Hello接口的SayHello类拥有Ok接口的实现。
Ok接口,使得Hello接口的所有实现类都绑定这个接口:

public interface Ok {
void sayOk();
}

IntroductionOk用于实现Ok接口,使得Hello接口实现类都具有sayOk的本领:

public class IntroductionOk implements Ok {

@Override
public void sayOk() {
System.out.println("Ok!");
}
}


Spring配置:

<!-- introduction -->
<!-- .*+是用于包下的,不是用于接口 +定义目标为该接口的所有实现类 -->
<bean
id="ok"
class="org.zlex.aop.IntroductionOk" />
<aop:config>
<aop:aspect ref="advice">
<aop:pointcut
id="introductionPoint"
expression="execution(* org.zlex.aop.Hello.sayHelloIntroduction(..))" />
<aop:declare-parents
implement-interface="org.zlex.aop.Ok"
types-matching="org.zlex.aop.Hello+"
delegate-ref="ok" />
<aop:around
method="introduction"
pointcut-ref="introductionPoint" />
</aop:aspect>
</aop:config>



控制台输出:
[quote]
=====Introduction Advice Begin=====
Introduction: sayHelloIntroduction
Say Hello Introduction!
Ok!
=====Introduction Advice End=====
[/quote]
同样是Hello接口,在执行了sayHelloIntroduction方法时被拦截,同时输出[i]say Hello introduction![/i],此时还可以执行Ok的方法输出[i]Ok![/i]。显然,Hello的实现类多了Ok接口的本领。

关于表达式符号:
[quote].*+定义目标为包下的所有类
+定义目标为该接口的所有实现类[/quote]


Spring Beans 结构图如下:
[img]http://dl.iteye.com/upload/attachment/0066/2781/1e1a64ae-c080-3f1d-a438-78c5c6e7557e.jpg[/img]


代码详见附件! :D


[b]
相关内容:
[url=http://snowolf.iteye.com/blog/236264]征服Spring AOP—— Schema[/url]
[url=http://snowolf.iteye.com/blog/1481442]征服Spring AOP—— @AspectJ[/url]
[/b]
资源下载链接为: https://pan.quark.cn/s/67c535f75d4c 在机器人技术中,轨迹规划是实现机器人从一个位置平稳高效移动到另一个位置的核心环节。本资源提供了一套基于 MATLAB 的机器人轨迹规划程序,涵盖了关节空间和笛卡尔空间两种规划方式。MATLAB 是一种强大的数值计算与可视化工具,凭借其灵活易用的特点,常被用于机器人控制算法的开发与仿真。 关节空间轨迹规划主要关注机器人各关节角度的变化,生成从初始配置到目标配置的连续路径。其关键知识点包括: 关节变量:指机器人各关节的旋转角度或伸缩长度。 运动学逆解:通过数学方法从末端执行器的目标位置反推关节变量。 路径平滑:确保关节变量轨迹连续且无抖动,常用方法有 S 型曲线拟合、多项式插值等。 速度和加速度限制:考虑关节的实际物理限制,确保轨迹在允许的动态范围内。 碰撞避免:在规划过程中避免关节与其他物体发生碰撞。 笛卡尔空间轨迹规划直接处理机器人末端执行器在工作空间中的位置和姿态变化,涉及以下内容: 工作空间:机器人可到达的所有三维空间点的集合。 路径规划:在工作空间中找到一条从起点到终点的无碰撞路径。 障碍物表示:采用二维或三维网格、Voronoi 图、Octree 等数据结构表示工作空间中的障碍物。 轨迹生成:通过样条曲线、直线插值等方法生成平滑路径。 实时更新:在规划过程中实时检测并避开新出现的障碍物。 在 MATLAB 中实现上述规划方法,可以借助其内置函数和工具箱: 优化工具箱:用于解决运动学逆解和路径规划中的优化问题。 Simulink:可视化建模环境,适合构建和仿真复杂的控制系统。 ODE 求解器:如 ode45,用于求解机器人动力学方程和轨迹执行过程中的运动学问题。 在实际应用中,通常会结合关节空间和笛卡尔空间的规划方法。先在关节空间生成平滑轨迹,再通过运动学正解将关节轨迹转换为笛卡
Spring AOPSpring框架中的一个重要模块,它提供了面向切面编程(AOP)的支持。AOP是一种编程思想,它可以在不改变原有代码的情况下,通过在程序运行时动态地将代码“织入”到现有代码中,从而实现对原有代码的增强。 Spring AOP提供了基于注解的AOP实现,使得开发者可以通过注解的方式来定义切面、切点和通知等相关内容,从而简化了AOP的使用。 下面是一个基于注解的AOP实现的例子: 1. 定义切面类 ```java @Aspect @Component public class LogAspect { @Pointcut("@annotation(Log)") public void logPointcut() {} @Before("logPointcut()") public void beforeLog(JoinPoint joinPoint) { // 前置通知 System.out.println("执行方法:" + joinPoint.getSignature().getName()); } @AfterReturning("logPointcut()") public void afterLog(JoinPoint joinPoint) { // 后置通知 System.out.println("方法执行完成:" + joinPoint.getSignature().getName()); } @AfterThrowing(pointcut = "logPointcut()", throwing = "ex") public void afterThrowingLog(JoinPoint joinPoint, Exception ex) { // 异常通知 System.out.println("方法执行异常:" + joinPoint.getSignature().getName() + ",异常信息:" + ex.getMessage()); } } ``` 2. 定义业务逻辑类 ```java @Service public class UserService { @Log public void addUser(User user) { // 添加用户 System.out.println("添加用户:" + user.getName()); } @Log public void deleteUser(String userId) { // 删除用户 System.out.println("删除用户:" + userId); throw new RuntimeException("删除用户异常"); } } ``` 3. 在配置文件中开启AOP ```xml <aop:aspectj-autoproxy/> <context:component-scan base-package="com.example"/> ``` 在这个例子中,我们定义了一个切面类LogAspect,其中通过@Aspect注解定义了一个切面,通过@Pointcut注解定义了一个切点,通过@Before、@AfterReturning和@AfterThrowing注解分别定义了前置通知、后置通知和异常通知。 在业务逻辑类中,我们通过@Log注解标注了需要增强的方法。 最后,在配置文件中,我们通过<aop:aspectj-autoproxy/>开启了AOP功能,并通过<context:component-scan>扫描了指定包下的所有组件。 这样,当我们调用UserService中的方法时,就会触发LogAspect中定义的通知,从而实现对原有代码的增强。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值