Spring 中 AOP 的实现

Spring 中 AOP 的实现

AOP 称为面向切面编程,在程序开发中,AOP 技术可以在不改变原有方法代码的情况下,把逻辑直接插入到方法上。Spring AOP 的大致原理主要依靠的是动态代理,你以为操作的这个类,实际上操作的是它的一个代理类。动态代理用到两种机制,一直是基于 JDK 的动态代理,另一种是基于 CGLib 的动态代理。网上有很多介绍这种原理的文章,深入了解可以参考这些文章。

AOP 中有很多术语,比如连接点,切入点,有必要解释一下:

术语解释

Joinpoint(连接点): 类里面可以被增强的方法,这些方法称为连接点。

Pointcut(切入点): 类中有很多连接点,但是我们真正增强的那个连接点,即那个方法,称之为所谓切入点。

Advice(通知/增强): 通知/增强,指的是增强某个方法而实现的逻辑。通知/增强 有几个类型。分为:

  • 前置增强,在方法之前执行。
  • 后置增强,在方法之后执行。
  • 异常增强,在方法异常的时候执行。
  • 最终增强,在后置之后执行。
  • 环绕增强,在方法之前和之后执行。
  • Aspect(切面): 把增强应用到具体方法上,这个过程称之为切面。

捋一下 AOP 的相关术语,白话的说如下:

一个类有好多个连接点(方法),当去增强某个连接点时,这个连接点就称之为切入点。有好几种方式增强某个切入点(就是扩展一些逻辑功能),分别是:

  • 方法执行之前执行。
  • 方法执行 之后执行 。
  • 在方法异常的时候执行。
  • 在后置之后执行,无论目标方法是否出现异常都会 执行。
  • 在方法之前和之后执行。

这个扩展功能的过程又称之为切面。

代码演示

使用 AOP 的时候,需要配置 AOP,分好几步。

1,配置切入点,就是要对哪个方法增强。用到 execution 表达式写法。

2,实现需要增强的逻辑,这个逻辑通常是写在某个方法中,这个方法可以用来增强切入点。

3,配置切面,即配置一下,把增强和切入点关联起来。确定了哪个方法需要哪方面的增强,增强方式是前置增强,或者后置增强,或者其他类型的增强。

配置可以是用 XML 配置,也可以基于注解配置。这里仅演示基于注解配置,本质上都是一样的。

配置文件:

bean1.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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
    <!-- 开启注解扫描 -->
	<context:component-scan base-package="com.learn" />
	<!-- 开启aop注解方式,此步骤s不能少,这样java类中的aop注解才会生效 -->
    <aop:aspectj-autoproxy/>	
</beans>

需要被增强的方法,即所谓的切入点:

package com.learn.demo;
import org.springframework.stereotype.Component;
 
@Component
public class HelloWorld {
    public void sayHello(){
        System.out.println("Hello!");
    }
}

配置 AOP 的类,AOP 的增强功能在这里实现 。

package com.learn.demo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
@Component
@Aspect
public class AopUtils {
 
        //前置通知
	@Before("execution(* com.learn.demo.HelloWorld.*(..))")
	public void before() {
		System.out.println("before doing.....");
	}
 
	// 后置通知
	@After("execution(* com.learn.demo.HelloWorld.*(..))")
	public void after() {
		System.out.println("after doing.....");
	}
 
	// 环绕通知。注意要有ProceedingJoinPoint参数传入。
	@Around("execution(* com.learn.demo.HelloWorld.*(..))")
	public void around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("around..begin");
		pjp.proceed();// 执行方法
		System.out.println("around..end");
	}
 
}

上面的代码中,有形如 execution 这样的表达式,这称之为切入点表达式。其语法格式:

    execution(< 访问修饰符>?< 返回类型>< 方法名>(< 参数>)< 异常>)

这个表达式和定义方法的写法很类似,举个例子:

  • 匹配精确的方法:execution(public String org.baeldung.dao.FooDao.findById(Long))。
  • 匹配指定包下的所有方法:execution(* com.myjava.dao.*(..)) 不包含子包。

上述代码中,每个注解上都有 execution 表达式。其实可以定义一个切入点函数,用该函数代替每个 execution 表达式。

package com.learn.demo;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
 
@Component
@Aspect
public class AopUtils {
 
	//定义切点
    @Pointcut("execution(* com.learn.demo.HelloWorld.*(..))")
    public int point(){return 0;}
 
    @Before("point()")
    public void before(){
        System.out.println("before doing.....");
    }
    //后置通知
    @After("point()")
    public void after(){
        System.out.println("after doing.....");
    }
    //环绕通知。注意要有ProceedingJoinPoint参数传入。
    @Around("point()")
    public void around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("around..begin");
        pjp.proceed();//执行方法
        System.out.println("around..end");
    }
}

调用代码如下:

package com.learn;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.learn.demo.HelloWorld;
 
 
public class Main {
 
	public static void main(String[] args) {
        ApplicationContext context = 
         	   new ClassPathXmlApplicationContext("bean1.xml");
        HelloWorld helloWorld = ( HelloWorld) context.getBean("helloWorld");
        helloWorld.sayHello();
    }
}

运行结果如下:

around..begin before doing….. Hello! around..end after doing…..

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏李家的小酒馆

AOP的具体实践-简化结果返回的处理

  以前学习Spring的时候着重学习过AOP概念,但是一直也没有用上,唯一碰到过的就是Spring内置的事务管理。现在碰到过一些结果后面的操作适合用到,所以这...

1192
来自专栏Kiba518

【我们一起写框架】C#的AOP框架

因为,AOP单独设计的框架几乎是无法使用的。普遍的情况是,AOP要是和其他设计模式结合在一起使用。

1133
来自专栏xingoo, 一个梦想做发明家的程序员

【Spring实战】—— 9 AOP环绕通知

假如有这么一个场景,需要统计某个方法执行的时间,如何做呢?   典型的会想到在方法执行前记录时间,方法执行后再次记录,得出运行的时间。 如果采用Sp...

2305
来自专栏微信公众号:Java团长

Spring AOP的实现原理

AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点...

1002
来自专栏Coding迪斯尼

VUE+WebPack游戏设计:实现子弹发射击打外星人效果

1013
来自专栏青青天空树

spring基础(3:面向切面编程)

  Spring的基础是IOC和AOP,前面两节对IOC和DI做了简单总结,这里再对AOP进行一个学习总结,Spring基础就算有一个初步了解了。

902
来自专栏cs

spring aop概念以及jdk的动态代理

AOP(Aspect-Oriented Programming),又称为面向切面编程。作为一种编程思想,AOP是 OOP(Object-Oriented Pro...

1034
来自专栏Jed的技术阶梯

Java设计模式之模板方法模式

假设我们现在要造一批悍马汽车,悍马汽车有两个系列H1和H2,首先不考虑任何设计模式,看看设计的类图:

1183
来自专栏黑泽君的专栏

day34_Spring学习笔记_02

1.2.2.2、切面类   MyAspect.java的代码同上 1.2.1.2、切面类 代码,这里不再赘述!

894
来自专栏一枝花算不算浪漫

[Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.

3368

扫码关注云+社区

领取腾讯云代金券