第三章:面向切面编程

面向切面编程(AOP)在提供模块化的意义上补充了 。但模块化的关键单元是切面而不是类。

AOP将程序逻辑分解为不同的部分(称为 )。它通过切点来增加模块化。

切点是能够影响整个应用程序的关注点,并且应该尽可能集中在代码中的一个位置,例如事务管理、身份验证、日志记录、安全性等

为什么使用AOP ?

它提供了可插入的方法,可以在实际逻辑之前,之后或周围动态添加其他关注点。假设一个类中有10个方法,如下所示:

有5种方法从m开始,2种从n开始,3种从p开始。了解场景,必须维护日志并在调用从m开始的方法后发送通知。如果没有AOP,我们可以从以m开头的方法调用方法(维护日志并发送通知)。在这种情况下,我们需要在所有5个方法中编写代码。

但是,如果客户说以后我不需要发送通知,你需要改变所有的方法。它导致了维护问题。AOP的解决方案我们不需要从方法中调用方法。现在,我们可以在类的方法中定义额外的关注点,比如维护日志、发送通知等。它的entry在xml文件中给出 ; 将来,如果客户机说要删除通知程序功能,我们只需要在xml文件中更改。因此,在AOP中维护很容易。

什么地方用 AOP ?

AOP主要用于以下情况:

提供声明式企业服务,如声明式事务管理。

它允许用户实现自定义方面。

AOP概念和术语

AOP概念和术语如下:

Join point

Advice

Pointcut

Introduction

Target Object

Aspect

Interceptor

AOP Proxy

Weaving

1)连接点(Joinpoint)

程序执行的某个特定位置:如类开始 、类初始化后、 某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。

2)切点(Pointcut)

每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“ ”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在Spring中,切点通过 接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。

通知表示方面在特定连接点上采取的行动。有不同类型的通知:

Before Advice:它在连接点之前执行。

After Returning Advice:节点正常完成后执行。

After Throwing Advice:如果方法退出,则通过抛出异常来执行。

After (finally) Advice:连接点之后执行,而不管连接点退出是正常返回还是异常返回。

Around Advice:它在连接点之前和之后执行。

3)增强(Advice)

增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。

4)目标对象(Target)

增强逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。

5)引介(Introduction)

引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

6)织入(Weaving)

织入是将增强添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、增强或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:

a、编译期织入,这要求使用特殊的Java编译器。

b、类装载期织入,这要求使用特殊的类装载器。

c、动态代理织入,在运行期为目标类添加增强生成子类的方式。

Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

7)代理(Proxy)

一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。

8)切面(Aspect)

切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。

Spring AOP的实现

Spring AOP实现由以下提供:

AspectJ

Spring AOP

JBoss AOP

Spring AOP

以下三种方法可以使用Spring AOP。但是广泛使用的方法是 注释样式。下面给出了使用spring AOP的三种方法:

由Spring1.2旧样式(基于dtd)(在Spring3中也支持)

由AspectJ注释风格

通过Spring XML配置样式(基于模式)

Spring AOP示例

以下是Spring1.2旧式AOP(基于dtd)实现的示例。虽然在Spring3也支持,但是advice将spring aop与aspectJ一起使用,

spring1.2旧式aop实现支持4种类型的advice。

Before Advice,在实际方法调用之前执行。

After Advice,在实际的方法调用之后执行。如果方法有返回值,则在返回值之后执行

Around Advice,在实际方法调用之前和之后执行。

Throws Advice,如果实际方法抛出异常,则执行 3在advice之后,它在实际方法调用之后执行。如果method返回一个值,则在返回值后执行。 在advice周围,它在实际方法调用之前和之后执行。 抛出advice如果实际方法抛出异常则执行它。

让我们通过下面给出的图表来理解advice 层次结构:

1)MethodBeforeAdvice示例

创建一个包含实际业务逻辑的类。

文件:A.java

现在,创建实现 接口的advisor类。

文件:BeforeAdvisor.java

在xml文件中,创建3个bean,一个用于A类,第二个用于 类,第三个用于 类。

文件:applicationContext.xml

ProxyFactoryBean类:

该ProxyFactoryBean的类是由Spring Famework提供。它包含2个属性target和 。A类的实例将被视为目标对象,而advisor类的实例将被视为拦截器。您需要将advisor对象作为列表对象传递,如上面给出的xml文件中所示。

类的编写如下:

文件:Test.java

Output:

在MethodBeforeAdvice中打印其他信息

我们可以打印其他信息,如方法名称, ,目标对象, ,代理类等。

您只需要更改两个类 和Test.java。

文件:BeforeAdvisor.java

File: Test.java

Output

2)AfterReturningAdvice 示例

创建一个包含实际业务逻辑的类。

文件:A.java 与前一个示例相同。

现在,创建实现 接口的advisor类。

文件:AfterAdvisor.java

File: Test.java Same as in the previous example.

Output

3)MethodInterceptor(AroundAdvice)示例

创建一个包含实际业务逻辑的类。

文件:A.java 与前一个示例相同。

现在,创建实现 接口的advisor类。

文件:AroundAdvisor.java

如上例所示创建xml文件,您只需在此处更改advisor 类。

文件:applicationContext.xml

文件:Test.java与前一个示例相同。

Output:

4) ThrowsAdvice 示例

File: Validator.java

File: ThrowsAdvisor.java

Create a class that contains actual business logic.

像前面的示例一样创建xml文件,您只需要更改Validator类和advisor类。

文件:applicationContext.xml

File: Test.java

Output

Spring AOP AspectJ注释示例

  Spring框架advice您使用 实现,而不是Spring 1.2老式的基于dtd的AOP实现,因为它提供了更多的控制,而且易于使用。

有两种方法可以使用Spring AOP AspectJ实现:

通过注释。

通过xml配置(基于模式)

Spring AspectJ AOP实现提供了许多注释:

@Aspect将该类声明为aspect。

@Pointcut声明了切入点表达式。

用于创建advices 的注释如下:

@Before在调用实际方法之前应用它。

@After调用实际方法之后和返回结果之前应用。

@AfterReturning在调用实际方法之后和返回结果之前应用。但是你可以在advices中得到结果值。

@Around在调用实际方法之前和之后应用。

@AfterThrowing如果实际方法抛出异常,则应用它。

理解切入点理解切(入)点(Pointcut)

切入点是Spring AOP的一种表达语言。

注释用于定义切点。我们还可以通过名称引用切点表达式。以下为切入点表达式的简单示例。

切入点表达式的名称是doSomething()。无论返回类型如何,它都将应用于Operation类的所有方法。

理解切点表示式

让我们通过以下示例来理解切入点表达式:

它将适用于所有的 方法。

它将应用于Operation 的所有 方法

它将应用于Operation类的 方法。

它将应用于Employee 的所有 setter方法。

它将应用于 int值的Operation类的所有方法。

1)@Before示例

在实际业务逻辑方法之前应用。您可以在此处执行任何操作,例如conversion,authentication等。

创建一个包含实际业务逻辑的类。

文件:Operation.java

现在,创建包含在advice之前的aspect 类。

文件:TrackOperation.java

现在创建定义bean的 文件。

文件:applicationContext.xml

现在,让我们调用实际方法。

文件:Test.java

Output :

如果您更改如下所示的切点表达式:

Output :

可以看到,在调用k()方法之前没有打印内容。

2) @After Example

在调用实际的业务逻辑方法之后应用AspectJ通知。它可以用来维护日志、安全、通知等。

在这里,我们假设Operation.java, 和Test.java文件与@Before示例中给出的相同。

文件:TrackOperation.java

Output:

可以看到,在调用 、m()和 方法之后,会打印出额外concern ;

3) @AfterReturning Example

通过在返回advice后使用,我们可以在advice中得到结果。

创建包含业务逻辑的类。

文件:Operation.java

File: TrackOperation.java

File: applicationContext.xml

它与 示例中给出的相同

文件:Test.java

现在创建调用实际方法的测试类。

Output:

可以看到返回值打印了两次,一次是通过 类打印的,另一次是通过测试类打印的。

4) @Around 示例

在调用实际的业务逻辑方法之前和之后应用 通知 。

在这里,我们假设 文件与 示例中给出的相同。

创建一个包含实际业务逻辑的类。

文件:Operation.java

创建包含advise的aspect类。

您需要在advice方法中传递 引用,以便我们可以通过调用 方法来继续请求。

文件:TrackOperation.java

`` 文件:Test.java 现在创建调用实际方法的测试类。

Output :

5) @AfterThrowing 示例

通过使用after throw advice,我们可以在 n类中打印异常。让我们看看AspectJ在抛出advice之后的例子。

File: Operation.java

文件:applicationContext.xml 它与 示例中给出的相同

文件:Test.java 现在创建调用实际方法的Test类。

`Output :

```

Spring AOP AspectJ Xml配置示例

Spring允许您在xml文件中定义方面,建advices 和pointcuts 。

在上文中,我们已经看到了使用注释的aop示例。现在我们将通过xml配置文件看到相同的示例。

让我们看一下用于定义advice的xml元素。

aop:before在调用实际业务逻辑方法之前应用它之前。

aop:after在调用实际业务逻辑方法后应用它。

aop:after-returning在返回后,在调用实际的业务逻辑方法之后应用它。它可以用来拦截advice中的返回值。

aop:around它在调用实际业务逻辑方法之前和之后应用。

aop:after-throwing如果实际的业务逻辑方法抛出异常,则应用它。

1) aop:before 示例

AspectJ Before Advice在实际业务逻辑方法之前应用。您可以在此处执行任何操作,例如conversion,authentication 等。

创建一个包含实际业务逻辑的类。

文件:Operation.java

`现在,创建包含在advice之前的aspect 类。

文件:TrackOperation.java

`

File: applicationContext.xml

File: Test.java

Output:

可以看见 : 调用 ,m()和 方法之前会打印 。

2) aop:after 示例

我们假设Operation.java,TrackOperation.java和Test.java文件与aop中给出的相同: 。

现在创建定义bean的applicationContext.xml文件。

文件:applicationContext.xml

Output :

3) aop:after-returning 示例

通过使用 ,我们可以在advice中得到结果。

File: Operation.java

File: TrackOperation.java

`File: applicationContext.xml

File: Test.java

`Output:

`4) aop:around 示例

在调用实际业务逻辑方法之前和之后应用 。

创建一个包含实际业务逻辑的类。

文件:Operation.java

在advice方法中传递 引用,以便我们可以通过调用 方法来继续请求。

文件:TrackOperation.java

File: applicationContext.xml

File: Test.java

Output

5) aop:after-throwing 示例

通过使用 ,我们可以在 类中打印异常。让我们看看AspectJ在抛出advice之后的例子。

File: Operation.java

File: TrackOperation.java

`` File: applicationContext.xml

File: Test.java

Output :

END

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181103G00ZIH00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券