Spring_13_Spring框架的AOP

Spring 框架的 AOP

Spring框架的一个关键组件是面向切面编程(AOP)框架,面向切面的编程需要把程序逻辑分解成不同的部分称为关注点.跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑.有各种各样的常见的很好的方面的例子.如:日志,审计,声明式事务,安全性和缓存等.

在OOP中,关键单元模块度类,而在AOP中单元模块度是方面.依赖注入帮助你对应用程序对象相互理解解耦和AOP可以帮助你从他们所影响的对象中对横切关注点解耦.AOP是像编程语言的触发物,如Perl,NET,java 或者其他.

Spring AOP模块是提供拦截器来拦截一个应用程序,例如,当执行一个方法时,你可以在方法执行之前或之后添加额外的功能.

AOP术语

在我们开始使用AOP工作之前,让我们熟悉一些AOP概念的术语,这些术语并不特定与Spring,而是与AOP有关的.

描述
Aspect一个模块具有一组提供横切需求的APIs.例如,一个日志模块为了记录日志将被AOP方面调用.应用程序可以用有任意数量的方面,这取决于需求.
Join Point在你的应用程序中它代表一个点,你可以在插件AOP方面.你也能说,它是在实际应用程序中,其中一个操作将使用Spring AOP 框架.
Advice这是实际行动之前或之后执行的方法.这是在应用程序执行期间通过Spring AOP 框架实际被调用的代码
Pointcut这是一组一个或多个连接点,通知应该被执行,你可以使用表达式或模式指定切入点正如我们将AOP的例子中看到的.
Introduction引用允许你添加新方法或属性到现有的类中.
Target Object被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象.也称为被通知对象.
WeavingWeaving 把方面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象.这些可以在编译时,类加载时和运行时完成.

Spring 方面可以使用下面提到的五种通知工作

通知描述
前置通知在一个方法执行前,执行通知
后置通知在一个方法执行后,不考虑其结果,执行通知.
返回后通知在一个方法执行后,只有在方法成功完成时,才能执行通知.
抛出异常通知在一个方法执行后,只有在方法退出抛出异常时,才能执行通知.
环绕通知在建议方法调用之后和之前,执行通知.

实现自定义方面

Spring 支持 @AspectJannotation style 的方法和基于模式的方法来实现自定义方面. 这两种方法已经在下面两个子节点进行了详细的介绍.

方法描述
XML Schema based方面是使用常规类一级基于配置的XML来实现的.
@AspectJ based@AspectJ 引用一种声明方面的风格作为带有java5注释的常规的Java注释.

Spring 中基于AOP的XML架构

为了在本届描述使用AOP命名空间标签,你需要导入spring-aop架构,如下所示

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
    <!-- bean definition & AOP specific configuration -->
</beans>

你还需要再你的应用程序的ClassPath中使用一下AspectJ库文件,这些库文件在一个AspectJ装置的lib目录中是可以用的,否则你可以在Internet中下载它们.

jar
aspectjrt.jar
aspectjweaver.jar
aspectj.jar
aopalliance.jar

声明一个aspect

一个aspcet是使用元素声明的,支持bean时使用ref属性引用的.

<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

这里的”aBean“将被配置和依赖注入,就像前面的章节中你看到的其他的Spring bean 一样.

声明一个切点

一个切点有注入确定使用不同建议执行的感兴趣的连接点(即方法),在处理基于配置的XML架构时,切入点将会按照如下所示定义.

<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

下面的实例定义了一个名为”businessService“的切入点,该切入点将与com.tutorialspoint 包下的 Student类中的getName() 方法相匹配

<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.tutorialspoint.Student.getName(..))"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

声明建议

你可以使用

<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>
<!-- a before advice definition -->
<aop:before pointcut-ref="businessService"
method="doRequiredTask"/>
<!-- an after advice definition -->
<aop:after pointcut-ref="businessService"
method="doRequiredTask"/>
<!-- an after-returning advice definition -->
<!--The doRequiredTask method must have parameter named retVal -->
<aop:after-returning pointcut-ref="businessService"
returning="retVal"
method="doRequiredTask"/>
<!-- an after-throwing advice definition -->
<!--The doRequiredTask method must have parameter named ex -->
<aop:after-throwing pointcut-ref="businessService"
throwing="ex"
method="doRequiredTask"/>
<!-- an around advice definition -->
<aop:around pointcut-ref="businessService"
method="doRequiredTask"/>
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>

你可以对不同的建议使用相同的doRequiredTask 或者不同的方法。这些方法将会作为 aspect 模块的一部分来定义.

基于AOP的xml架构的示例

步骤描述
1创建一个名为 SpringExample 的项目,并且在所创建项目的 c src 文件夹下创建一个名为 com.tutorialspoint的包
2使用 Add External JARs 选项添加所需的 Spring 库文件,就如在 Spring Hello World Example 章节中解释的
3在项目中添加 Spring AOP 指定的库文件 r aspectjrt.jar, aspectjweaver.jar 和 aspectj.jar。
4在 com.tutorialspoint 包下创建 Java 类 Logging, Student 和 MainApp
5在src 文件夹下创建 Beans 配置文件 Beans.xml 。
6最后一步是创建所有 Java 文件和 Bean 配置文件的内容,并且按如下解释的那样运行应用程序。

Logging.java

package com.tutorialspoint;

public class Logging {
    /**
     * This is the method which I would like to execute before a selected method
     * execution.
     */
    public void beforeAdvice() {
        System.out.println("Going to setup student profile.");
    }

    /**
     * This is the method which I would like to execute after a selected method
     * execution.
     */
    public void afterAdvice() {
        System.out.println("Student profile has been setup.");
    }

    /**
     * This is the method which I would like to execute when any method returns.
     */
    public void afterReturningAdvice(Object retVal) {
        System.out.println("Returning:" + retVal.toString());
    }

    /**
     * This is the method which I would like to execute if there is an exception
     * raised.
     */
    public void AfterThrowingAdvice(IllegalArgumentException ex) {
        System.out.println("There has been an exception: " + ex.toString());
    }
}

Student.java

package com.tutorialspoint;

public class Student {
    private Integer age;
    private String name;

    public Integer getAge() {
        System.out.println("Age : " + age);
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        System.out.println("Name : " + name);
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void printThrowException() {
        System.out.println("Exception raised");
        throw new IllegalArgumentException();
    }
}

MainApp.java

package com.tutorialspoint;

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

public class MainApp {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        Student student = (Student) context.getBean("student");
        student.getName();
        student.getAge();
        student.printThrowException();
    }
}

Beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

    <aop:config>
        <aop:aspect id="log" ref="logging">
            <aop:pointcut id="selectAll"
                expression="execution(* com.tutorialspoint.*.*(..))" />
            <aop:before pointcut-ref="selectAll" method="beforeAdvice" />
            <aop:after pointcut-ref="selectAll" method="afterAdvice" />
            <aop:after-returning pointcut-ref="selectAll"
                returning="retVal" method="afterReturningAdvice" />
            <aop:after-throwing pointcut-ref="selectAll"
                throwing="ex" method="AfterThrowingAdvice" />
        </aop:aspect>
    </aop:config>
    <!-- Definition for student bean -->
    <bean id="student" class="com.tutorialspoint.Student">
        <property name="name" value="Zara" />
        <property name="age" value="11" />
    </bean>
    <!-- Definition for logging aspect -->
    <bean id="logging" class="com.tutorialspoint.Logging" />

</beans>

运行程序后会报错

Going to setup student profile.
Name : Zara
Student profile has been setup.
Returning:Zara
Going to setup student profile.
Age : 11
Student profile has been setup.
Returning:11
Going to setup student profile.
Exception raised
Student profile has been setup.
There has been an exception: java.lang.IllegalArgumentException
Exception in thread "main" java.lang.IllegalArgumentException

让我们来解释一下上面定义的com.tutorialspoint中选择所有的方法,让我们假设一下,你想要在一个特殊的方法之前或者之后执行你的建议,你可以通过替换使用真实类和方法名称的切点定义中的星号(*)来定义你的切入点来缩短你的执行

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
    <aop:config>
        <aop:aspect id="log" ref="logging">
            <aop:pointcut id="selectAll"
                expression="execution(* com.tutorialspoint.Student.getName(..))" />
            <aop:before pointcut-ref="selectAll" method="beforeAdvice" />
            <aop:after pointcut-ref="selectAll" method="afterAdvice" />
        </aop:aspect>
    </aop:config>
    <!-- Definition for student bean -->
    <bean id="student" class="com.tutorialspoint.Student">
        <property name="name" value="Zara" />
        <property name="age" value="11" />
    </bean>
    <!-- Definition for logging aspect -->
    <bean id="logging" class="com.tutorialspoint.Logging" />
</beans>

Spring 中基于AOP的@AspectJ

@AspectJ作为Java5 注释的普通java类,它指明的是上面aspects的一种风格,通过你再基于架构的XML配置文件中包含以下元素,@AspectJ支持是可以用的.

<aop:aspectj-autoproxy/>

你还需要再你的应用程序的classPath中使用一下AspectJ库文件.这些库文件子一个AspectJ装置的”lib”目录中是可用的,否则你可以在internet中下载它们

jar
aspectjr.jar
aspectjweaver.jar
aspectj.jar
aopalliance.jar

声明一个aspect

Aspects类和其他任何正常的bean一样,除了他们将会用@AspectJ注释之外,它和其他类一样可能有方法和字段 如下:

package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AspectModule {
}

它们将在XML中按照如下进行配置,就和其他任何bean一样.

<bean id="myAspect" class="org.xyz.AspectModule">
<!-- configure properties of aspect here as normal -->
</bean>

声明一个切入点

一个切入点有助于确定使用不同建议执行的感兴趣的连接点(即方法).在处理基于配置的XML架构时,欺辱的的生命有两部分.

  • 一个切入点表达式决定了我们感兴趣的哪个方法会真正被执行
  • 一个切入点标签包含一个名称和任意数量的参数.方法的真正内容是不相干的,并且实际上它应该是空的.

下面的实例中定义了一个名为”businessService”的切入点,该切入点将与com.tutorialspoint包下的类中可用的每一个方法相匹配:

import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.xyz.myapp.service.*.*(..))") // expression
private void businessService() {} // signature

下面的示例中定义了一个名为”getname”的切入点,该切入点将与com.tutorialspoint包下的Student类中的getName() 方法相匹配.

import org.aspectj.lang.annotation.Pointcut;
@Pointcut("execution(* com.tutorialspoint.Student.getName(..))")
private void getname() {}

声明建议

你可以使用 @{ADVICE-NAME} 注释来声明五个建议中的任意一个,如下所示.这个假设你已经定义了一个切入点标签方法businessService()

@Before("businessService()")
public void doBeforeTask(){
...
}
@After("businessService()")
public void doAfterTask(){
...
}
@AfterReturning(pointcut = "businessService()", returning="retVal")
public void doAfterReturnningTask(Object retVal){
// you can intercept retVal here.
...
}
@AfterThrowing(pointcut = "businessService()", throwing="ex")
public void doAfterThrowingTask(Exception ex){
// you can intercept thrown exception here.
...
}
@Around("businessService()")
public void doAroundTask(){
...
}

你可以为任意一个建议定义你的切入点内联.下面是在建立之前定义内联切入点的一个示例:

@Before("execution(* com.xyz.myapp.service.*.*(..))")
public doBeforeTask(){
...
}

基于AOP 的@AspectJ示例

步骤描述
1创建一个名为 SpringExample 的项目,并且在所创建项目的 c src 文件夹下创建一个名为 com.tutorialspoint 的包。
2使用 Add External JARs 选项添加所需的 Spring 库文件,就如在 Spring Hello World Example 章节中解释的那样。
3在项目中添加 Spring AOP 指定的库文件 r aspectjrt.jar, aspectjweaver.jar 和 aspectj.jar。
4在 com.tutorialspoint 包下创建 Java 类 Logging, Student 和 MainApp
5在src 文件夹下创建 Beans 配置文件 Beans.xml
6最后一步是创建所有 Java 文件和 Bean 配置文件的内容,并且按如下解释的那样运行应用程序。

Logging.java

package com.tutorialspoint;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

public class Logging {
    /**
     * Following is the definition for a pointcut to select all the methods
     * available. So advice will be called for all the methods.
     */
    @Pointcut("execution(* com.tutorialspoint.*.*(..))")
    private void selectAll() {
    }

    /**
     * This is the method which I would like to execute before a selected method
     * execution.
     */
    @Before("selectAll()")
    public void beforeAdvice() {
        System.out.println("Going to setup student profile.");
    }

    /**
     * This is the method which I would like to execute after a selected method
     * execution.
     */
    @After("selectAll()")
    public void afterAdvice() {
        System.out.println("Student profile has been setup.");
    }

    /**
     * This is the method which I would like to execute when any method returns.
     */
    @AfterReturning(pointcut = "selectAll()", returning = "retVal")
    public void afterReturningAdvice(Object retVal) {
        System.out.println("Returning:" + retVal.toString());
    }

    /**
     * This is the method which I would like to execute if there is an exception
     * raised by any method.
     */
    @AfterThrowing(pointcut = "selectAll()", throwing = "ex")
    public void AfterThrowingAdvice(IllegalArgumentException ex) {
        System.out.println("There has been an exception: " + ex.toString());
    }
}

Student.java

package com.tutorialspoint;

public class Student {
    private Integer age;
    private String name;

    public Integer getAge() {
        System.out.println("Age : " + age);
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        System.out.println("Name : " + name);
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void printThrowException() {
        System.out.println("Exception raised");
        throw new IllegalArgumentException();
    }
}

MainApp.java

package com.tutorialspoint;

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

public class MainApp {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        Student student = (Student) context.getBean("student");
        student.getName();
        student.getAge();
        student.printThrowException();
    }
}

Beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
    <aop:aspectj-autoproxy />
    <!-- Definition for student bean -->
    <bean id="student" class="com.tutorialspoint.Student">
        <property name="name" value="Zara" />
        <property name="age" value="11" />
    </bean>
    <!-- Definition for logging aspect -->
    <bean id="logging" class="com.tutorialspoint.Logging" />
</beans>

结果

Age : 11
Exception raised
java.lang.IllegalArgumentException
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值