注解开发AOP制作步骤:

在XML格式基础上

  1. 导入坐标(伴随spring-context坐标导入已经依赖导入完成
  2. 开启AOP注解支持
  3. 配置切面@Aspect
  4. 定义专用的切入点方法,并配置切入点@Pointcut
  5. 为通知方法配置通知类型及对应切入点@Before

注解开发AOP注意事项:

  1. 切入点最终体现为一个方法,无参无返回值,无实际方法体内容,但不能是抽象方法
  2. 引用切入点时必须使用方法调用名称,方法后面的()不能省略
  3. 切面类中定义的切入点只能在当前类中使用,如果想引用其他类中定义的切入点使用“类名.方法名()”引用
  4. 可以在通知类型注解后添加参数,实现XML配置中的属性,例如after-returning后的returning属性

AOP注解详解:

@Aspect

  • 名称:@Aspect

  • 类型:注解

  • 位置:类定义上方

  • 作用:设置当前类为切面类

  • 格式:

@Aspect
public class AopAdvice { }
  • 说明:一个beans标签中可以配置多个aop:config标签

@Pointcut

  • 名称:@Pointcut

  • 类型:注解

  • 位置:方法定义上方

  • 作用:使用当前方法名作为切入点引用名称

  • 格式:

    @Pointcut("execution(* *(..))")public void pt() {}
  • 说明:被修饰的方法忽略其业务功能,格式设定为无参无返回值的方法,方法体内空实现(非抽象)

@Before

  • 名称:@Before

  • 类型:注解

  • 位置:方法定义上方

  • 作用:标注当前方法作为前置通知

  • 格式:

    @Before("pt()")public void before() {}
  • 特殊参数:

@After

  • 名称:@After

  • 类型:注解

  • 位置:方法定义上方

  • 作用:标注当前方法作为后置通知

  • 格式:

    @After("pt()")public void after() {}
  • 特殊参数:

@AfterReturning

  • 名称:@AfterReturning

  • 类型:注解

  • 位置:方法定义上方

  • 作用:标注当前方法作为返回后通知

  • 格式:

    @AfterReturning(value = "pt()", returning = "ret")public void afterReturning(Object ret) {}
  • 特殊参数:

    • returning :设定使用通知方法参数接收返回值的变量名

@AfterThrowing

  • 名称:@AfterThrowing

  • 类型:注解

  • 位置:方法定义上方

  • 作用:标注当前方法作为异常后通知

  • 格式:

    @AfterThrowing(value = "pt()", throwing = "t")public void afterThrowing(Throwable t) {}
  • 特殊参数:

    • throwing :设定使用通知方法参数接收原始方法中抛出的异常对象名

@Around

  • 名称:@Around

  • 类型:注解

  • 位置:方法定义上方

  • 作用:标注当前方法作为环绕通知

  • 格式:

    @Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {Object ret = pjp.proceed();return ret;}

配置文件加载注解:

public class AopPointcut {@Pointcut("execution(* *..ABC(..))")public void pt(){}
}
    @Before("AopPointcut.pt()")public void before(){}

AOP注解开发通知执行顺序控制

1.AOP使用XML配置情况下,通知的执行顺序由配置顺序决定,在注解情况下由于不存在配置顺序的概念的概念,参照通知所配置的方法名字符串对应的编码值顺序,可以简单理解为字母排序

  • 同一个通知类中,相同通知类型以方法名排序为准

  • 不同通知类中,以类名排序为准

  • 使用@Order注解通过变更bean的加载顺序改变通知的加载顺序

2.企业开发经验

  • 通知方法名由3部分组成,分别是前缀、顺序编码、功能描述

  • 前缀为固定字符串,例如baidu、itzhuzhu等,无实际意义

  • 顺序编码为6位以内的整数,通常3位即可,不足位补0

  • 功能描述为该方法对应的实际通知功能,例如exception、strLenCheck

    • 制通知执行顺序使用顺序编码控制,使用时做一定空间预留

    • 003使用,006使用,预留001、002、004、005、007、008

    • 使用时从中段开始使用,方便后期做前置追加或后置追加

    • 最终顺序以运行顺序为准,以测试结果为准,不以设定规则为准

AOP注解驱动

  • 名称:@EnableAspectJAutoProxy

  • 类型:注解

  • 位置:Spring注解配置类定义上方

  • 作用:设置当前类开启AOP注解驱动的支持,加载AOP注解

  • 格式:

@Configuration
@ComponentScan("com.itzhuzhu")
@EnableAspectJAutoProxy
public class SpringConfig {}

AOP底层原理:

  • 静态代理
  • 动态代理——Proxy
  • 动态代理——CGLIB
  • 织入形式

静态代理:

装饰者模式(Decorator Pattern):在不惊动原始设计的基础上,为其添加功能

public class UserServiceDecorator implements UserService{private UserService userService;public UserServiceDecorator(UserService userService) {this.userService = userService;}public void save() {//原始调用userService.save();//增强功能(后置)System.out.println("刮大白");}
}

动态代理——JDK Proxy:

JDKProxy动态代理是针对对象做代理,要求原始对象具有接口实现,并对接口方法进行增强

public class UserServiceJDKProxy {public UserService createUserServiceJDKProxy(final UserService userService){//获取被代理对象的类加载器ClassLoader classLoader = userService.getClass().getClassLoader();//获取被代理对象实现的接口Class[] classes = userService.getClass().getInterfaces();//对原始方法执行进行拦截并增强InvocationHandler ih = new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//前置增强内容Object ret = method.invoke(userService, args);//后置增强内容System.out.println("刮大白2");return ret;}};//使用原始被代理对象创建新的代理对象UserService proxy = (UserService) Proxy.newProxyInstance(classLoader,classes,ih);return proxy;}
}

动态代理——CGLIB:

  • CGLIB(Code Generation Library),Code生成类库
  • CGLIB动态代理不限定是否具有接口,可以对任意操作进行增强
  • CGLIB动态代理无需要原始被代理对象,动态创建出新的代理对象
  • 可以动态生成字节码文件
public class UserServiceImplCglibProxy {public static UserServiceImpl createUserServiceCglibProxy(Class clazz){//创建Enhancer对象(可以理解为内存中动态创建了一个类的字节码)Enhancer enhancer = new Enhancer();//设置Enhancer对象的父类是指定类型UserServerImplenhancer.setSuperclass(clazz);Callback cb = new MethodInterceptor() {public Object intercept(Object o, Method m, Object[] a, MethodProxy mp) throws Throwable {Object ret = mp.invokeSuper(o, a);if(m.getName().equals("save")) {System.out.println("刮大白");}return ret;}};//设置回调方法enhancer.setCallback(cb);//使用Enhancer对象创建对应的对象return (UserServiceImpl)enhancer.create();}
}

代理模式的选择:

Spirng可以通过配置的形式控制使用的代理形式,默认使用jdkproxy,通过配置可以修改为使用cglib

  • XML配置
<!--XMP配置AOP-->
<aop:config proxy-target-class="false"></aop:config>
  • XML注解支持
<!--注解配置AOP-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
  • 注解驱动
//注解驱动
@EnableAspectJAutoProxy(proxyTargetClass = true)

综合案例:

对项目进行业务层接口执行监控,测量业务层接口的执行效率

public interface AccountService {void save(Account account);void delete(Integer id);void update(Account account);List<Account> findAll();Account findById(Integer id);
}

案例分析:

  • 测量接口执行效率:接口方法执行前后获取执行时间,求出执行时长

    • System.currentTimeMillis( )
  • 对项目进行监控:项目中所有接口方法,AOP思想,执行期动态织入代码

    • 环绕通知

    • proceed()方法执行前后获取系统时间

案例制作步骤:

  • 定义切入点(务必要绑定到接口上,而不是接口实现类上)

  • 制作AOP环绕通知,完成测量功能

  • 注解配置AOP

  • 开启注解驱动支持

案例制作核代码:

@Component
@Aspect
public class RunTimeMonitorAdvice {//切入点,监控业务层接口@Pointcut("execution(* com.itzhuzhu.service.*Service.find*(..))")public void pt() {}@Around("pt()")public Object runtimeAround(ProceedingJoinPoint pjp) throws Throwable {//获取执行签名信息Signature signature = pjp.getSignature();//通过签名获取执行类型(接口名)String className = signature.getDeclaringTypeName();//通过签名获取执行操作名称(方法名)String methodName = signature.getName();//执行时长累计值long sum = 0L;for (int i = 0; i < 10000; i++) {//获取操作前系统时间beginTimelong startTime = System.currentTimeMillis();//原始操作调用pjp.proceed(pjp.getArgs());//获取操作后系统时间endTimelong endTime = System.currentTimeMillis();sum += endTime - startTime;}//打印信息System.out.println(className + ":" + methodName + "   (万次)run:" + sum + "ms");return null;}
}

案例后续思考与设计:

  • 测量真实性

    • 开发测量是隔离性反复执行某个操作,是理想情况,上线测量差异过大

    • 上线测量服务器性能略低于单机开发测量

    • 上线测量基于缓存的性能查询要优于数据库查询测量

    • 上线测量接口的性能与最终对外提供的服务性能差异过大

    • 当外部条件发生变化(硬件),需要进行回归测试,例如数据库迁移

  • 测量结果展示

    • 测量结果无需每一个都展示,需要设定检测阈值

    • 阈值设定要根据业务进行区分,一个复杂的查询与简单的查询差异化很大

    • 阈值设定需要做独立的配置文件或通过图形工具配置(工具级别的开发)

    • 配合图形界面展示测量结果

AOP底层原理与注解配置详解相关推荐

  1. spring之旅第四篇-注解配置详解

    spring之旅第四篇-注解配置详解 一.引言 最近因为找工作,导致很长时间没有更新,找工作的时候你会明白浪费的时间后面都是要还的,现在的每一点努力,将来也会给你回报的,但行好事,莫问前程!努力总不会 ...

  2. Nginx 反向代理工作原理简介与配置详解

    Nginx 反向代理工作原理简介与配置详解 测试环境 CentOS 6.8-x86_64 nginx-1.10.0 下载地址:http://nginx.org/en/download.html 安装 ...

  3. 使用LVS实现负载均衡原理及安装配置详解

    使用LVS实现负载均衡原理及安装配置详解 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均 ...

  4. servlet过滤器中的注解配置详解

    文章目录 注解配置@WebFilter中的属性 1. urlPatterns 2. initParams 3. dispatcherTypes 多个过滤器的执行顺序 销毁阶段的触发机制 注解配置@We ...

  5. SpringMVC原理及非注解配置详解

    如需转发请标明出处:http://www.cnblogs.com/gudu1/p/7222556.html 1. Spring介绍 Spring MVC是Spring提供的一个强大而灵活的web框架. ...

  6. springboot 中使用 Mybatis 注解 配置 详解

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 传参方式 使用不同的传参方式: 使用@Param 之前博文中的项目使用了这种简单的传参方式: @In ...

  7. Spring Boot中使用MyBatis注解配置详解

    之前在Spring Boot中整合MyBatis时,采用了注解的配置方式,相信很多人还是比较喜欢这种优雅的方式的,也收到不少读者朋友的反馈和问题,主要集中于针对各种场景下注解如何使用,下面就对几种常见 ...

  8. cacheable注解原理_Cacheable注解使用详解

    完成Redis基础配置之后,就可以使用Redis对数据进行缓存了. 最简单的方式就是使用springframe为我们提供的@Cacheable注解,以下是@Cacheable注解的具体使用方式. @C ...

  9. RabbitMQ底层原理及安装使用详解

    1.RabbitMQ原理概述 2.RabbitMQ安装和管理 3-1.Direct(直接交换器) 4.消息发布时的权衡 5.消息消费时的权衡 6.消息消费的拒绝 7.Spring整合RabbitMQ ...

最新文章

  1. ExtJS4.2学习(14)基于表格的扩展插件(2)
  2. hdu 4502(DP)
  3. oracle 行送,Oracle 行专列
  4. Tengine开源新特性:如何让HTTPS处理能力轻松翻倍?
  5. Taro+react开发(36)每一个节点要一个view包裹
  6. (软件工程复习核心重点)第七章软件维护-第三节:软件可维护性
  7. python判断数据是否在另一个集合中_python判断一个集合是否包含了另外一个集合中所有项的方法...
  8. sqlserver分区表索引
  9. 2020 年,Serverless 将给大前端带来什么样的变化?
  10. php 读xml的两种方式
  11. 作业必备:【操作系统实验报告】实验一:熟悉Ubuntu环境(后续会更新~)
  12. Java实现生成32位UUID工具类
  13. 小米台灯突然自己亮了_米家台灯Pro,工作读书随我选
  14. DCDC中电感的计算
  15. 流程图设计(泳道图 | 任务流程图 | 页面流程图)
  16. 友盟分享 qq没有存储权限 分享失败
  17. java生气_Java来抢饭碗,C++可别生气
  18. 2018年高教社杯全国大学生数学建模竞赛题目
  19. source insight的使用
  20. PaddleX---MobileNetV3_ssld图像分类

热门文章

  1. java 二叉树迭代器_C,为二叉树实现自定义迭代器(长)
  2. api数据加密的定义_API 设计基础规范
  3. python 概率分布类型检验_统计学:假设检验Python案例实现+概率论基础知识回顾...
  4. c++输入错误重新输入_C程序-根据时长和时薪计算工资1.3(解决输入非数字选项退出的bug)...
  5. Xcode11 后Appdelegate自定义UIWindow对象失败详解。
  6. Java注释:类、方法和字段注释
  7. 主成分分析(PCA)及其可视化——matlab
  8. 山东大学 2020级数据库系统 实验四
  9. mysql如何查看表拥有的键_如何查看表或列的所有外键?
  10. ibm招mysql_IBM-ETP实训之MySql基本命令总结