1. 什么是AOP

aop:面向切面编程。采用横向机制。

oop:面向对象编程。采用纵向机制。

AOP,面向切面编程。就是通过某个切入点(比如方法开始、结束)向某个切面(被切的对象)切入环绕通知(需要切入的逻辑代码)。

比如一个类中的所有方法执行前都需要打印日志,那么可以通过AOP的方式来统一实现,而不需要在每个方法中都加入打印日志的代码逻辑。

2. AOP的常用使用场景

  • 日志记录
  • 权限控制
  • 事物管理
  • 缓存处理

    ...

3. AOP的实现方式

  • Spring AOP

        a) JDK 动态代理

        b) Cglib 动态代理
  • AspectJ

4. AspectJ是什么

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

Spring2.0以后新增了对AspectJ的全面支持。

常用注解:

@Before             前置通知                                          使用时需要指定一个value属性值,用于指定一个切入点表达式。【可以是现写的表达式,也阔以是已有的】。
@AfterReturning 后置通知 使用时需要指定pointcut/value属性值,用于指定切入点表达式。还能使用returning属性指定一个形参,该形参可用于访问目标方法的返回值。
@Around 环绕通知 使用时需要指定一个value属性值,用于指定一个切入点表达式。
@AfterThrowing 异常通知 使用时需要指定pointcut/value属性值,用于指定切入点表达式。还能使用throwing属性指定一个形参,该形参可用于访问目标方法抛出的异常。
@After 最终final通知,不管是否异常,该通知都会执行 使用时需要指定一个value属性值,用于指定一个切入点表达式。
@DeclareParents 引介通知
@Aspect 用在类上,表示当前类为一个切面类 在通知中可以使用了 JoinPoint 接口及其子接口 ProceedingJoinPoint 作为参数来获得目标对象的————类名,方法名,方法参数等。
★环绕通知必须接受一个类型为 ProceedingJoinPoint 的参数, 返回值也必须是 Object 类型,且需要抛出异常。返回值即为目标方法的返回值★
★异常通知可以传入Throwable类型的参数 来接收异常信息★

切入点表达式:

格式:execution( * com.example.aop.*.*(..))

    execution:就是表达式的主体。

    第一个*:返回类型,可以用代表所有类型

    com.example.aop:表示需要拦截的路径包名

    第二个*:类名,可以用
代表所有类

    第三个*:方法名,可以用*表示所有方法

    (..):..表示任意参数

还支持通配符的使用:

1) *:匹配所有字符
2) ..:一般用于匹配多个包,多个参数
3) +:表示类及其子类
4)运算符有:&&,||,! AspectJ使用 且(&&)、或(||)、非(!)来组合切入点表达式。

除了上述表达式写法,aop还支持如下的几种写法:

参考

@Around("within(类型表达式)")

within(cn.javass..*)                        cn.javass包及子包下的任何方法执行

within(cn.javass..IPointcutService+)        cn.javass包或所有子包下IPointcutService类型及子类型的任何方法

within(@cn.javass..Secure *)                持有cn.javass..Secure注解的任何类型的任何方法,必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

@Around("@within(注解类型))

@within cn.javass.spring.chapter6.Secure)   任何目标对象对应的类型持有Secure注解的类方法;必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

@Around("target(类型全限定名)")

target(cn.javass.spring.chapter6.service.IPointcutService)      当前目标对象(非AOP对象)实现了 IPointcutService接口的任何方法
target(cn.javass.spring.chapter6.service.IIntroductionService) 当前目标对象(非AOP对象) 实现了IIntroductionService 接口的任何方法不可能是引入接口

@Around("@target(注解类型)")

@target (cn.javass.spring.chapter6.Secure)    任何目标对象持有Secure注解的类方法;必须是在目标对象上声明这个注解,在接口上声明的对它不起作用

@Around("args(参数类型列表)")

args (java.io.Serializable,..)         任何一个以接受“传入参数类型为 java.io.Serializable” 开头,且其后可跟任意个任意类型的参数的方法执行,args指定的参数类型是在运行时动态匹配的

@Around("@args(注解列表)")

@args (cn.javass.spring.chapter6.Secure)   任何一个只接受一个参数的方法,且方法运行时传入的参数持有注解 cn.javass.spring.chapter6.Secure;动态切入点,类似于arg指示符;

@Around("@annotation(注解类型)")

@annotation(cn.javass.spring.chapter6.Secure )    当前执行方法上持有注解 cn.javass.spring.chapter6.Secure将被匹配

@Around("this(类型全限定名)")

this(cn.javass.spring.chapter6.service.IPointcutService)         当前AOP对象实现了 IPointcutService接口的任何方法
this(cn.javass.spring.chapter6.service.IIntroductionService) 当前AOP对象实现了 IIntroductionService接口的任何方法也可能是引入接口

关于JoinPoint对象介绍:

JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象。

补充aop相关概念

    连接点(Join point)

        连接点是在应用执行过程中能够插入切面的一个点。

    切点(Pointcut)

        一个切面并不需要通知应用的所有连接点,切点有助于缩小切面所通知的连接点范围。如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”。

        因此,切点其实就是定义了需要执行在哪些连接点上执行通知。

    切面(Aspect)

        切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和在何处完成其功能。

    织入(Weaving)

        织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。

public interface JoinPoint {
String toString(); //连接点所在位置的相关信息
String toShortString(); //连接点所在位置的简短相关信息
String toLongString(); //连接点所在位置的全部相关信息
Object getThis(); //返回AOP代理对象
Object getTarget(); //返回目标对象
Object[] getArgs(); //返回被通知方法参数列表
Signature getSignature(); //返回当前连接点签名
SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置
String getKind(); //连接点类型
StaticPart getStaticPart(); //返回连接点静态部分
}

关于ProceedingJoinPoint对象介绍:

ProceedingJoinPoint对象是JoinPoint的子接口。

主要多了如下两个方法:
Object proceed() throws Throwable //执行目标方法
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法

关于Signature对象介绍:

Signature对象:是一个接口。用于获取或记录有关连接点的反射信息。他的子接口还提供了很多额外的实用方法。

常用子接口如下:

UnlockSignature       实现类     UnlockSignatureImpl
LockSignature 实现类 LockSignatureImpl
CatchClauseSignature 实现类 CatchClauseSignatureImpl
MemberSignature
子接口 FieldSignature 实现类 FieldSignatureImpl
子接口 CodeSignature[它还有很多子接口] 实现类 CodeSignatureImpl

常用方法如下:

String toString();
String toShortString(); //返回此签名的字符串缩写形式
String toLongString(); //返回此签名的字符串扩展形式
String getName(); //返回此签名的方法【即返回方法名】
int getModifiers(); //以整数形式返回此签名方法的修饰符。
Class getDeclaringType(); //返回一个java.lang.Class对象,该对象表示声明此成员的类或接口。
String getDeclaringTypeName(); //返回声明类型的全限定名称

关于签名咋个理解看下面的代码

5. SpringBoot整合AspectJ实现日志记录

★也可以AspectJ+自定义注解来做,这里没有展示这种★

5.1 导入依赖

<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

5.2 定义切面类

① 在类上使用 @Component 注解 把切面类加入到IOC容器中

② 在类上使用 @Aspect 注解 使之成为切面类

@Aspect
@Component
public class LogAspect {
private Logger logger = LoggerFactory.getLogger(LogAspect.class); /**
* 定义切入点,切入点为com.jsy.community下的函数
*/
@Pointcut("execution( * com.lihao.community..*.*(..)) && !execution(* com.lihao.community.intercepter..*.*(..)) && !execution(* com.lihao.community.exception..*.*(..))")
public void webLog() {
} /**
* 前置通知:在连接点之前执行的通知
*
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");
Calendar ca = Calendar.getInstance();
String time = df.format(ca.getTime());
logger.info("");
logger.info("访问时间 : " + time);
logger.info("访问路径 : " + request.getRequestURL().toString());
logger.info("请求方式 : " + request.getMethod());
logger.info("访问方法 : " + joinPoint.getSignature().getName()); //签名?
logger.info("访问IP : " + request.getRemoteAddr());
logger.info("方法参数 : " + Arrays.toString(joinPoint.getArgs()));
}
} /**
* @return void
* @Author lihao
* @Description 后置通知
* @Date 2021/1/19 17:09
* @Param [ret]
**/
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 处理完请求,返回内容
logger.info("返回结果 : " + ret);
} /**
* @return void
* @Author lihao
* @Description 异常通知
* @Date 2021/1/19 17:43
* @Param [joinPoint, ex]
**/
@AfterThrowing(pointcut = "webLog()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
logger.info("异常信息 : " + methodName + "() 出现了异常——————" + ex.getMessage());
}
}

6 SpringBoot整合AspectJ实现缓存[利用aop做延时双删]

实现原理:

热点数据(经常会被查询,但是不经常被修改或者删除的数据),首选是使用redis缓存,毕竟强大到冒泡的QPS和极强的稳定性不是所有类似工具都有的,而且相比于memcached还提供了丰富的数据类型可以使用,另外,内存中的数据也提供了AOF和RDB等持久化机制可以选择,要冷、热的还是忽冷忽热的都可选。

结合具体应用需要注意一下:很多人用spring的AOP来构建redis缓存的自动生产和清除,过程可能如下:

Select 数据库前查询redis,有的话使用redis数据,放弃select 数据库,没有的话,select 数据库,然后将数据插入redis

update或者delete数据库钱,查询redis是否存在该数据,存在的话先删除redis中数据,然后再update或者delete数据库中的数据

上面这种操作,如果并发量很小的情况下基本没问题,但是高并发的情况请注意下面场景:

为了update先删掉了redis中的该数据,这时候另一个线程执行查询,发现redis中没有,瞬间执行了查询SQL,并且插入到redis中一条数据,回到刚才那个update语句,这个悲催的线程压根不知道刚才那个该死的select线程犯了一个弥天大错!于是这个redis中的错误数据就永远的存在了下去,直到下一个update或者delete。

aop详解与实战的更多相关文章

  1. Spring4 AOP详解

    Spring4 AOP详解 第一章Spring 快速入门并没有对Spring4 的 AOP 做太多的描述,是因为AOP切面编程概念不好理解.所以这章主要从三个方面详解AOP:AOP简介(了解),基于注 ...

  2. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

  3. Android为TV端助力 转载:Android绘图Canvas十八般武器之Shader详解及实战篇(上)

    前言 Android中绘图离不开的就是Canvas了,Canvas是一个庞大的知识体系,有Java层的,也有jni层深入到Framework.Canvas有许多的知识内容,构建了一个武器库一般,所谓十 ...

  4. Android为TV端助力 转载:Android绘图Canvas十八般武器之Shader详解及实战篇(下)

    LinearGradient 线性渐变渲染器 LinearGradient中文翻译过来就是线性渐变的意思.线性渐变通俗来讲就是给起点设置一个颜色值如#faf84d,终点设置一个颜色值如#CC423C, ...

  5. [Spring学习笔记 5 ] Spring AOP 详解1

    知识点回顾:一.IOC容器---DI依赖注入:setter注入(属性注入)/构造子注入/字段注入(注解 )/接口注入 out Spring IOC容器的使用: A.完全使用XML文件来配置容器所要管理 ...

  6. AOP 详解

    1. 需求:统计方法执行的性能情况(来源:<精通Spring 4.x>) // 性能监视类 PerformanceMonitor package com.noodles.proxy; pu ...

  7. Spring AOP详解(转载)所需要的包

    上一篇文章中,<Spring Aop详解(转载)>里的代码都可以运行,只是包比较多,中间缺少了几个相应的包,根据报错,几经百度搜索,终于补全了所有包. 截图如下: 在主测试类里面,有人怀疑 ...

  8. Spring AOP详解及简单应用

    Spring AOP详解   一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址: ...

  9. 转:Spring AOP详解

    转:Spring AOP详解 一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址: ...

随机推荐

  1. kubernets之job资源

    一  介绍job资源 1.1   前面介绍的RC,RS,DS等等,管控的pod都是需要长期持久的运行的应用,但是尝试考虑另外一种场景,在微服务的场景下,有些pod的作用就是需要 执行完一些命令之后正常 ...

  2. library cache pin解决方法

    library cache pin大部分都是因为编译存储过程造成的 查找造成问题的数据库对象(一般为存储过程) SELECT * FROM v$session_wait WHERE event = ' ...

  3. Promise.all()使用实例

    一.什么是Promise.all()? 在说这个之前要先说清楚promise.promise就是一个对象,专门用来处理异步操作的. 而Promise.all方法用于将多个 Promise 实例,包装成 ...

  4. python中IF语句容易犯的错误CASE

    python中没有switch   case类似的语句,但是下面的IF语句却与之类似,却又不同: A = B = C = D = E = 1 if A == 1: B=2 elif B ==2: C= ...

  5. 如何用Python中自带的Pandas和NumPy库进行数据清洗

    一.概况 1.数据清洗到底是在清洗些什么? 通常来说,你所获取到的原始数据不能直接用来分析,因为它们会有各种各样的问题,如包含无效信息,列名不规范.格式不一致,存在重复值,缺失值,异常值等..... ...

  6. Core3.1 微信v3 JSAPI支付

    1.前言 "小魏呀,这个微信支付还要多久?","快了快了老板,就等着最后一步了...","搞快点哈,就等着上线呢","...... ...

  7. jackson学习之一:基本信息

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  8. MongoDB 总结

    目录 1. 逻辑结构 2. 安装部署 2.1 系统准备 2.2 mongodb安装 2.2.1 创建所需用户和组 2.2.2 创建mongodb所需目录结构 2.2.3 上传并解压软件到指定位置 2. ...

  9. CentOS 7.4通过rpm包离线安装 Mysql8.0并部署主从复制(附从库备份脚本)

    一. 部署MySQL (两个节点都做) 下载 rpm包 wget https://goodrain-pkg.oss-cn-shanghai.aliyuncs.com/mysql8.rpm tar xv ...

  10. Wi-Fi IoT套件连PCF8563实现电子钟功能

    首先跟同样新入手单片机开发的小伙伴分享一点I2C通信的知识.我估计大部分入手开发板的小伙伴都有一定程序开发的能力,但是底层开发可能是新接触,我看有的小伙伴配置开发环境都有障碍,其实并不是多复杂,只是首 ...