Spring——从AOP到Spring AOP

一、前言

本文分为三个部分,分别介绍AOP基础知识,以AspectJ AOP为例介绍静态AOP,以Spring AOP为例介绍动态AOP,其中以Spring AOP为重点,全文从AOP到AspectJ AOP,再到Spring AOP。

二、AOP基础知识

2.1 AOP

2.1.1 引子:AOP,AOP是什么?

官方解释:
AOP英文全称Aspect Oriented Program,直译面向切面编程,通过预编译方式或运行期动态代理(根据具体使用何种方式划分静态AOP、动态AOP)实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性。

我的理解:
AOP像OOP一样,只是一种编程范式,AOP并没有规定说,实现AOP协议的代码,要用什么方式去实现,AOP和OOP一样只是一种编程思想,且看两种对比的方式理解AOP。

2.1.2 使用对比的方式理解AOP——AOP与OOP

用理解OOP(面向对象编程)的方式去理解AOP(面向切面编程)
OOP是一种软件开发思想,是以类为基本单位的;AOP是一种程序维护思想,是以方法为基本单位的。
OOP的特点是继承、多态和封装;AOP的特点是对方法增强(有装饰器模式的影子),有静态AOP和动态AOP两种。

2.1.3 使用对比的方式理解AOP——静态AOP与动态AOP

静态AOP:AOP框架在编译阶段对程序进行修改,即实现对目标类的增强,生成静态的AOP代理类(生成的*.class文件已经被修改,需要使用特定的编译器)。举例:AspectJ。 动态AOP:AOP框架在运行阶段动态生成AOP代理(在内存中以JDK动态代理或cglib动态地生成AOP代理类),以实现对目标对象的增强。举例:Spring AOP。

如下表:

AOP形式 举例 特点
编译时织入(即静态AOP) AspectJ 具有较好的性能,但需要特殊的编译器
运行时织入(即动态AOP) Spring AOP、JBoss 性能较差,纯Java代码实现,无需特殊编译器

所以,Spring中的AOP是一种动态AOP,在运行阶段动态生成AOP代理以实现对目标对象的增强。

2.1.4 使用对比的方式理解AOP——Spring IOC与Spring AOP

Spring IOC和Spring AOP都是Spring的功能点,但是两个特点之间存在联系。
Spring中的AOP代理由Spring的loC容器负责生成、管理,其依赖关系也由loC容器负责管理。因此,AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可由loC容器的依赖注入提供。
Spring实现AOP的方法跟其他的框架不同,Spring并不是要提供最完整的AOP实现(尽管 Spring AOP有这个能力), Spring侧重于AOP实现和 Spring loc容器之间的整合,用于帮助解决企业级开发 中的常见问题。
因此, Spring的AOP通常和 Spring loc容器一起使用, Spring AOP从来没有打算通过提供一种全 面的AOP解决方案来与 Aspect竞争, Spring AOP采用基于代理的AOP实现方案,而 AspectJ则采用编译时增强的解决方案。

2.2 AOP的项目需求

现在假设系统中有三段完全相同的代码,这些代码通常会采用“复制”、“粘贴”的方式来完成,通过这种“复制”,“粘贴”的方式开发出来的软件示意图如图所示:

这种做法的不足之处在于如果有一天,上图中的深色代码段需要修改,那是不是要打开三个地方的代码进行修改?如果不是三个地方包含这段代码,而是100个地方,甚至是1000个地方包含这个代码段,那会是什么后果? 为了解决这个问题,通常会将图中所示的深色代码部分定义成一个方法,然后在三个代码段中分别调用该方法即可。在这种方式下,软件系统的结构示意图如图:

如果需要修改深色代码部分,只要修改一个地方即可,不管整个系统中有多少个地方调用了该方法,程序无须修改这些地方,只需修改被调用的方法即可—通过这种方式,大大降低了软件后期维护的复杂度,这就是AOP在项目开发中的实践使用

2.3 AOP的相关概念(用Spring AOP来解释AOP的相关概念)

AOP含义(基于AOP概念的AOP含义解释):在面向切面编程的思想里面,把功能分为核心业务功能和周边功能。 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务;所谓的周边功能,比如性能统计,日志,事务管理等等 。周边功能在Spring的面向切面编程AOP思想里,即被定义为切面,在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 “编织” 在一起,这个过程叫AOP。

AOP核心概念(一共8个)

1、横切关注点(Cross-cutting concern)名词, 对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点

2、切面(aspect) 类是对物体特征的抽象,切面就是对横切关注点的抽象

3、连接点(joinpoint) 被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器

4、切入点(pointcut) 对连接点进行拦截的定义,就是定义对哪些方法进行拦截,可以对一个类的所有方法进行拦截,也可以只拦截一个类以特定字符开头的方法

5、通知(advice) 所谓通知指的就是切面Aspect中对于横切关注点Cross-cutting concerns的具体实现代码,亦即拦截到连接点之后要执行的代码。通知分为前置@Before、后置@After、异常@AfterThrowing、最终@AfterRunning、环绕@Around通知五类,就是拦截到方法之后,在什么时机执行切面的代码.

附:AspectJ 支持 5 种类型的通知注解(这五种通知后面的Spring AOP代码中会一一演示):
@Before:前置通知, 在方法执行之前执行
@After: 后置通知,在方法执行之后执行
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知,在方法抛出异常之后
@Around: 环绕通知,围绕着方法执行

6、目标对象(Target)名词,代理的目标对象。
7、织入(weave) 动词,将切面(aspect)应用到目标对象(Target)并导致代理对象创建的过程成为织入。
8、引入(introduction) 在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段

三、静态AOP实现:AspectJ AOP

3.1 AspectJ AOP

为什么是AspectJ?
AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能,后来的很多AOP框架都借鉴或接纳其中的一些思想,由于 Spring4.0的AOP与 Aspect进行了很好的集成,因此掌握 Aspect是学习Spring AOP的基础。
从Spring2.0开始, Spring AOP已经引入了对 AspectJ的支持,并允许直接使用 AspectJ进行AOP编程,而 Spring自身的 AOP API也努力与 AspectJ保持一致,因此,学习 Spring AOP就必然需要从 Aspect 开始,因为它是Java领域最流行的AOP解决方案,即使不用 Spring框架,也可以直接使用 Aspect进行AOP编程。

AspectJ简介
AspectJ是Java语言的一个AOP实现,其主要包括两个部分:一个部分定义了如何表达、定义AOP编程中的语法规范,通过这套语法规范,可以方便地用AOP来解决Java语言中存在的交叉关注点的问题:另一个部分是工具部分,包括编译器、调试工具等。
AspectJ是最早的、功能比较强大的AOP实现之一,对整套AOP机制都有较好的实现,很多其他的AOP实现,也借鉴或采纳了 AspectJ中的很多设计,在Java领域, Aspect中的很多语法结构基本上已成为AOP领域的标准。

3.2 AspectJ AOP实践

3.2.1 目录结构

如图:

3.2.2 两个实体类和启动类

代码:

package mypackage;

/**
 * Created by 30292 on 2020/2/22.
 */
public class Test {
    public static  void main(String[] args){
        Hello hello=new Hello();
        hello.foo();
        hello.addUser("张三","1000");

        World world=new World();
        world.bar();
    }
}
class Hello {
    public void foo(){
        System.out.println("执行Hello组件的foo()方法");
    }
    public int addUser(String name,String pass){
        System.out.println("执行Hello组件的addUser()添加用户:"+name);
        return 20;
    }
}
class World {
    public void bar(){
        System.out.println("执行World组件的bar()方法");
    }
}

3.2.3 三个切面

package mypackage;

public aspect AuthAspect{
        before():execution(* mypackage.*.*(..))
        {
        System.out.println("before 模拟进行权限检查...");
        }
}
aspect LogAspect{
        after():execution(* mypackage.*.*(..))
        {
        System.out.println("after 模拟记录日志...");
        }
}

aspect TxAspect{
        Object around():call(* mypackage.*.*(..)){
        System.out.println("around start模拟开启事务...");
        Object rvt=proceed();
        System.out.println("around finish模拟结束事务...");
        return rvt;
}
}

3.2.4 运行结果

运行结果如下(AspectJ和实际方法均有打印):

四、动态AOP实现:Spring AOP

Spring AOP采用基于代理的AOP实现方案,而AspectJ采用编译时增强的解决方案。

4.1 Spring AOP基本知识

4.1.1 Spring IOC实现Spring AOP

Spring中的AOP代理由Spring的loC容器负责生成、管理,其依赖关系也由loC容器负责管理。因此,AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可由loC容器的依赖注入提供。
Spring实现AOP的方法跟其他的框架不同,Spring并不是要提供最完整的AOP实现(尽管 Spring AOP有这个能力), Spring侧重于AOP实现和 Spring loc容器之间的整合,用于帮助解决企业级开发 中的常见问题。
因此, Spring的AOP通常和 Spring loc容器一起使用, Spring AOP从来没有打算通过提供一种全 面的AOP解决方案来与 Aspect竞争, Spring AOP采用基于代理的AOP实现方案,而 AspectJ则采用 编译时增强的解决方案。

4.1.2 Spring AOP代理

Spring默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了。 Spring也可以使用cglib代理,在需要代理类而不是代理接口的时候, Spring会自动切换为使用cglib代理。但Spring推荐使用面向接口编程,因此业务对象通常都会实现一个或多个接口,此时默认将使用JDK动态代理,但也可强制使用cglib代理。

4.1.3 Spring AOP是一种动态AOP

Spring AOP是动态AOP,使用纯Java实现,不需要特定的编译工具, Spring AOP也不需要控制类装载器层次,因此它可以在所有的 Java Web容器或应用服务器中运行良好。

4.1.4 Spring AOP连接点

Spring目前仅支持将方法调用作为连接点( Joinpoint),如果需要把对成员变量的访向和更新也作为增强处理的连接点,则可以考虑使用 AspecJ。

4.1.5 Spring AOP实现

Spring2.0可以无缝地整合 Spring AOP,IOC和 AspectJ,使得所有的AOP应用完全融入基于 Spring 的框架中,这样的集成不会影响 Spring AOP API或者 AOP Alliance API, Spring AOP保持了向下兼容性 依然允许直接使用 Spring AOP API来完成AOP编程。

一旦掌握了上面AOP的相关概念,不难发现进行AOP编程其实是很简单的事情,纵观AOP编程, 其中需要程序员参与的只有三个部分。
(1)定义普通业务组件;
(2)定义切入点,一个切入点可能横切多个业务组件;
(3)定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作;
其中第一个部分是最平常不过的事情,所以无须额外说明。那么进行AOP编程的关键就是定义切入点定义增强处理。一旦定义了合适的切入点和增强处理,AOP框架将会自动生成AOP代理,而 AOP代理的方法大致有如下公式:
AOP代理的方法=增强处理+目标对象的方法
我们使用 AspectJ方式来定义切入点和增强处理,在这种方式下, Spring依然有如下两种选择来定 义切入点和增强处理:
(1)基于注解的“零配置”方式:使用@Aspect,@Pointcut等注解来标注切入点和增强处理,在4.2
(2)基于XML配置文件的管理方式:使用Spring配置文件来定义切入点和增强处理,在4.3

附:自动增强
自动增强,指的是Spring会判断一个或多个切面是否需要对指定的Bean进行增强,并据此自动生成相应的代理,从而使得增强处理在合适的时候被调用,整个过程对开发者来说是透明的,是自动的,所以是自动增强。

4.2 Spring AOP实践:注解方式实现

4.2.1 目录结构

4.2.2 两个POJO类

先看两个pojo类,很简单:

package com.pojo;

import org.springframework.stereotype.Component;

/**
 * Created by 30292 on 2020/2/21.
 */
@Component("hello")
public class Hello {
    public void foo() {
        System.out.println("执行hello组件的foo()方法");
    }

    public void addUser(String name, String pass) {
        System.out.println("执行Hello组件的addUser添加用户:" + name);
    }

    public void _functionException(int orgin) {
        int a = orgin / 0;  //除数为0,制造异常
    }
}

package com.pojo;

import org.springframework.stereotype.Component;

/**
 * Created by 30292 on 2020/2/21.
 */
@Component("world")
public class World {
    public void bar(){
        System.out.println("执行World组件的bar()方法");
    }
}

4.2.3 五个Aspect类

再看五个aspect类,描述了五种增强,使用注解的方式,分别是@Before @After @AfterReturning @AfterThrowing @Around

@Before

package com.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import java.util.Arrays;

@Aspect
public class BeforeAspect {  //定义一个切面   某类上面加上一个@Aspect注解,就变成了一个切面
    @Before("execution(* com.pojo.*.*(..))")
    public void beforeAspectFunction(JoinPoint joinPoint){
        System.out.println("这里是Before增强");
        System.out.println("Before增强:被织入增强处理的目标方法为: "+joinPoint.getSignature().getName());
        System.out.println("Before增强:目标方法的参数为: "+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("Before增强:被织入增强处理的目标对象为: "+joinPoint.getTarget());
    }
}

@After

package com.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;

import java.util.Arrays;


@Aspect
public class AfterAspect {
    @After("execution(* com.pojo.*.*(..))")
    public void afterAspectFunction(JoinPoint joinPoint){
        System.out.println("这里是After增强");
        System.out.println("After增强:被织入增强处理的目标方法为: "+joinPoint.getSignature().getName());
        System.out.println("After增强:目标方法的参数为: "+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("After增强:被织入增强处理的目标对象为: "+joinPoint.getTarget());
    }
}

@AfterReturning

package com.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

import java.util.Arrays;

@Aspect
public class AfterReturningAspect {
    @AfterReturning(returning = "rvt", pointcut = "execution(* com.pojo.*.*(..))")
    public void afterReturningAspectFunction(JoinPoint joinPoint, Object rvt){
        System.out.println("这里是AfterReturning增强,打印目标方法返回值: "+rvt);
        System.out.println("AfterReturning增强:被织入增强处理的目标方法为: "+joinPoint.getSignature().getName());
        System.out.println("AfterReturning增强:目标方法的参数为: "+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("AfterReturning增强:被织入增强处理的目标对象为: "+joinPoint.getTarget());

    }
}

@AfterThrowing

package com.aspect;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AfterThrowingAspect {
    @AfterThrowing(throwing = "ex",pointcut = "execution(* com.pojo.*.*(..))")
    public void afterThrowingAspectFunction(Throwable ex){
        System.out.println("这里是AfterThrowing增强处理,打印目标方法中抛出的异常: "+ex);
    }
}

@Around

package com.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AroundAspect {
    @Around("execution(* com.pojo.*.*(..))")
    public Object aroundAspectFunction(ProceedingJoinPoint joinPoint) throws java.lang.Throwable {
        System.out.println("这里是Around增强");
        System.out.println("Around增强:执行目标方法之前,模拟开始事务...");
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 1) {
            args[0] = "[增加的前缀]" + args[0];
        }
        Object rvt = joinPoint.proceed(args);
        System.out.println("Around增强:执行目标方法之后,模拟结束事务...");
        if (rvt != null && rvt instanceof Integer)
            rvt = (Integer) rvt * (Integer) rvt;
        return rvt;
    }
}

4.2.4 配置文件applicationContext.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"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
	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
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context      
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	<!-- 启用AspectJ支持-->
	<aop:aspectj-autoproxy/>
    <!--扫描pojo中的@Component注解 aspect中的@Aspect注解和其他增强注解,如@Before @After @AfterReturning @AfterThrowing @Around  -->
	<context:component-scan base-package="com.pojo,com.aspect">
		<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"></context:include-filter>
	</context:component-scan>
</beans>

4.2.5 启动类Test

package com.test;

import com.pojo.Hello;
import com.pojo.World;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {

	public static void main(String[] args) {
        //加载配置文件 applicationContext.xml
		ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
        //hello类
		Hello hello=(Hello)context.getBean("hello");
		hello.foo();
		hello.addUser("小明","1000");
		try{
			hello._functionException(5);
		}catch (Exception e){
		}

		//world类
		World world=(World) context.getBean("world");
		world.bar();
	}
}

4.2.6 运行结果

这里是Around增强
Around增强:执行目标方法之前,模拟开始事务...
这里是Before增强
Before增强:被织入增强处理的目标方法为: foo
Before增强:目标方法的参数为: []
Before增强:被织入增强处理的目标对象为: com.pojo.Hello@1d2adfbe
执行hello组件的foo()方法
Around增强:执行目标方法之后,模拟结束事务...
这里是AfterReturning增强,打印目标方法返回值: null
AfterReturning增强:被织入增强处理的目标方法为: foo
AfterReturning增强:目标方法的参数为: []
AfterReturning增强:被织入增强处理的目标对象为: com.pojo.Hello@1d2adfbe
这里是After增强
After增强:被织入增强处理的目标方法为: foo
After增强:目标方法的参数为: []
After增强:被织入增强处理的目标对象为: com.pojo.Hello@1d2adfbe

这里是Around增强
Around增强:执行目标方法之前,模拟开始事务...
这里是Before增强
Before增强:被织入增强处理的目标方法为: addUser
Before增强:目标方法的参数为: [[增加的前缀]小明, 1000]
Before增强:被织入增强处理的目标对象为: com.pojo.Hello@1d2adfbe
执行Hello组件的addUser添加用户:[增加的前缀]小明
Around增强:执行目标方法之后,模拟结束事务...
这里是AfterReturning增强,打印目标方法返回值: null
AfterReturning增强:被织入增强处理的目标方法为: addUser
AfterReturning增强:目标方法的参数为: [[增加的前缀]小明, 1000]
AfterReturning增强:被织入增强处理的目标对象为: com.pojo.Hello@1d2adfbe
这里是After增强
After增强:被织入增强处理的目标方法为: addUser
After增强:目标方法的参数为: [[增加的前缀]小明, 1000]
After增强:被织入增强处理的目标对象为: com.pojo.Hello@1d2adfbe

这里是Around增强
Around增强:执行目标方法之前,模拟开始事务...
这里是Before增强
Before增强:被织入增强处理的目标方法为: _functionException
Before增强:目标方法的参数为: [5]
Before增强:被织入增强处理的目标对象为: com.pojo.Hello@1d2adfbe
这里是AfterThrowing增强处理,打印目标方法中抛出的异常: java.lang.ArithmeticException: / by zero
这里是After增强
After增强:被织入增强处理的目标方法为: _functionException
After增强:目标方法的参数为: [5]
After增强:被织入增强处理的目标对象为: com.pojo.Hello@1d2adfbe

这里是Around增强
Around增强:执行目标方法之前,模拟开始事务...
这里是Before增强
Before增强:被织入增强处理的目标方法为: bar
Before增强:目标方法的参数为: []
Before增强:被织入增强处理的目标对象为: com.pojo.World@36902638
执行World组件的bar()方法
Around增强:执行目标方法之后,模拟结束事务...
这里是AfterReturning增强,打印目标方法返回值: null
AfterReturning增强:被织入增强处理的目标方法为: bar
AfterReturning增强:目标方法的参数为: []
AfterReturning增强:被织入增强处理的目标对象为: com.pojo.World@36902638
这里是After增强
After增强:被织入增强处理的目标方法为: bar
After增强:目标方法的参数为: []
After增强:被织入增强处理的目标对象为: com.pojo.World@36902638

解释:输出结果分为四段,分别是
hello.foo();
hello.addUser(“小明”,“1000”);
hello._functionException(5);
world.bar();
给出解释,为节约篇幅,仅解释 hello.addUser(“小明”,“1000”);和hello._functionException(5);两个就好了,一个带两个参数,一个带异常,hello.foo();和world.bar(); 都是一样的,解释如下图:

4.3 Spring AOP实践:XML配置文件方式实现

4.3.1 目录结构

4.3.2 两个POJO类

package com.pojo;

public class Hello {
    public void foo() {
        System.out.println("执行hello组件的foo()方法");
    }

    public void addUser(String name, String pass) {
        System.out.println("执行Hello组件的addUser添加用户:" + name);
    }

    public void _functionException(int orgin) {
        int a = orgin / 0;
    }
}

package com.pojo;

public class World {
    public void bar() {
        System.out.println("执行World组件的bar()方法");
    }
}

4.3.3 三个Aspect类

package com.aspect;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

import java.util.Arrays;
//该类中有四个通知 @Before @AfterReturning @After @Around  所以称为FourAdviceTest
public class FourAdviceTest {
    public Object aroundFunction(ProceedingJoinPoint joinPoint) throws java.lang.Throwable{
          System.out.println();
        System.out.println("Around增强:执行目标方法之前,模拟开始事务...");
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0 && args[0].getClass()==String.class) {
            args[0] = "[增加的前缀]" + args[0];
        }
        Object rvt = joinPoint.proceed(args);
        System.out.println("Around增强:执行目标方法之后,模拟结束事务...");
        if (rvt != null && rvt instanceof Integer)
            rvt = (Integer) rvt * (Integer) rvt;
        return rvt;
    }
    public void beforeFunction(JoinPoint joinPoint){
        System.out.println("FourAdviceTest-Before增强:模拟执行权限检查");
        System.out.println("FourAdviceTest-Before增强:被织入增强处理的目标方法为: "+joinPoint.getSignature().getName());
        System.out.println("FourAdviceTest-Before增强:目标方法的参数为: "+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("FourAdviceTest-Before增强:被织入增强处理的目标对象为: "+joinPoint.getTarget());
    }

    public void afterReturningFunction(JoinPoint joinPoint, Object rvt){
        System.out.println("AfterReturning增强:获取目标方法的返回值: "+rvt);
        System.out.println("AfterReturning增强:模拟记录日志功能...");
        System.out.println("AfterReturning增强:被织入增强处理的目标方法为: "+joinPoint.getSignature().getName());
        System.out.println("AfterReturning增强:目标方法的参数为: "+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("AfterReturning增强:被织入增强处理的目标对象为: "+joinPoint.getTarget());
    }

    public void afterFunction(JoinPoint joinPoint){
        System.out.println("After增强:模拟方法结束后的释放资源...");
        System.out.println("After增强:被织入增强处理的目标方法为: "+joinPoint.getSignature().getName());
        System.out.println("After增强:目标方法的参数为: "+ Arrays.toString(joinPoint.getArgs()));
        System.out.println("After增强:被织入增强处理的目标对象为: "+joinPoint.getTarget());
    }
}

package com.aspect;

public class RepairAspect {
    public void doRecoveryActions(Throwable ex){
        System.out.println("打印异常:"+ex);
    }
}
package com.aspect;

//该类中有一个通知 @Before  因为这是第二个@Before  所以称为SecondAdviceTest
public class SecondAdviceTest {
    public void authority(String aa){
        System.out.println("SecondAdviceTest-Before增强:模拟执行检查"+"目标方法的参数为: "+aa);
    }
}

4.3.4 Spring配置文件appliationContext.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"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
       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
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context      
   http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* com.pojo.*.*(..))"></aop:pointcut>

        <aop:aspect id="fourAdviceAspect" ref="fourAdviceBean" order="2">
            <aop:after pointcut="execution(* com.pojo.*.*(..))" method="afterFunction"></aop:after>
            <aop:before pointcut="execution(* com.pojo.*.*(..))" method="beforeFunction"></aop:before>
            <aop:after-returning pointcut="execution(* com.pojo.*.*(..))" method="afterReturningFunction"
                                 returning="rvt"></aop:after-returning>
            <aop:around pointcut="execution(* com.pojo.*.*(..))" method="aroundFunction"></aop:around>
        </aop:aspect>
        
        <aop:aspect id="secondAdviceAspect" ref="secondAdviceBean" order="1">
            <aop:before pointcut="execution(* com.pojo.*.*(..)) and args(aa)" method="authority"></aop:before>
        </aop:aspect>
        
        <aop:aspect id="afterThrowingAdviceAspect" ref="afterThrowingAdviceBean">
            <aop:after-throwing pointcut-ref="myPointcut" method="doRecoveryActions" throwing="ex"></aop:after-throwing>
        </aop:aspect>

    </aop:config>

    <bean id="fourAdviceBean" class="com.aspect.FourAdviceTest"></bean>
    <bean id="secondAdviceBean" class="com.aspect.SecondAdviceTest"></bean>
    <bean id="afterThrowingAdviceBean" class="com.aspect.RepairAspect"></bean>
    <bean id="hello" class="com.pojo.Hello"></bean>
    <bean id="world" class="com.pojo.World"></bean>
</beans>

4.3.5 Test启动类

package com.test;

import com.pojo.Hello;
import com.pojo.World;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {

    public static void main(String[] args) {
        //加载配置文件 applicationContext.xml
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
        //hello类
        Hello hello = (Hello) context.getBean("hello");
        hello.foo();
        hello.addUser("小明", "1000");
        try {
            hello._functionException(5);
        } catch (Exception e) {
        }

        //world类
        World world = (World) context.getBean("world");
        world.bar();
    }
}

4.3.6 运行结果

和使用注解方式基本一样(也就是打印语句的说法有少许不同),这里略过。

五、小结

本文分为三个部分,分别介绍AOP基础知识,以AspectJ AOP为例介绍静态AOP,以Spring AOP为例介绍动态AOP,其中以Spring AOP为重点,全文从AOP到AspectJ AOP,再到Spring AOP,希望对Java学习者有用。
天天打码,天天进步!

AspectJ工程代码:
https://download.csdn.net/download/qq_36963950/12182608

Spring AOP工程代码(注解方式):https://download.csdn.net/download/qq_36963950/12182612

Spring AOP工程代码(XML配置方式):https://download.csdn.net/download/qq_36963950/12182620

发布了185 篇原创文章 · 获赞 37 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_36963950/article/details/104433973