Spring 中 AOP 的强大功能





4.00/5 (4投票s)
Spring 本身非常庞大。它有许多功能。但 AOP 是一个非常酷且强大的功能。本提示旨在帮助不使用 AOP 的 Spring 开发者。它提供了 AOP 的概览。
引言
作为优秀的开发者,我们始终遵循一个原则:不要重复编写相同的代码。但是,当涉及到不属于业务逻辑的通用过程时,例如异常处理、日志记录、安全性或类似的事务,我们最终会一次又一次地编写样板代码,或者为这类类创建对象,或者与横切对象之间存在过多的关联。再次,如果我们想批量更改这些非业务部分,那将是非常痛苦的。为了解决所有这些问题,AOP 出现在了我们的视野中。
Spring Framework 参考文档将 AOP 定义为
“面向方面编程 (AOP) 通过提供另一种思考程序结构的方式来补充面向对象编程 (OOP)。OOP 中模块化的关键单位是类,而 AOP 中模块化的单位是方面。方面使得关注点(例如跨越多个类型和对象的事务管理)得以模块化。(这类关注点在 AOP 文献中通常被称为横切关注点。)”
AOP 模块化意味着方法不会调用横切对象,而是横切类方法以一种在需要时自行调用的方式表达。我将解释它是如何工作的。我不会涉及环境设置或 AOP 的详细使用。
目标
本提示的主要目的是向以前没有使用过 AOP 的 Spring 开发者介绍 AOP。AOP 看起来很复杂,但它使用起来相当简单,并提供了非常强大的功能。本提示展示了如何通过使用一些简单的关键字来实现 AOP。
我不会深入探讨环境设置。一旦 Spring 开发者理解了 AOP 的简单性,那么设置环境将不再是一项艰巨的任务。
目录
首先,如果您想在任何现有项目中包含 AOP,我们可以从在项目中创建一个新包 (AOPFolder
) 开始。当然,首先我们需要下载 AOP jar 包,如果是 Spring MVC,我们还需要在 spring.xml 中包含配置。当我们对 AOP 有了一些了解后,我们就可以处理这些事情了。
回到我们的第一段,我谈到了 OOP 编程的基本问题。现在如果我们使用 AOP,这意味着我们将在一个文件中编写横切类。这里出现了一些问题
-
如何在 AOPFolder 内的某些文件中编写类(例如 AOPClass)在项目中的许多其他方法中被调用。
-
如果在另一个方法中调用它,那么我们可以在方法中有不同的点来调用 AOPClass 方法。这些点可以是方法的开始、中间和结束。
-
如果我想获取触发特定 AOPClass 方法的方法的信息怎么办。
-
如果我想使用触发特定 AOPClass 方法的方法的参数信息和返回值怎么办。
-
再次,我们面临一种情况,当任何方法被触发时,在该方法之前和之后都会执行一些功能。
当我处理上述答案时,我们可能会遇到其他问题。我们可以在那里看到这个问题。现在,是时候开始回答上述问题了。
第二个问题
让我从第二个问题开始
如果它在另一个方法中被调用,那么我们可以在方法中有不同的点来调用 AOPClass 方法。这些点可以是方法的开始、中间和结束。
我们为此有 Advice
。Advice
与 pointcut
表达式相关联,并在 pointcut
匹配的任何连接点(例如,执行具有特定名称的方法)处运行。现在我们不用担心这个关键字。
通知的类型
- 前置通知:在连接点之前执行的通知,但没有能力阻止执行流继续到连接点(除非它抛出异常)。
- 后置返回通知:在连接点正常完成之后执行的通知:例如,如果方法返回而没有抛出异常。
- 后置抛出通知:如果方法因抛出异常而退出,则执行的通知。
- 后置(最终)通知:无论连接点以何种方式退出(正常返回或异常返回)都将执行的通知。
- 环绕通知:围绕连接点(例如方法调用)的通知。这是最强大的通知类型。环绕通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续到连接点,还是通过返回自己的返回值或抛出异常来快捷方式执行被通知的方法。
第一个问题
如何在 AOPFolder 内的某些文件中编写类(例如 AOPClass)在项目中的许多其他方法中被调用。
在回答这个问题之前,让我告诉您关键的 @Aspect
。它用于 AOPClass
之前,以告诉编译器这是一个面向方面的类。
现在是答案
我们在 AOPClass
中定义一个成员函数,并在方法之前,我们给出一些带有其值的注解。
@Before("execution(* xyz(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature
让我解释一下上面的代码。无论代码中何处找到带有任意数量参数的 xyz()
,都会调用 private void anyOldTransfer()
。
再次,我们仔细查看 * xyz(..)
,我们正在使用表达式,这意味着我们可以将执行的参数作为许多表达式使用 * 通配符,&& 或 ||。* 表示任何字符。
让我举一个例子。
@Before("execution(* com.xyz.someapp.service.*.*(..) && * com.xyz.someapp.dao.*.*(..))”)
这意味着在 com.xyz.someapp.service
和 com.xyz.someapp.dao
包中的任何类中,任何带有任意数量参数的方法。每当这样的方法被触发时,在它之前,@Before
之后的函数将被调用。在上述情况下,是 anyOldTransfer()
。
与 @Before
类似,我们还有其他类型的通知,如 @After
、@AfterThrowing
、@AfterReturning
、@Around
。
还有一个概念叫做 Pointcut
。当表达式变得复杂并且我们必须在许多地方使用表达式时,我们使用 Pointcut
。
声明一个 pointcut
@Pointcut("execution(* com.xyz.someapp.service.*.*(..) &&
* com.xyz.someapp.dao.*.*(..))”)// the pointcut expression
private void pointcutExpression() {}// the pointcut signature
所以而不是编写
@Before("execution(* com.xyz.someapp.service.*.*(..) && * com.xyz.someapp.dao.*.*(..))”)
我们可以写
@Before(“(pointcutExpression()”)
如果我们更多地谈论 Pointcut
,那么我们有许多类型的 Pointcut
,例如 execution。execution 是在表达式中执行特定方法。同样,我们有像这样的关键字
在此目标内
第三个问题
如果我想获取触发特定 AOPClass 方法的方法的信息怎么办。
AOP 有一个名为 JointPoint
的关键字。它存储有关调用 AOPClass
方法的方法的信息。
让我给出一个示例代码
@Before(“execution(org.nand.javaexample.model.Circle.getName())”)
public void LoggingMethod(JointPoint jointPoint) {
System.out.println(jointPoint.toString());
}
jointPoint.toString()
将包含执行 (String org.nand.javaexample.model.Circle.getName()
)。
同样,jointPoint.getTarget()
将在上述代码中返回 Circle 类的对象。
第四个问题
如果我想使用触发特定 AOPClass 方法的方法的参数信息和返回值怎么办。
首先,我们处理参数信息。
代码示例
@Before(“args(name)”)
public void LoggingMethod(String name) {
System.out.println(“Argument value :” + name);
}
在上述代码中,当调用任何带有参数名 name
的方法时,loggingMethod
会被调用。
为了使用返回值,我们必须使用 @After
通知。
@AfterReturning(pointcut = “args(name)”, returning = “returnString”)
public void LoggingMethods(String name, Object returnString) {
System.out.println(“The argument value: ”+ name +”Return value” +returnString)
}
我们已经包含了 pointcut=
,并且我们在表达式中声明的任何 string
,其值都应与方法参数相同。这里是 returnString
。
同样,还有异常处理通知
@AfterReturning(pointcut = “args(name)”, throwing = “ex”)
public void LoggingMethods(String name, Exception returnString) {
System.out.println(“The argument value: ”+ name +”Exception occurred:-” +ex);
}
第五个问题
再次,我们面临一种情况,当任何方法被触发时,在该方法之前和之后都会执行一些功能。
AOP 为此提供了 @Around
通知。
让我给出一个示例代码
@Around(“execution(* get*())”)
public Object myAroundMethod(ProceedingJointPoint anyMethodUsingAround ) {
Object returnValue = null;
try {
returnValue = anyMethodUsingAround.proceed
}
catch (Throwable ex) {
System.out.println(Exception occurred : “ + ex);
}
return returnValue;
}
ProceedingJointPoint
表示调用 myAroundMethod()
的方法。同样,如果它有任何返回值,那么我们才将 myAroundMethod()
的返回类型指定为 Object
。否则,void
就可以了。在 Around
中,我们同时使用前置通知和后置通知。但除了这两种通知之外,我们还在 around
中有一个额外的功能。在 after
通知中,我们不能操纵返回值,但我们可以在 around
通知中这样做。
对于熟悉 XML 的开发者,还有一条附加信息。我们上面通过注解处理的所有配置都可以通过 XML 完成。我不会深入讨论,但让我给出一个简单的例子。它应该在 spring.xml 中。
<bean name=“loggingAspect” class “org.nand.testApplication.aspect.LoggingAspect”
<aop:config>
<aop:aspect id="loggingAspect" ref="loggingAspect">
<aop:pointcut id = “allGetters” expression = “execution(* get*())”/>
<aop:before
pointcut-ref ="allGetters method="myBeforeMethod"/>
</aop:aspect>
</aop:config>
希望我的帖子能让您对 AOP 有一些了解。学习愉快!