【AOP】Spring AOP基础 + 实践 完整记录
Spring AOP的基础概念
=============================================================
AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角.
在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。
==============================================================
基础概念图:

有了上面这张原理图,那么关于AOP面向切面编程的核心几个概念,就可以顺利铺开了:
1》join point【连接点】
在spring aop中,认为原有代码中所有的方法都是join point。
2》point cut【切点】
在不改变原有代码的情况下,想多干点事情,那就需要定义point cut,切点。切点的任务是通过一组表达式来匹配要在哪个join point切入,并且匹配要在这个join point的什么位置切入。
3》advice【增强逻辑】
根据1,2切入了原有代码后,要做些什么事情?这多做的事情就是advice,也就是多处理的一些逻辑。比如,你的原有方法是对数据的保存方法,项目交付后,新需求是需要将这些保存操作在日志中记录下来,并且不能更改原有代码,这就是增强逻辑。
advice增强逻辑你是准备放在原有代码之前还是之后,有以下几种:
before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
after return advice, 在一个 join point 正常返回后执行的 advice
after throwing advice, 当一个 join point 抛出异常后执行的 advice
after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
4》weaving【织入】
现在有了原有代码,有了新的增强逻辑。将这两部分代码连接在一起的过程,就是织入weaving。
根据不同的实现技术, AOP织入有三种方式:
编译器织入, 这要求有特殊的Java编译器.
类装载期织入, 这需要有特殊的类装载器.
动态代理织入, 在运行期为目标类添加增强(Advice)生成子类的方式.
Spring 采用动态代理织入, 而AspectJ采用编译器织入和类装载期织入.
5》Target【目标对象】
原有代码和增强逻辑织入在一起,重新生成的就是目标对象Target,也叫adviced object.
Spring Aop使用运行时代理的方式实现Aspect,因此adviced object是一个代理对象。
【在 Spring AOP 中, 一个 AOP 代理是一个 JDK 动态代理对象或 CGLIB 代理对象。】
注意, adviced object 指的不是原来的类, 而是织入 advice 后所产生的代理类.
【关于java中代理的概念,类型,区别和理解:
https://www.cnblogs.com/hongcong/p/5806024.html
https://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
可以去看上面两篇文章,我自己还没有心思去研究。
但我记住了一句话:CGLIB方式不能代理final类。也就是说Spring AOP的切口不能切在final类上了。
】
6》aspect【切面】
aspect切面,是由point cut和advice组合而成的。既包含了连接点也就是切点的定义,也有增强逻辑的具体实现。
===========================================
到这里,一个概念就顺利的出来了:Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中.
===========================================
Spring Aop的实现和使用的各种情况
=======================================================
要在Spring中通过注解方式使用AOP,需要下面两步:
1》在配置文件中配置
<!-- 自动扫描注解 -->
<context:component-scan base-package="com.sxd" />
<!--Spring aop 使用注解的方式-->
<aop:aspectj-autoproxy />
2》定义切面【aspect】
切面包括 切点【point cut】 和 增强逻辑【advice】
【下面aspect中已经显示了point cut的各种定义表达式 和 各种类的advice】
【具体使用,应该按照实际使用逻辑选择性使用即可!!!】
package com.sxd.aop; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component; import java.util.Objects; /**
* 切面定义
* ①在类上定义@Aspect和@Component
* ②@Pointcut()定义切点
* ③adive方法上,声明在哪一个切点上切入 加入增强逻辑
*/
@Aspect
@Component
public class Aspects { /**
* execution() 表达式:用来匹配执行方法的连接点
* 【..(两个点)代表零个或多个】
* 【本例子中 第一个*代表方法的返回值类型可以为任何类型 如果本方法为void,也符合切入条件】
* 【本例子中 第一个..代表controller包以及子包下】
* 【本例子中 第二个*代表这个包下的任意类】
* 【本例子中 第三个*代表这个包下的任意类中的任意方法】
* 【本例子中 第二个..代表有无参数都可以被切入】
*
* args 如果想要切入的方法的参数要符合什么类型的话
* 【本例子代表入参中,第一个参数类型需要为String类型的才会被切入,之后有零个入参或多个入参】
*
*/
@Pointcut(value = "execution(* com.sxd.controller..*.*(..)) && args(String,..)")
public void pointCut1(){} /**
* within() 表达式用于匹配这个包下的任意类
*/
@Pointcut(value = "within(com.sxd.controller.*)")
public void pointCut2(){} /**
* this() 表达式限定了匹配这个类的实例下的方法
*/
@Pointcut(value = "this(com.sxd.controller.MainController)")
public void pointCut3(){} /**
* bean() 匹配IOC容器中的bean的名称
*/
@Pointcut(value = "bean(memberService)")
public void pointCut4(){} /**
* 下面是各种advice的展示
*
* 1》advice的执行优先级: around方法执行前》before》【方法自己】》around方法执行后》after》afterReturning
* 2》若有异常
* advice的执行优先级:around方法执行前》before》【方法自己】》around方法执行后》after》afterReturning
* 很奇怪为什么两次都是一样的执行优先级,为什么没有进afterThrowing().因为使用了around。
* 3》注意:Spring AOP的环绕通知会影响到AfterThrowing通知的运行,不要同时使用!
*
* 4》注意:在切面的advice里面,一定不要让异常抛出去,影响原方法的执行和返回。
* 5》JoinPoint 即原方法的入参
* 6》returning即原方法的返回值
* 7》如果原方法没有返回值,而这里的advice定义了returning,即使pointCut可以匹配上切点,也不会切入原方法
*/ @Before("pointCut1()")
public void justBefore(JoinPoint joinPoint){
System.out.println("切入方法前");
} @After("pointCut1()")
public void justAfter(JoinPoint joinPoint){
Object[] arr = joinPoint.getArgs();
if(Objects.nonNull(arr) && arr.length >0){
System.out.println((String)arr[0]);
System.out.println((Integer)arr[1]);
}
System.out.println("切入方法后");
} @AfterReturning(pointcut = "pointCut1()",returning = "returnVal")
public void justAfterReturn(JoinPoint joinPoint,Object returnVal){
System.out.println(returnVal.toString());
System.out.println("在方法执行完,并未抛异常,能正确返回值的情况下,在返回值之前切入");
} @AfterThrowing(pointcut = "pointCut1()",throwing = "err")
public void justAfterThrow(JoinPoint joinPoint,Throwable err){
System.out.println("在方法执行,抛异常的情况下,切入");
} @Around("pointCut1()")
public Object justAround(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("环绕型切入,方法执行前");
Object a = null;
try {
a = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕型切入,方法执行后");
return a;
} }
3》上面两步 就把aop写完了 ,最后要测试一下各种情况
package com.sxd.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MainController {
@ResponseBody
@RequestMapping("do")
public String doSomething(){
return "无参";
}
@ResponseBody
@RequestMapping("do2")
public String doSomething2(String a){
return "有参"+a;
}
@ResponseBody
@RequestMapping("do3")
public String doSomething3(Integer a,String b){ return "整数"+a+"---字符串"+b; }
@ResponseBody
@RequestMapping("do4")
public String doSomething4(String a,Integer b){
return "字符串"+a+"---整数"+b;
}
@ResponseBody
@RequestMapping("do5")
public String doSomething5(String a,Integer b) throws Throwable{
return "字符串转成数字"+Integer.parseInt(a)+"---整数"+b;
}
@ResponseBody
@RequestMapping("do6")
public void doSomething6(){
System.out.println("无返回值的");
}
}
发请求 测试即可。
======================================================================
补充:
1》两个或多个位置定义切点
方法1:
@Pointcut(value = "(execution(* net.shopxx.xgn.controller.MemberCybController.cjtg(..))) or (execution(* net.shopxx.hunan.MemberCybController.cjtg(..)))")
public void endbgDeal() {
}
方法2:
@Pointcut(value = "execution(* net.shopxx.xgn.controller.MemberCybController.cjtg(..))")
public void endbgDeal1(){ }
@Pointcut(value = "execution(* net.shopxx.hunan.MemberCybController.cjtg(..))")
public void endbgDeal2(){ } @Pointcut(value = "endbgDeal1() || endbgDeal2()")
public void endbgDeal() {
}
【AOP】Spring AOP基础 + 实践 完整记录的更多相关文章
- Spring 学习——Spring AOP——AOP概念篇
AOP AOP的定义:AOP,Aspect Oriented Programming的缩写,意为面向切面编程,是通过预编译或运行期动态代理实现程序功能处理的统一维护的一种技术 实现方式 预编译 Asp ...
- spring aop与aspectj
AOP:面向切面编程 简介 AOP解决的问题:将核心业务代码与外围业务(日志记录.权限校验.异常处理.事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一. AOP应用场景: 日志记录.权 ...
- spring aop介绍和示例
参考:<Spring in Action> 一.AOP介绍 AOP是Aspect Oriented Programming的缩写,意思是面向切面编程. 应用中有一些功能使用非常普遍,比如事 ...
- Spring5.0源码学习系列之Spring AOP简述
前言介绍 附录:Spring源码学习专栏 在前面章节的学习中,我们对Spring框架的IOC实现源码有了一定的了解,接着本文继续学习Springframework一个核心的技术点AOP技术. 在学习S ...
- Spring Aop技术原理分析
本篇文章从Aop xml元素的解析开始,分析了Aop在Spring中所使用到的技术.包括Aop各元素在容器中的表示方式.Aop自动代理的技术.代理对象的生成及Aop拦截链的调用等等.将这些技术串联起来 ...
- Spring Aop、拦截器、过滤器的区别
Filter过滤器:拦截web访问url地址.Interceptor拦截器:拦截以 .action结尾的url,拦截Action的访问.Spring AOP拦截器:只能拦截Spring管理Bean的访 ...
- Spring AOP统一异常处理
1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- spring-第十六篇之AOP面向切面编程之Spring AOP
1.上一篇介绍了AspectJ在AOP的简单应用,让我们了解到它的作用就是:开发者无需修改源代码,但又可以为这些组件的方法添加新的功能. AOP的实现可分为两类(根据AOP修改源码的时机划分): 1& ...
- spring boot基础学习教程
Spring boot 标签(空格分隔): springboot HelloWorld 什么是spring boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新 ...
随机推荐
- Android onConfigurationChanged用法(规避横竖屏切换导致的重新调用onCreate方法)
onConfigurationChanged的目的是为了规避横竖屏切换干掉activity而重新调用onCreate方法的问题:有的时候,我们希望重新进入OnCreate生命周期,此时可以调用onSa ...
- 微信小程序-----校园头条详细开发之列表展示数据
1.分类列表数据展示功能的实现 1.1 结构 1.2 代码实现 1.2.1 列表显示数据,.每次界面显示6条数据,发请求获取数据,动态存放 var app = getApp() Page({ dat ...
- Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发
接入微信公众平台开发,开发者需要按照如下步骤完成: 1.填写服务器配置 2.验证服务器地址的有效性 3.依据接口文档实现业务逻辑 资料准备: 1.一个可以访问的外网,即80的访问端口,因为微信公众号接 ...
- Mysql Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
Mysql update error: Error Code: 1175. You are using safe update mode and you tried to update a table ...
- rm 、git rm 、git rm --cached的区别
rm 删除文件 git rm git rm 当我们需要删除暂存区或分支上的文件, 同时工作区也不需要这个文件了, 可以使用git rm git rm file = rm file+ git add f ...
- zookeeper 下载安装
下载:wget https://www-us.apache.org/dist/zookeeper/zookeeper-3.4.13/zookeeper-3.4.13.tar.gz 解压:tar -zx ...
- springboot 连接redis
引入依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>s ...
- 【bzoj3601】一个人的数论 莫比乌斯反演+高斯消元
题目描述 题解 莫比乌斯反演+高斯消元 (前方高能:所有题目中给出的幂次d,公式里为了防止混淆,均使用了k代替) #include <cstdio> #include <cstrin ...
- Codeforces Round #316 (Div. 2) A 水
A. Elections time limit per test 1 second memory limit per test 256 megabytes input standard input o ...
- Jerasure 1.2A 中的 C 函数 tips
C stat函数的用法举例 C语言 fread()与fwrite()函数说明与示例 / C 库函数 - fwrite() C 库函数 - sprintf()