热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

SpringAOP切面@Around注解的用法说明

这篇文章主要介绍了SpringAOP切面@Around注解的用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

@Around注解可以用来在调用一个具体方法前和调用后来完成一些具体的任务。

比如我们想在执行controller中方法前打印出请求参数,并在方法执行结束后来打印出响应值,这个时候,我们就可以借助于@Around注解来实现;

再比如我们想在执行方法时动态修改参数值等

类似功能的注解还有@Before等等,用到了Spring AOP切面思想,Spring AOP常用于拦截器、事务、日志、权限验证等方面。

完整演示代码如下:

需要说明的是,在以下例子中,我们即可以只用@Around注解,并设置条件,见方法run1();也可以用@Pointcut和@Around联合注解,见方法pointCut2()和run2(),这2种用法是等价的。如果我们还想利用其进行参数的修改,则调用时必须用joinPoint.proceed(Object[] args)方法,将修改后的参数进行回传。如果用joinPoint.proceed()方法,则修改后的参数并不会真正被使用。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; 
import javax.persistence.EntityManager;
 
/**
 * 控制器切面
 *
 * @author lichuang
 */
 
@Component
@Aspect
public class ControllerAspect {
 
 private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class);
 
 @Autowired
 private EntityManager entityManager;
 
 /**
  * 调用controller包下的任意类的任意方法时均会调用此方法
  */
 @Around("execution(* com.company.controller.*.*(..))")
 public Object run1(ProceedingJoinPoint joinPoint) throws Throwable {
  //获取方法参数值数组
  Object[] args = joinPoint.getArgs();
  //得到其方法签名
  MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  //获取方法参数类型数组
  Class[] paramTypeArray = methodSignature.getParameterTypes();
  if (EntityManager.class.isAssignableFrom(paramTypeArray[paramTypeArray.length - 1])) {
   //如果方法的参数列表最后一个参数是entityManager类型,则给其赋值
   args[args.length - 1] = entityManager;
  }
  logger.info("请求参数为{}",args);
  //动态修改其参数
  //注意,如果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args)
  Object result = joinPoint.proceed(args);
  logger.info("响应结果为{}",result);
  //如果这里不返回result,则目标对象实际返回值会被置为null
  return result;
 }
 
 @Pointcut("execution(* com.company.controller.*.*(..))")
 public void pointCut2() {}
 
 @Around("pointCut2()")
 public Object run2(ProceedingJoinPoint joinPoint) throws Throwable {
  //获取方法参数值数组
  Object[] args = joinPoint.getArgs();
  //得到其方法签名
  MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  //获取方法参数类型数组
  Class[] paramTypeArray = methodSignature.getParameterTypes();
  if (EntityManager.class.isAssignableFrom(paramTypeArray[paramTypeArray.length - 1])) {
   //如果方法的参数列表最后一个参数是entityManager类型,则给其赋值
   args[args.length - 1] = entityManager;
  }
  logger.info("请求参数为{}",args);
  //动态修改其参数
  //注意,如果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args)
  Object result = joinPoint.proceed(args);
  logger.info("响应结果为{}",result);
  //如果这里不返回result,则目标对象实际返回值会被置为null
  return result;
 }
}

补充:Spring Aop实例(AOP 如此简单)@Aspect、@Around 注解方式配置

IoC相关的基本内容告一段落,本次介绍Spring的第二个特性,AOP,面向切面编程,术语听起来比较不容易理解,没关系,一切尽在实例中,让我们看一个简单的实例,就能明白。

实例

项目工程目录结构和代码获取地址

获取地址(版本Log将会注明每一个版本对应的课程)

https://github.com/laiyijie/SpringLearning

目录结构

运行工程

运行具有Main函数的 App.java

得到如下输出

method start time:1480223298250
userHello
method end time:1480223299250

项目详解

从App.java入手

App.java

package me.laiyijie.demo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import me.laiyijie.demo.service.HelloInterface;
public class App {
 public static void main(String[] args) {
  ClassPathXmlApplicationContext cOntext= new ClassPathXmlApplicationContext("root-context.xml");
  HelloInterface userService = context.getBean(HelloInterface.class);
  userService.sayHello();
  context.close();
 }
}

调用的是HelloInterface的sayHello方法

HelloInterface.java

package me.laiyijie.demo.service;
public interface HelloInterface{
 
 void sayHello();
 
} 

其实现类为UserServiceImpl.java

UserServiceImpl.java

package me.laiyijie.demo.service;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements HelloInterface {
 public void sayHello() {
  try {
   Thread.sleep(1000);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println("userHello");
 }
 
} 

诶?情况跟我们看到的代码有出入?

sayHello 应该只输出 userHello,前后两行输出从何出现?

在Main函数中找不到一点儿线索!

这就是AOP的一个强大特性:

无侵入性,不改变原有的代码,却能增加功能!

那么究竟是如何增加功能的呢?

让我们看看TimeMonitor.java

TimeMonitor.java

package me.laiyijie.demo.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Service;
@Service
@Aspect
public class TimeMonitor {
 @Around("execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..))")
 public void monitorAround(ProceedingJoinPoint pjp) throws Throwable {
  System.out.println("method start time:" + System.currentTimeMillis());
  Object re = pjp.proceed();
  System.out.println("method end time:" + System.currentTimeMillis());
 }
}

终于看到了 method start time:1480223298250 和 method end time:1480223299250这两行输出是从哪儿出现的了!

让我们来仔细解读一下这个类

类有两个注释,分别是@Service和@Aspect,第一个注解是使得TimeMonitor受Spring托管并实例化。@Aspect就是使得这个类具有AOP功能(你可以这样理解)两个注解缺一不可

类里面只有一个方法,名字叫做monitorAroud,其实就是为了检测函数执行时间的!

那么关键点来了,两个输出语句是怎么插入到sayHello方法的前后的呢!

看这个注解:

@Around("execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..))")

@Around表示包围一个函数,也就是可以在函数执行前做一些事情,也可以在函数执行后做一些事情

execution(* me.laiyijie.demo.service.UserServiceImpl.sayHello(..)) 

这个比较好理解,就是使用表达式的方式指定了要对哪个函数进行包围!(除了execution以外还有很多,可以搜索AspectJ语法来学习)

也就是说,这个注解完整的说明了,应该在函数的什么位置插入变化,也就是所谓的切点

之后是函数的定义:

public Object monitorAround(ProceedingJoinPoint pjp)

这里引入了ProceedingJoinPoint,在使用了@Around之后可以带入这个参数,代表的其实就是sayHello这个函数,不过做了一些封装

而 Object re = pjp.proceed(); 就是相当于执行了 sayHello方法!

剩下的代码就不用过多解释了,就是在执行这个函数的前后分别进行了系统时间的获取。

我们把这个函数体,也就是定义了要做那些事情的代码,称作增强

而包含切点和增强结合起来就称作切面

面向切面由此而来!

Spring AOP 开启需要的配置

需要配置两项

1、pom.xml增加依赖(因为要用到AOP还需要不同的JAR包)

2、root-context.xml中增加切面相关配置

root-context.xml

<&#63;xml version="1.0" encoding="UTF-8"&#63;>

  
 
 

root-context.xml 增加了两行

1、xmlns:aop="http://www.springframework.org/schema/aop"

代表加入命名空间

2、

使用1中引入的aop命名空间开起自动代理(自动代理具体含义后续慢慢解释,简单的理解就是AOP的实现是依靠自动代理实现的)

pom.xml


 4.0.0
 me.laiyijie
 demo
 0.0.1-SNAPSHOT
 jar
 
  
  
   org.springframework
   spring-context
   4.3.2.RELEASE
  
  
  
  
   org.aspectj
   aspectjweaver
   1.8.9
  
 

增加了一个依赖

AspectJ 一个强大的AOP框架,也就是@Aspect和@Around以及ProceedingJoinPoint这些注解和方法的提供者

小结

增强:定义了应该怎么把额外的动作加入到指定函数中

切点:定义了你应该把增强插入到哪个函数的什么位置

切面:切点和增强组合起来的称呼

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。


推荐阅读
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • Android LED 数字字体的应用与实现
    本文介绍了一种适用于 Android 应用的 LED 数字字体(digital font),并详细描述了其在 UI 设计中的应用场景及其实现方法。这种字体常用于视频、广告倒计时等场景,能够增强视觉效果。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 扫描线三巨头 hdu1928hdu 1255  hdu 1542 [POJ 1151]
    学习链接:http:blog.csdn.netlwt36articledetails48908031学习扫描线主要学习的是一种扫描的思想,后期可以求解很 ... [详细]
  • 本文详细介绍了如何在 Spring Boot 应用中通过 @PropertySource 注解读取非默认配置文件,包括配置文件的创建、映射类的设计以及确保 Spring 容器能够正确加载这些配置的方法。 ... [详细]
  • This document outlines the recommended naming conventions for HTML attributes in Fast Components, focusing on readability and consistency with existing standards. ... [详细]
  • 本文详细介绍了Java中org.w3c.dom.Text类的splitText()方法,通过多个代码示例展示了其实际应用。该方法用于将文本节点在指定位置拆分为两个节点,并保持在文档树中。 ... [详细]
  • 本文详细介绍了 Apache Jena 库中的 Txn.executeWrite 方法,通过多个实际代码示例展示了其在不同场景下的应用,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 在现代网络环境中,两台计算机之间的文件传输需求日益增长。传统的FTP和SSH方式虽然有效,但其配置复杂、步骤繁琐,难以满足快速且安全的传输需求。本文将介绍一种基于Go语言开发的新一代文件传输工具——Croc,它不仅简化了操作流程,还提供了强大的加密和跨平台支持。 ... [详细]
  • 解决微信电脑版无法刷朋友圈问题:使用安卓远程投屏方案
    在工作期间想要浏览微信和朋友圈却不太方便?虽然微信电脑版目前不支持直接刷朋友圈,但通过远程投屏技术,可以轻松实现在电脑上操作安卓设备的功能。 ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
  • 从零开始构建完整手机站:Vue CLI 3 实战指南(第一部分)
    本系列教程将引导您使用 Vue CLI 3 构建一个功能齐全的移动应用。我们将深入探讨项目中涉及的每一个知识点,并确保这些内容与实际工作中的需求紧密结合。 ... [详细]
  • 本报告涵盖了个人博客账号和码云账号的注册过程,以及对网络工程专业学习的反思与展望。通过回顾初入大学时的专业选择,分析当前的专业知识和技能水平,并对未来的职业规划进行了详细讨论。 ... [详细]
author-avatar
峡谷人123_742
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有