Spring AOP用法详解

什么是AOP

AOP:Aspect Oriented Programming,中文翻译为”面向切面编程“。面向切面编程是一种编程范式,它作为OOP面向对象编程的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、权限控制、缓存控制、日志打印等等。AOP采取横向抽取机制,取代了传统纵向继承体系的重复性代码
AOP把软件的功能模块分为两个部分:核心关注点横切关注点。业务处理的主要功能为核心关注点,而非核心、需要拓展的功能为横切关注点。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点进行分离
使用AOP有诸多好处,如:
1.集中处理某一关注点/横切逻辑
2.可以很方便的添加/删除关注点
3.侵入性少,增强代码可读性及可维护性

AOP的术语

1.Join point(连接点)
Spring 官方文档的描述:

A point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.

程序执行过程中的一个点,如方法的执行或异常的处理。在Spring AOP中,连接点总是表示方法的执行。通俗的讲,连接点即表示类里面可以被增强的方法
2.Pointcut(切入点)

Pointcut are expressions that is matched with join points to determine whether advice needs to be executed or not. Pointcut uses different kinds of expressions that are matched with the join points and Spring framework uses the AspectJ pointcut expression language

切入点是与连接点匹配的表达式,用于确定是否需要执行通知。切入点使用与连接点匹配的不同类型的表达式,Spring框架使用AspectJ切入点表达式语言。我们可以将切入点理解为需要被拦截的Join point
3.Advice(增强/通知)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知和环绕通知(切面要完成的功能)
4.Aspect(切面)
Aspect切面表示Pointcut(切入点)和Advice(增强/通知)的结合

Spring AOP用法

示例代码

/**
 * 设置登录用户名
 */
public class CurrentUserHolder {

    private static final ThreadLocal<String> holder = new ThreadLocal<>();

    public static String get() {
        return holder.get();
    }
    public static void set(String user) {
        holder.set(user);
    }
}
/**
 * 校验用户权限
 */
@Service("authService")
public class AuthServiceImpl implements AuthService {

    @Override
    public void checkAccess() {
        String user = CurrentUserHolder.get();

        if(!"admin".equals(user)) {
            throw new RuntimeException("该用户无此权限!");
        }
    }
}
/**
 * 业务逻辑类
 */
@Service("productService")
public class ProductServiceImpl implements ProductService {

    @Autowired
    private AuthService authService;

    @Override
    public Long deleteProductById(Long id) {
        System.out.println("删除商品id为" + id + "的商品成功!");
        return id;
    }

    @Override
    public void deleteProductByName(String name) {
        System.out.println("删除商品名称为" + name + "的商品成功!");
    }

    @Override
    public void selectProduct(Long id) {
        if("100".equals(id.toString())) {
            System.out.println("查询商品成功!");
        } else {
            System.out.println("查询商品失败!");
            throw new RuntimeException("该商品不存在!");
        }
    }
}

1.使用within表达式匹配包类型

//匹配ProductServiceImpl类里面的所有方法
@Pointcut("within(com.aop.service.impl.ProductServiceImpl)")
public void matchType() {}

//匹配com.aop.service包及其子包下所有类的方法
@Pointcut("within(com.aop.service..*)")
public void matchPackage() {}

2.使用this、target、bean表达式匹配对象类型

//匹配AOP对象的目标对象为指定类型的方法,即ProductServiceImpl的aop代理对象的方法
@Pointcut("this(com.aop.service.impl.ProductServiceImpl)")
public void matchThis() {}

//匹配实现ProductService接口的目标对象
@Pointcut("target(com.aop.service.ProductService)")
public void matchTarget() {}

//匹配所有以Service结尾的bean里面的方法
@Pointcut("bean(*Service)")
public void matchBean() {}

3.使用args表达式匹配参数

//匹配第一个参数为Long类型的方法
@Pointcut("args(Long, ..) ")
public void matchArgs() {}

4.使用@annotation、@within、@target、@args匹配注解

//匹配标注有AdminOnly注解的方法
@Pointcut("@annotation(com.aop.annotation.AdminOnly)")
public void matchAnno() {}

//匹配标注有Beta的类底下的方法,要求annotation的Retention级别为CLASS
@Pointcut("@within(com.google.common.annotations.Beta)")
public void matchWithin() {}

//匹配标注有Repository的类底下的方法,要求annotation的Retention级别为RUNTIME
@Pointcut("@target(org.springframework.stereotype.Repository)")
public void matchTarget() {}

//匹配传入的参数类标注有Repository注解的方法
@Pointcut("@args(org.springframework.stereotype.Repository)")
public void matchArgs() {}

5.使用execution表达式
execution表达式是我们在开发过程中最常用的,它的语法如下:

modifier-pattern:用于匹配public、private等访问修饰符
ret-type-pattern:用于匹配返回值类型,不可省略
declaring-type-pattern:用于匹配包类型
modifier-pattern(param-pattern):用于匹配类中的方法,不可省略
throws-pattern:用于匹配抛出异常的方法
代码示例:

@Component
@Aspect
public class SecurityAspect {

    @Autowired
    private AuthService authService;

    //匹配com.aop.service.impl.ProductServiceImpl类下的方法名以delete开头、参数类型为Long的public方法
    @Pointcut("execution(public * com.aop.service.impl.ProductServiceImpl.delete*(Long))")
    public void matchCondition() {}

    //使用matchCondition这个切入点进行增强
    @Before("matchCondition()")
    public void before() {
        System.out.println("before 前置通知......");
        authService.checkAccess();
    }
}

单元测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootApplicationTests {

    @Autowired
    private ProductService productService;

    @Test
    public void contextLoads() {
        //设置用户名
        CurrentUserHolder.set("hello");

        productService.selectProduct(100L);
        productService.deleteProductByName("衣服");
        productService.deleteProductById(100L);
    }
}

运行结果(只有deleteProductById方法拦截成功):

查询商品成功!
删除商品名称为衣服的商品成功!
before 前置通知......

java.lang.RuntimeException: 该用户无此权限!

    at com.aop.service.impl.AuthServiceImpl.checkAccess(AuthServiceImpl.java:15)
    at com.aop.security.SecurityAspect.before(SecurityAspect.java:50)

可以在多个表达式之间使用连接符匹配多个条件, 如使用||表示“或”,使用 &&表示“且”

//匹配com.aop.service.impl.ProductServiceImpl类下方法名以select或delete开头的所有方法
@Pointcut("execution(* com.aop.service.impl.ProductServiceImpl.select*(..)) || " +
            "execution(* com.aop.service.impl.ProductServiceImpl.delete*(..))")
public void matchCondition() {}

//使用matchCondition这个切入点进行增强
@Before("matchCondition()")
public void before() {
   System.out.println("before 前置通知......");
   authService.checkAccess();
}

单元测试:

    @Test
    public void contextLoads() {
        CurrentUserHolder.set("admin");

        productService.selectProduct(100L);
        productService.deleteProductByName("衣服");
        productService.deleteProductById(100L);
    }

运行结果(所有方法均拦截成功):

before 前置通知......
查询商品成功!
before 前置通知......
删除商品名称为衣服的商品成功!
before 前置通知......
删除商品id为100的商品成功!

6.Advice注解
Advice注解一共有五种,分别是:
[email protected]前置通知
前置通知在切入点运行前执行,不会影响切入点的逻辑
[email protected]后置通知
后置通知在切入点正常运行结束后执行,如果切入点抛出异常,则在抛出异常前执行
[email protected]异常通知
异常通知在切入点抛出异常前执行,如果切入点正常运行(未抛出异常),则不执行
[email protected]返回通知
返回通知在切入点正常运行结束后执行,如果切入点抛出异常,则不执行
[email protected]环绕通知
环绕通知是功能最强大的通知,可以在切入点执行前后自定义一些操作。环绕通知需要负责决定是继续处理join point(调用ProceedingJoinPoint的proceed方法)还是中断执行
示例代码:

    //匹配com.aop.service.impl.ProductServiceImpl类下面的所有方法
    @Pointcut("execution(* com.aop.service.impl.ProductServiceImpl.*(..))")
    public void matchAll() {}

    @Around("matchAll()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object result = null;
        authService.checkAccess();
        System.out.println("befor 在切入点执行前运行");

        try{
            result = joinPoint.proceed(joinPoint.getArgs());//获取参数
            System.out.println("after 在切入点执行后运行,result = " + result);
        } catch (Throwable e) {
            System.out.println("after 在切入点执行后抛出exception运行");
            e.printStackTrace();
        } finally {
            System.out.println("finally......");
        }

       return result;
    }

单元测试:

    @Test
    public void contextLoads() {
        CurrentUserHolder.set("admin");

        productService.deleteProductById(100L);
        productService.selectProduct(10L);
    }

运行结果:

before 在切入点执行前运行
删除商品id为100的商品成功!
after 在切入点执行后运行,result = 100
finally......
before 在切入点执行前运行
查询商品失败!
after 在切入点执行后抛出exception运行
java.lang.RuntimeException: 该商品不存在!
    at com.aop.service.impl.ProductServiceImpl.selectProduct(ProductServiceImpl.java:41)
    at com.aop.service.impl.ProductServiceImpl$$FastClassBySpringCGLIB$$f17a76a2.invoke(<generated>)
finally......

在执行ProceedingJoinPoint对象的proceed方法前相当于Before前置通知;执行proceed方法相当于运行切入点(同时可以获取参数);在方法执行之后相当于After后置通知,如果运行切入点抛出异常,则catch中的内容相当于AfterThrowing异常通知;finally中的内容无论切入点是否抛出异常,都将执行

原文地址:https://www.cnblogs.com/liantdev/p/10125284.html

时间: 2024-10-19 07:34:17

Spring AOP用法详解的相关文章

Spring中AOP实例详解

Spring中AOP实例详解 需要增强的服务 假如有以下service,他的功能很简单,打印输入的参数并返回参数. @Service public class SimpleService { public String getName(String name) { System.out.println(get name is: + name); return name; } } 定义切面和切点 @Component @Aspect public class L ogAspect { // 定义切

spring框架 AOP核心详解

AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子. 一 AOP的基本概念 (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知 (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用 (3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around

一份spring配置文件及其详解

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/axu20/archive/2009/10/14/4668188.aspx 1.基本配置:<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/

Spring——jar包详解(转)

Spring——jar包详解 org.springframework.aop ——Spring的面向切面编程,提供AOP(面向切面编程)的实现 org.springframework.asm——spring 2.5.6的时候需要asm jar包,spring3.0开始提供它自己独立的asm jar包 org.springframework.aspects——Spring提供的对AspectJ框架的整合 org.springframework.beans——所有应用都用到,包含访问配置文件,创建和

Spring Cache抽象详解

缓存简介 缓存,我的理解是:让数据更接近于使用者:工作机制是:先从缓存中读取数据,如果没有再从慢速设备上读取实际数据(数据也会存入缓存):缓存什么:那些经常读取且不经常修改的数据/那些昂贵(CPU/IO)的且对于相同的请求有相同的计算结果的数据.如CPU--L1/L2--内存--磁盘就是一个典型的例子,CPU需要数据时先从L1/L2中读取,如果没有到内存中找,如果还没有会到磁盘上找.还有如用过Maven的朋友都应该知道,我们找依赖的时候,先从本机仓库找,再从本地服务器仓库找,最后到远程仓库服务器

转载:Spring MVC配置详解

以下内容引自:http://www.cnblogs.com/superjt/p/3309255.html spring MVC配置详解 现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过要想灵活运用Spring MVC来应对大多数的Web开发,就必须要掌握它的配置及原理. 一.Spring MVC环境搭建:(Spring 2.5.6 + Hiber

js的offsetParent属性用法详解

js的offsetParent属性用法详解:此属性是javascript中较为常用的属性,对于它的良好掌握也是非常有必要的,下面就通过代码实例介绍一下它的用法,希望能够给需要的朋友带来一定的帮助.一.基本介绍:此属性可以返回距离指定元素最近的采用定位(position属性值为fixed.relative或者absolute)父级元素,如果父级元素中没有采用定位的元素,则返回body对象的引用.语法结构: obj.offsetParent 二.代码实例: <!DOCTYPE html> <

python处理word文件:win32com用法详解

目标:用python处理doc文件 方法:引入win32com模块 ************************************************************************** 一.安装 ************************************************************************** 首先要先下载安装win32com模块(起先在linux下装不成功,后在windows下面成功了...) 下载地址:http

jQuery 事件用法详解

jQuery 事件用法详解 目录 简介 实现原理 事件操作 绑定事件 解除事件 触发事件 事件委托 事件操作进阶 阻止默认事件 阻止事件传播 阻止事件向后执行 命名空间 自定义事件 事件队列 jquery中文文档 简介 jquery 之所以成为最受欢迎的前端库,很大一部分是得益于它的事件具有良好的语义,优秀的兼容性,并且便于管理和扩展. 在这里我会介绍 jquery 事件的一些比较基础的用法. 实现原理 jquery 事件脱胎于浏览器的 addEventListener (W3) 和 attac