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. 【Oracle】删除表空间

    删除表空间如果是 SQL> DROP TABLEPSACE XXXX; 是无法将数据文件一同都删除的 想要删除表空间和数据文件需要如下操作: SQL> drop tablespace XX ...

  2. 【ASM】asm中添加 diskgroup

    环境:rhel5 Oracle10g rac 背景:在esxi中添加了一个20g的共享磁盘准备存放归档日志用 一.准备环境 1.添加共享磁盘并且格式化 #fdisk -l查看磁盘已经添加完成 #fdi ...

  3. postgresql插件安装

    postgresql安装包自带插件安装: 1.编译安装插件 # root用户 # postgresql安装过程省略 # 进入解压包的contrib目录 cd postgresql-10.6/contr ...

  4. 《Go 语言并发之道》读后感 - 第四章

    <Go 语言并发之道>读后感-第四章 约束 约束可以减轻开发者的认知负担以便写出有更小临界区的并发代码.确保某一信息再并发过程中仅能被其中之一的进程进行访问.程序中通常存在两种可能的约束: ...

  5. 在nodejs中创建child process

    目录 简介 child process 异步创建进程 同步创建进程 在nodejs中创建child process 简介 nodejs的main event loop是单线程的,nodejs本身也维护 ...

  6. BeetleX大数据之产品分析服务

        数据规模过于庞大?数据标签过多难以管理?增加新的分析维度需要配置?这些beetlex.io都能轻松解决,即导即用,数据标签自动管理,轻易实现多种维度数据分析处理.接下介绍BeetleX针对产品 ...

  7. python多线程和GIL全局解释器锁

    1.线程     线程被称为轻量级进程,是最小执行单元,系统调度的单位.线程切换需要的资源一般,效率一般.  2.多线程         在单个程序中同时运行多个线程完成不同的工作,称为多线程 3.并 ...

  8. Java并发组件三之Semaphore

    使用场景:常用于使用有限的资源,限制线程并发的最大数量.默认情况下,信号量是非公平性的(先等待先执行为公平.类似于买东西的时候大家排队付款,先来的先付款是公平的.但是这时候有人插队,那就是非公平的)设 ...

  9. 2、剑指offer-字符串——替换空格

    **题目描述** **请实现一个函数,将一个字符串中的每个空格替换成"%20".例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. ...

  10. Architecture and design 洋葱 中间件 装饰器

    Go kit - Frequently asked questions https://gokit.io/faq/ Architecture and design Introduction - Und ...