Spring系列(六)- Spring 框架的 AOP

Spring 框架的 AOP

  • 跨一个应用程序的多个点的功能被称为横切关注点
  • 这些横切关注点在概念上独立于应用程序的业务逻辑
  • 有各种各样的常见的很好的方面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
  • Spring AOP 模块提供拦截器来拦截一个应用程序,例如,当执行一个方法时,你可以在方法执行之前或之后添加额外的功能。

AOP 术语

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

通知的类型

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

实现自定义方面

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

基于 AOP 的 XML架构(XML Schema based)

为了在本节的描述中使用 aop 命名空间标签,你需要导入 spring-aop j架构,如下所述:

<?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>

我这里用的是maven工具,所以可以直接导入相关依赖

    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
    </dependencies>

创建切面类

package com.demo.point;

public class Logging {
    /**
     * 前置通知,在一个方法执行之前,执行通知
     */
    public void beforeAdvice(){
        System.out.println("Going to setup student profile");
    }

    /**
     * 后置通知,在一个方法执行之后,不考虑其结果,执行通知
     */
    public void afterAdvice(){
        System.out.println("Student profile has been setup");
    }

    /**
     * 返回后通知,在一个方法执行之后,只有在方法成功完成时,才能执行通知。
     */
    public void afterReturningAdvice(Object retVal){
        System.out.println("Returning:" + retVal.toString() );
    }

    /**
     * 抛出异常后通知,在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知
     * @param ex
     */
    public void afterThrowingAdvice(IllegalArgumentException ex){
        System.out.println("There has been an exception: " + ex.toString());
    }
}

创建student

package com.demo.dao;

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();
    }
}

spring-confg配置

<?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">
            <!-- execution(* com.demo.dao.*.*(..)) 表示  所有修饰符的所有返回值类型  com.demo.dao 包下的所有方法-->
            <aop:pointcut id="selectAll" expression="execution(* com.demo.dao.*.*(..))"/>
            <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>

    <!-- 配置bean -->
    <bean id="student" class="com.demo.dao.Student">
        <property name="name" value="张三"/>
        <property name="age" value="24"/>
    </bean>

    <!-- 将切面类交与Spring容器管理 -->
    <bean id="logging" class="com.demo.point.Logging"/>


</beans>

测试类

import com.demo.dao.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public static void main(String[] args){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
        Student student = (Student) applicationContext.getBean("student");
        student.getName();
        student.getAge();
        student.printThrowException();
    }
}

运行

Going to setup student profile
Name : 张三
Student profile has been setup
Returning:张三
Going to setup student profile
Age : 24
Student profile has been setup
Returning:24
Going to setup student profile
Exception raised
Student profile has been setup
There has been an exception: java.lang.IllegalArgumentException
.....
other exception content

如果你想要在一个特殊的方法之前或者之后执行你的建议,你可以通过替换使用真实类和方法名称的切入点定义中的星号(*)来定义你的切入点来缩短你的执行

<?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">
            <!-- execution(* com.demo.*(..)) 表示  所有修饰符的所有返回值类型  com.demo 包下的所有方法-->
            <aop:pointcut id="selectAll" expression="execution(* com.demo.dao.Student.getName(..))"/>
            <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>

    <!-- 配置bean -->
    <bean id="student" class="com.demo.dao.Student">
        <property name="name" value="张三"/>
        <property name="age" value="24"/>
    </bean>

    <!-- 将切面类交与Spring容器管理 -->
    <bean id="logging" class="com.demo.point.Logging"/>


</beans>

运行结果

Going to setup student profile
Name : 张三
Student profile has been setup
Returning:张三
Age : 24
Exception raised

基于 AOP 的 @AspectJ(@AspectJ based)

要想使用@AspectJ,首先需要再spring-config.xml中配置,也需要导入aspectjweaver依赖包

      <!-- 使用注解自动生成代理对象 -->
      <aop:aspectj-autoproxy/>

例:

<?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/>

    <!-- 配置bean -->
    <bean id="student" class="com.demo.dao.Student">
        <property name="name" value="张三"/>
        <property name="age" value="24"/>
    </bean>

    <!-- 将切面类交与Spring容器管理 -->
    <bean id="logging" class="com.demo.point.Logging"/>


</beans>
package com.demo.point;

import org.aspectj.lang.annotation.*;

/**
 * @Title:
 * @auther: raohr
 * @Date: 2021/2/23 10:18
 * @param:
 * @Description:
 * @return:
 * @throws:
 */
@Aspect
public class Logging {


    @Pointcut("execution(* com.demo.dao.Student.*(..))")
    public void selectAll(){}

    /**
     * 前置通知,在一个方法执行之前,执行通知
     */
    @Before("selectAll()")
    public void beforeAdvice(){
        System.out.println("Going to setup student profile");
    }

    /**
     * 后置通知,在一个方法执行之后,不考虑其结果,执行通知
     */
    @After("selectAll()")
    public void afterAdvice(){
        System.out.println("Student profile has been setup");
    }

    /**
     * 返回后通知,在一个方法执行之后,只有在方法成功完成时,才能执行通知。
     */
    @AfterReturning(pointcut = "selectAll()",returning = "retVal")
    public void afterReturningAdvice(Object retVal){
        System.out.println("Returning:" + retVal.toString() );
    }

    /**
     * 抛出异常后通知,在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知
     * @param ex
     */
    @AfterThrowing(pointcut = "selectAll()",throwing = "ex")
    public void afterThrowingAdvice(IllegalArgumentException ex){
        System.out.println("There has been an exception: " + ex.toString());
    }
}

import com.demo.dao.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    public static void main(String[] args){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
        Student student = (Student) applicationContext.getBean("student");
        student.getName();
        student.getAge();
        student.printThrowException();
    }
}

运行

Going to setup student profile
Name : 张三
Student profile has been setup
Returning:张三
Going to setup student profile
Age : 24
Student profile has been setup
Returning:24
Going to setup student profile
Exception raised
Student profile has been setup
There has been an exception: java.lang.IllegalArgumentException
...

以上难以理解,可以参考这篇,更通俗易懂
详解spring AOP

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值