介绍

概念

面向切面编程AOP与面向对象编程OOP有所不同,AOP不是对OOP的替换,而是对OOP的一种补充,AOP增强了OOP。

假设我们有几个业务代码,都调用了某个方法,按照OOP的思想,我们就会将此方法封装在一个类中,之后通过对象.方法名调用

我们可以看作我们的业务代码被其他代码入侵或者是业务代码被其他与业务不相关的代码入侵了

这个时候,如果我们使用AOP进行编写代码,我们的业务代码就可以不需要写其他与业务相关的代码,这样就可以保证业务代码的纯洁性

AOP运行流程

通过配置文件,给各个业务方法标识切入点(PointCut),即切入点方法。

之后当程序运行到切入点方法的时候,就会发出一个通知(Advice),去通知执行某个切面方法(Aspect)

专业术语

描述
Aspect 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。
Join point 在你的应用程序中它代表一个点,你可以在插件 AOP 方面。你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。
Advice 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。
Pointcut 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。
Introduction 引用允许你添加新方法或属性到现有的类中。
Target object 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。
Weaving Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。

Advice通知

通知 类型
前置通知(Before Advice) 在切入点方法执行之前,执行通知
环绕通知(Around Advice) 在切入点方法执行的整个过程都可以执行通知
后置通知(After Returning Advice) 在切入点方法执行之后,只有在方法成功执行时,才能执行通知。
最终通知(After Finally Advices) 在一个方法执行之后,不管是方法是否成功执行 ,执行通知
异常通知(After Throwing Advice) 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。

PS:其实,这些通知就是相当于你可以在业务方法的执行前(前置通知)、执行中(环绕通知)、执行成功之后(后置通知)、发生异常(异常通知)、不管方法是发生异常还是执行成功(最终通知),执行某些与业务功能无关的功能代码。

这样就可以降低业务功能代码的入侵和污染

下面使用两种不同的方式来实现一个方法日志打印的简单例子

后置通知例子

下面的通知是基于xml配置的

1.添加依赖

除了之前的spring的jar包,还需要两个jar包,aopalliance.jaraspectjweaver.jar

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>RELEASE</version>
</dependency>
<!-- aop需要的jar -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>RELEASE</version>
</dependency>

2.业务代码

我编写了一个TeacherDao类,里面只有add和delete方法

package com.wan;

/**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:34
* @description
*/
public class TeacherDao {
public void add(Teacher teacher) {
System.out.println("往数据库中插入一条数据");
} public void delete(Teacher teacher) {
System.out.println("从数据库中删除一条数据");
}
}

3.编写MyLogging.java

前面说过了通知具有五种类型,我们根据需要,选择合适的通知类型,让某个类实现通知对应的接口,这里其实就是相当于编写切面方法

通知类型 接口 接口方法 接口方法参数说明
前置通知 org.springframework.aop.MethodBeforeAdvice before(Method method, Object[] args, Object target) method是方法,args是方法的参数,target是目标对象
环绕通知 org.aopalliance.intercept.MethodInterceptor invoke(MethodInvocation invocation) invocation对象中包含有method,方法参数和目标对象
后置通知 org.springframework.aop.AfterReturningAdvice afterReturning(Object returnValue, Method method, Object[] args, Object target) returnValue是方法的返回值,其他的参数和前置通知一样
最终通知 org.springframework.aop.AfterAdvice
异常通知 org.springframework.aop.ThrowsAdvice

我们日志输出,选择后置通知,也就是方法执行完成之后调用

MyLogging.java

package com.wan;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; /**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
public class MyLogging implements AfterReturningAdvice { @Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
String methodName = method.getName();//方法名
int size = args.length;//参数个数
System.out.println("调用了"+target+"的"+methodName+"方法,该方法的参数个数有"+size+"个");
}
}

4.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="mylog" class="com.wan.MyLogging"/>
<bean id="teacherdao" class="com.wan.TeacherDao"/> <aop:config>
<aop:pointcut id="mypointcut" expression="execution(public void add(com.wan.Teacher))"/>
<aop:advisor advice-ref="mylog" pointcut-ref="mypointcut"/>
</aop:config>
</beans>

这里和之前一样,也需要引用aop命名空间,IDEA可以智能帮我们导入,输入<aop:,之后就会弹出提示

各元素和属性说明:

子元素/属性 含义
aop:pointcut 切入点,当执行当切入点方法的时候,就会根据通知(Advice)的类型,从而执行非业务功能的代码
id 切入点的唯一表示,下面pointcut-ref属性需要引用此id
expression 表达式,只要是符合此表达式的方法,都会被当作切入点
aop:advisor 通知
pointcut-ref 引用切入点的id
advice-ref 引用切入点接口类的bean的id

补充,关于expression的例子:

例子 说明
public boolean addTeacher(com.wan.Teacher) 所有返回类型为boolean,参数类型为com.wan.Teacher,方法名为addTeacher的方法
public void com.wan.TeacherDao.add(com.wan.Teacher) 方法存在TeacherDao类中,返回类型为空,参数类型为Teacher,方法名为add的方法
public * addTeacher(com.wan.Teacher) 所有返回类型为任意类型,参数类型为com.wan.Teacher,方法名为addTeacher的方法
public boolean *(com.wan.Teacher) 所有返回类型为任意类型,参数类型为Teacher,方法名任意的方法
public boolean addTeacher(..) 所有返回类型为任意类型,参数类型和个数不限,方法名为addTeacher的方法
* com.wan.*.*(..) 在com.wan包下面的所有方法(不包括子包)
* com.wan..*.*(..) 在com.wan包下面的所有方法(包括子包)

表达式要写在execution()的括号里面,多个条件可以使用or连接

5.测试

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
TeacherDao teacherdao = (TeacherDao) context.getBean("teacherdao");
teacherdao.add(new Teacher());

其他类型通知Advice使用

前置通知

前置通知和后置通知一样,也是实现对应的接口,然后重写before方法,这里就不过多说明了

异常通知

异常通知有点特殊,因为此接口是不需要重写方法的,但是,我们想要实现异常通知,得按照它定义的规则来

afterThrowing([Method method,Object[] args,Object target],Throwable ex)
  1. 方法名必须是afterThrowing
  2. 参数列表中的最后一个参数必须存在,可以是Throwable或者Throwable的子类
  3. 方法列表的前三个参数要么都存在,要么一个都不存在

环绕通知

此通知是spring的最强扩展,因为环绕通知可以拦截方法,对方法的传入参数的数值、返回值进行更改,或者是决定方法是否执行,也可以对目标进行异常处理。

如果对破解有所了解的话,环绕通知还可以被称为hook,像Android的Xposed框架就是通过hook原理,来达到自由更改系统目的。

实现MethodInterceptor接口,重写其的invoke方法

invoke方法可以获得像之前的前置通知的三个参数,methodtargetargs,也可以获得返回值returnValue

package com.wan;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; /**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
public class MyLogging implements MethodInterceptor { @Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object target = invoke.getThis();
Method method = invoke.getMethod();
Object[] args = invoke.getArguments();
//执行方法,获得返回值
Object returnValue = invoke.proceed();
}
}

基于注解配置使用

上面说的几个例子都是基于xml配置文件,我们可以使用注解,从而达到简化的目的

注解 说明
@Aspect 标注切入点
@Before 标注前置通知
@Around 标注环绕通知
@AfterReturning 标注后置通知
@After 标注最终通知
@AfterThrowing 标注异常通知

步骤

1. 导入相关jar(之前导入的那两个jar包)

2. 使用注解,标注类和方法

3. 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="teacherdao" class="com.wan.TeacherDao"/> <aop:aspectj-autoproxy/>
<bean class="com.wan.MyLogging"/>
</beans>

之后的测试代码和之前的一样

前置通知

package com.wan;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; /**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
@Aspect
public class MyLogging {
@Before("execution(public void add(com.wan.Teacher))")
public void sayHello() {
System.out.println("这是前置通知");
}
}

注解使用挺简单的,大概看一下示例代码就能知道怎么使用了

获得三个参数target、args、method

AOP中有个JoinPoint的接口,此接口可以获得targetargsmethod这三个参数

方法名 说明
getTarget() 获得目标对象
getSignature() 获得目标方法的Signature对象,由此对象的getName可以获得方法名
getArgs() 获得参数列表
package com.wan;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; /**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
@Aspect
public class MyLogging {
@AfterReturning(pointcut="execution(public boolean add(com.wan.Teacher))",returning="returnValue")
public void test(JoinPoint jp,Object returnValue) {
//上面的注解的returning属性把方法的返回值赋值给了参数returnValue
}
}

环绕通知

环绕通知有个特殊的接口ProceedingJoinPoint,此接口是JoinPoint的子接口,比JoinPoint接口多了一个proceed方法,用于执行目的对象的方法获得返回值

package com.wan;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; /**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
@Aspect
public class MyLogging {
@Around("execution(public boolean add(com.wan.Teacher))")
public void test(ProceedingJoinPoint jp) {
Object returnValue = jp.proceed();
}
}

异常通知

package com.wan;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; /**
* @author StarsOne
* @date Create in 2019/9/25 0025 16:53
* @description
*/
@Aspect
public class MyLogging {
@AfterThorwing(pointcut="execution(public boolean add(com.wan.Teacher))",throwing="e")
public void test(JoinPoint jp,NullPointException e) {
//上面的注解的throwing属性把异常赋值给了参数e
//参数中指定了异常为空指针异常,所有,发生异常为空指针异常时候,异常通知才会调用此方法
}
}

PS:除以上两种方式可以实现AOP,还有一种使用Schema进行配置,我看了一下步骤,觉得比上面两种还要繁琐,在这里就补充了

Spring框架学习笔记(2)——面向切面编程AOP的更多相关文章

  1. Spring学习笔记:面向切面编程AOP(Aspect Oriented Programming)

    一.面向切面编程AOP 目标:让我们可以“专心做事”,避免繁杂重复的功能编码 原理:将复杂的需求分解出不同方面,将公共功能集中解决 *****所谓面向切面编程,是一种通过预编译方式和运行期动态代理实现 ...

  2. spring框架(2)— 面相切面编程AOP

    spring框架(2)— 面相切面编程AOP AOP(Aspect Oriented Programming),即面向切面编程. 可以说是OOP(Object Oriented Programming ...

  3. ASP.NET MVC 学习笔记之面向切面编程与过滤器

    AOP(面向切面)是一种架构思想,用于把公共的逻辑放到一个单独的地方,这样就不用每个地方都写重复的代码了.比如程序中发生异常,不用每个地方都try…catch 只要在Golbal的Applicatio ...

  4. Spring框架系列(4) - 深入浅出Spring核心之面向切面编程(AOP)

    在Spring基础 - Spring简单例子引入Spring的核心中向你展示了AOP的基础含义,同时以此发散了一些AOP相关知识点; 本节将在此基础上进一步解读AOP的含义以及AOP的使用方式.@pd ...

  5. 04 Spring:01.Spring框架简介&&02.程序间耦合&&03.Spring的 IOC 和 DI&&08.面向切面编程 AOP&&10.Spring中事务控制

    spring共四天 第一天:spring框架的概述以及spring中基于XML的IOC配置 第二天:spring中基于注解的IOC和ioc的案例 第三天:spring中的aop和基于XML以及注解的A ...

  6. Spring学习手札(二)面向切面编程AOP

    AOP理解 Aspect Oriented Program面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 但是,这种说法有些片面,因为在软件工程中,AOP的价值体现的并 ...

  7. Spring框架学习笔记(1)

    Spring 框架学习笔记(1) 一.简介 Rod Johnson(spring之父) Spring是分层的Java SE/EE应用 full-stack(服务端的全栈)轻量级(跟EJB比)开源框架, ...

  8. Spring之控制反转——IoC、面向切面编程——AOP

      控制反转——IoC 提出IoC的目的 为了解决对象之间的耦合度过高的问题,提出了IoC理论,用来实现对象之间的解耦. 什么是IoC IoC是Inversion of Control的缩写,译为控制 ...

  9. Spring框架学习笔记(5)——Spring Boot创建与使用

    Spring Boot可以更为方便地搭建一个Web系统,之后服务器上部署也较为方便 创建Spring boot项目 1. 使用IDEA创建项目 2. 修改groupid和artifact 3. 一路n ...

随机推荐

  1. 目标检测YOLO进化史之yolov1

    yolov3在目标检测领域可以算得上是state-of-art级别的了,在实时性和准确性上都有很好的保证.yolo也不是一开始就达到了这么好的效果,本身也是经历了不断地演进的. yolov1 测试图片 ...

  2. swith case判断

    swith case是js中的一种判断方式 应用于变量或表达式在不同值情况下的不同操作,每一种case结束都要加break结束整个判断 var num = 2; switch(num){ case 0 ...

  3. 操作系统-IO管理疑难点

    IO管理疑难点 一.分配设备 首先根据IO请求中的物理设备名查找系统设备表(SDT),从中找出该设备的DCT(设备控制表),再根据DCT中的设备状态字段,可知该设备是否正忙.若忙,便将请求IO进程的P ...

  4. Leetcode之二分法专题-167. 两数之和 II - 输入有序数组(Two Sum II - Input array is sorted)

    Leetcode之二分法专题-167. 两数之和 II - 输入有序数组(Two Sum II - Input array is sorted) 给定一个已按照升序排列 的有序数组,找到两个数使得它们 ...

  5. 【原创】ARMv8 MMU及Linux页表映射

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  6. MSIL实用指南-生成foreach语句

    foreach可以迭代数组或者一个集合对象.foreach语句格式是它的生成步骤是foreach (<成员> in <集合>) <循环体> 一.声明三个变量,loc ...

  7. javaScript 基础知识汇总(四)

    1.对象 概念:对象可以通过花括号{...} 和其中包含一些可选的属性来创建. 属性时一个键值对,键是一个字符串,值可以是任何类型. 对象的创建 let user = new Object(); // ...

  8. Angular Material 的设计之美

    前言 Angular Material 作为 Angular 的官方组件库,无论是设计交互还是易用性都有着极高的质量.正如官方所说其目的就是构建基于 Angular 和 Typescript 的高质量 ...

  9. d3.js 教程 模仿echarts legend功能

    上一节记录没有加上echarts的legend功能,这一小节补一下. 1. 数据 我们可以从echarts中看出,折线数据并不是我们传进入的原始数据(多数情况下我们也不会修改原始数据),而是原始数组的 ...

  10. POJ-2253-Frogger +最短路小变形

    传送门:http://poj.org/problem?id=2253 参考:https://www.cnblogs.com/lienus/p/4273159.html 题意:给出一个无向图,求一条从 ...