AOP:面向切面编程【底层使用动态代理实现】,就是在运行期间动态的将某段代码切入到方法的指定位置进行运行的编程方式

基本使用

  1. 使用AOP功能需要引入spring的aop以及aspects相关包

    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.6.RELEASE</version>
    </dependency> <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.0.6.RELEASE</version>
    </dependency> 
  2. 定义逻辑类
    public class TestService {
    public int service(int i, int j){
    System.out.println(".........service........");
    return i/j;
    }
    }
  3. 定义切面类
    @Aspect//声明该类是切面类
    public class MyAspectJ { /**
    * @Before
    * 前置通知:在目标方法(service)运行之前运行
    */
    @Before("execution(public int com.enjoy.study.cap13.TestService.service(int,int))")
    public void logStart(){
    System.out.println("@Before目标方法执行前");
    } /**
    * @After
    * 后置通知:在目标方法(service)运行结束之后运行,无论正常或异常结束
    */
    @After("execution(public int com.enjoy.study.cap13.TestService.service(int,int))")
    public void logEnd(){
    System.out.println("@After目标方法执行后");
    } /**
    * @AfterReturning
    * 返回通知:在目标方法(service)正常返回之后运行
    */
    @AfterReturning("execution(public int com.enjoy.study.cap13.TestService.service(int,int))")
    public void logReturn() {
    System.out.println("@AfterReturning目标方法执行后返回"); } /**
    * @AfterThrowing
    * 异常通知:在目标方法(service)出现异常后运行
    */
    @AfterThrowing("execution(public int com.enjoy.study.cap13.TestService.service(int,int))")
    public void logThrowing(){
    System.out.println("@AfterThrowing目标方法执行出现异常");
    } /**
    * @Around
    * 环绕通知:动态代理,手动执行目标方法运行joinPoint.procced(),最底层通知,手动指定执行目标方法,
    * 执行之前相当于前置通知, 执行之后相当于返回通知,其实就是通过反射执行目标对象的连接点处的方法
    * @param joinPoint : 用于环绕通知,通过procced()来执行目标方法
    * @return
    * @throws Throwable
    */
    @Around("execution(public int com.enjoy.study.cap13.TestService.service(int,int))")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("@Around目标方法执行前...........");
    Object object = joinPoint.proceed();
    System.out.println("@Around目标方法执行后...........");
    return object;
    }
    }
  4. 配置类
    @Configuration
    @EnableAspectJAutoProxy//用于开启spring注解的AOP模式,使得ioc容器可以辨别Aspect类
    public class AopMainConfig {
    @Bean
    public MyAspectJ myAspectJ(){
    return new MyAspectJ();
    } @Bean
    public TestService testService(){
    return new TestService();
    }
    }
  5. 测试

    public class TestAspectJ {
    @Test
    public void testM(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopMainConfig.class);
    TestService service = context.getBean(TestService.class);
    int result = service.service(4, 2);
    System.out.println("result = " + result);
    }
    } 
  6. 结果
    @Around目标方法执行前...........
    @Before目标方法执行前
    .........service........
    @Around目标方法执行后...........
    @After目标方法执行后
    @AfterReturning目标方法执行后返回
    result = 2
      

知识点

  • execution:用于匹配方法执行的连接点
  • ProceedingJoinPoint:应用于环绕通知,使用proceed()执行目标方法
  • @Aspect:声明当前类是切面类
  • @EnableAspectJAutoProxy:在配置类上使用该注解开启Spring基于注解的AOP模式,使得Spring的IOC容器可以识别哪个bean是Aspect切面bean  

优化

对于上面的切面类,每个通知的execution都一样,是冗余的,导致重复写了多次,可以抽取公共切入方法

@Pointcut("execution(public int com.enjoy.study.cap13.TestService.service(int,int))")
public void pointCut(){}

本类可以直接引用,其他类可以通过路径+方法引用

@Aspect
public class MyAspectJ { @Pointcut("execution(public int com.enjoy.study.cap13.TestService.service(int,int))")
public void pointCut(){}

/**
* @Before
* 前置通知:在目标方法(service)运行之前运行
*/
@Before("pointCut()")
public void logStart(){
System.out.println("@Before目标方法执行前");
} /**
* @After
* 后置通知:在目标方法(service)运行结束之后运行,无论正常或异常结束
*/
@After("com.enjoy.study.cap13.MyAspectJ.pointCut()")
public void logEnd(){
System.out.println("@After目标方法执行后");
} /**
* @AfterReturning
* 返回通知:在目标方法(service)正常返回之后运行
*/
@AfterReturning("pointCut()")
public void logReturn() {
System.out.println("@AfterReturning目标方法执行后返回"); } /**
* @AfterThrowing
* 异常通知:在目标方法(service)出现异常后运行
*/
@AfterThrowing("pointCut()")
public void logThrowing(){
System.out.println("@AfterThrowing目标方法执行出现异常");
} /**
* @Around
* 环绕通知:动态代理,手动执行目标方法运行joinPoint.procced(),最底层通知,手动指定执行目标方法,
* 执行之前相当于前置通知, 执行之后相当于返回通知,其实就是通过反射执行目标对象的连接点处的方法
* @param joinPoint : 用于环绕通知,通过procced()来执行目标方法
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("@Around目标方法执行前...........");
Object object = joinPoint.proceed();
System.out.println("@Around目标方法执行后...........");
return object;
}
}

我们还可以在切面类中得到方法名、方法的参数、结果、异常等信息

@Aspect
public class MyAspectJ { @Pointcut("execution(public int com.enjoy.study.cap13.TestService.service(int,int))")
public void pointCut(){} /**
* @Before
* 前置通知:在目标方法(service)运行之前运行
* @param joinPoint:应用于每个通知
*/
@Before(value = "pointCut()")
public void logStart(JoinPoint joinPoint){
System.out.println("@Before目标方法执行前,方法名:"+joinPoint.getSignature().getName()+
"方法参数:"+ Arrays.asList(joinPoint.getArgs()));
} /**
* @After
* 后置通知:在目标方法(service)运行结束之后运行,无论正常或异常结束
*/
@After("com.enjoy.study.cap13.MyAspectJ.pointCut()")
public void logEnd(){
System.out.println("@After目标方法执行后");
} /**
* @AfterReturning
* 返回通知:在目标方法(service)正常返回之后运行
*/
@AfterReturning(value = "pointCut()",returning = "result")
public void logReturn(Object result) {
System.out.println("@AfterReturning目标方法执行后返回结果:"+result); } /**
* @AfterThrowing
* 异常通知:在目标方法(service)出现异常后运行
*/
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void logThrowing(Exception ex){
System.out.println("@AfterThrowing目标方法执行出现异常信息:"+ex);
} /**
* @Around
* 环绕通知:动态代理,手动执行目标方法运行joinPoint.procced(),最底层通知,手动指定执行目标方法,
* 执行之前相当于前置通知, 执行之后相当于返回通知,其实就是通过反射执行目标对象的连接点处的方法
* @param joinPoint : 用于环绕通知,通过procced()来执行目标方法
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("@Around目标方法执行前...........");
Object object = joinPoint.proceed();
System.out.println("@Around目标方法执行后...........");
return object;
}
}

结果

@Around目标方法执行前...........
@Before目标方法执行前,方法名:service方法参数:[4, 2]
.........service........
@Around目标方法执行后...........
@After目标方法执行后
@AfterReturning目标方法执行后返回结果:2
result = 2

如果有异常会打印异常信息,例如将i/j中的j设置为0

@AfterThrowing目标方法执行出现异常信息:java.lang.ArithmeticException: / by zero

  

spring(六):spring中AOP的基本使用的更多相关文章

  1. Spring中AOP实现

    1.什么是SpringAOP 什么是aop:Aspect Oriented Programming的缩写,面向切面编程,通过预编译和动态代理实现程序功能的 统一维护的一种技术 主要功能:日志记录,性能 ...

  2. Spring中AOP原理,源码学习笔记

    一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...

  3. spring Aop中aop:advisor 与 aop:aspect的区别

    转载:http://blog.csdn.net/u011710466/article/details/52888277 在spring的配置中,会用到这两个标签.那么他们的区别是什么呢?       ...

  4. 关于Spring中AOP的理解

    AOP简介[理解][重点] 1.AOP(Aspect Oriented Programing)面向切面/方面编程 2.AOP隶属软件工程的范畴,指导开发人员如何制作开发软件,进行结构设计 3.AOP联 ...

  5. Spring中AOP简介与切面编程的使用

    Spring中AOP简介与使用 什么是AOP? Aspect Oriented Programming(AOP),多译作 "面向切面编程",也就是说,对一段程序,从侧面插入,进行操 ...

  6. 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)

    一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...

  7. Spring 中aop切面注解实现

    spring中aop的注解实现方式简单实例   上篇中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢.前面已经讲过aop的简单理解了,这里就不在赘述了. 注解方式实现aop我们 ...

  8. Spring中AOP相关源码解析

    前言 在Spring中AOP是我们使用的非常频繁的一个特性.通过AOP我们可以补足一些面向对象编程中不足或难以实现的部分. AOP 前置理论 首先在学习源码之前我们需要了解关于AOP的相关概念如切点切 ...

  9. 六、Spring之初步认识AOP

    Spring之初步认识AOP [1]AOP概览 什么是AOP?(来自百度) ​ 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行 ...

随机推荐

  1. hdu4348 To the moon (可持久化线段树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4348 题目大意:给定含有n个数的序列,有以下四种操作 1.C l r d:表示对区间[l,r]中的数加 ...

  2. 洛谷 P2783 有机化学之神偶尔会做作弊(Tarjan,LCA)

    题目背景 LS中学化学竞赛组教练是一个酷爱炉石的人. 有一天他一边搓炉石一边监考,而你作为一个信息竞赛的大神也来凑热闹. 然而你的化竞基友却向你求助了. “第1354题怎么做”<--手语 他问道 ...

  3. Springboot 默认cache

    1:Springboot 默认缓存为ConcurrentMapCacheManager(spring-context) 2:再启动类上开启缓存 @SpringBootApplication //相当于 ...

  4. Java 逻辑运算符相关解析

    问:定简单说说 Java 中 & 与 && 有什么区别?| 与 || 呢? 答:& 是位运算符,&& 是布尔逻辑运算符,| 与 || 类似同理.在进行逻 ...

  5. map接口、hashmap常用方法

    注意:map中键不能重复(是否重复是根据equals方法判断),否则新的会覆盖为旧的 范例: public class TestMap { public static void main(String ...

  6. LA 6834 Shopping

    题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...

  7. 033:DTL常用过滤器(2)

    date过滤器: date过滤器:将一个日期按照指定的格式,格式化成字符串.示例代码如下: views.py: from datetime import datetime def cur_date(r ...

  8. Linux安装mysql5.6.33

    1.下载mysql安装包: 下载地址:http://dev.mysql.com/downloads/mysql/5.6.html#downloads 下载版本:我这里选择的5.6.33,通用版,lin ...

  9. React笔记01——React开发环境准备

    1 React简介 2013年由Facebook推出,代码开源,函数式编程.目前使用人数最多的前端框架.健全的文档与完善的社区. 官网:reactjs.org 阅读文档:官网中的Docs React ...

  10. 使用ant编译Android APK

    ANT —— Apache Ant is a Java library and command-line tool that help building software. 1. 部署ANT的使用环境 ...