AOP底层原理与注解配置详解
注解开发AOP制作步骤:
在XML格式基础上
- 导入坐标(伴随spring-context坐标导入已经依赖导入完成
- 开启AOP注解支持
- 配置切面@Aspect
- 定义专用的切入点方法,并配置切入点@Pointcut
- 为通知方法配置通知类型及对应切入点@Before
注解开发AOP注意事项:
- 切入点最终体现为一个方法,无参无返回值,无实际方法体内容,但不能是抽象方法
- 引用切入点时必须使用方法调用名称,方法后面的()不能省略
- 切面类中定义的切入点只能在当前类中使用,如果想引用其他类中定义的切入点使用“类名.方法名()”引用
- 可以在通知类型注解后添加参数,实现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底层原理与注解配置详解相关推荐
- spring之旅第四篇-注解配置详解
spring之旅第四篇-注解配置详解 一.引言 最近因为找工作,导致很长时间没有更新,找工作的时候你会明白浪费的时间后面都是要还的,现在的每一点努力,将来也会给你回报的,但行好事,莫问前程!努力总不会 ...
- Nginx 反向代理工作原理简介与配置详解
Nginx 反向代理工作原理简介与配置详解 测试环境 CentOS 6.8-x86_64 nginx-1.10.0 下载地址:http://nginx.org/en/download.html 安装 ...
- 使用LVS实现负载均衡原理及安装配置详解
使用LVS实现负载均衡原理及安装配置详解 负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群.常用的负载均衡开源软件有nginx.lvs.haproxy,商业的硬件负载均 ...
- servlet过滤器中的注解配置详解
文章目录 注解配置@WebFilter中的属性 1. urlPatterns 2. initParams 3. dispatcherTypes 多个过滤器的执行顺序 销毁阶段的触发机制 注解配置@We ...
- SpringMVC原理及非注解配置详解
如需转发请标明出处:http://www.cnblogs.com/gudu1/p/7222556.html 1. Spring介绍 Spring MVC是Spring提供的一个强大而灵活的web框架. ...
- springboot 中使用 Mybatis 注解 配置 详解
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 传参方式 使用不同的传参方式: 使用@Param 之前博文中的项目使用了这种简单的传参方式: @In ...
- Spring Boot中使用MyBatis注解配置详解
之前在Spring Boot中整合MyBatis时,采用了注解的配置方式,相信很多人还是比较喜欢这种优雅的方式的,也收到不少读者朋友的反馈和问题,主要集中于针对各种场景下注解如何使用,下面就对几种常见 ...
- cacheable注解原理_Cacheable注解使用详解
完成Redis基础配置之后,就可以使用Redis对数据进行缓存了. 最简单的方式就是使用springframe为我们提供的@Cacheable注解,以下是@Cacheable注解的具体使用方式. @C ...
- RabbitMQ底层原理及安装使用详解
1.RabbitMQ原理概述 2.RabbitMQ安装和管理 3-1.Direct(直接交换器) 4.消息发布时的权衡 5.消息消费时的权衡 6.消息消费的拒绝 7.Spring整合RabbitMQ ...
最新文章
- ExtJS4.2学习(14)基于表格的扩展插件(2)
- hdu 4502(DP)
- oracle 行送,Oracle 行专列
- Tengine开源新特性:如何让HTTPS处理能力轻松翻倍?
- Taro+react开发(36)每一个节点要一个view包裹
- (软件工程复习核心重点)第七章软件维护-第三节:软件可维护性
- python判断数据是否在另一个集合中_python判断一个集合是否包含了另外一个集合中所有项的方法...
- sqlserver分区表索引
- 2020 年,Serverless 将给大前端带来什么样的变化?
- php 读xml的两种方式
- 作业必备:【操作系统实验报告】实验一:熟悉Ubuntu环境(后续会更新~)
- Java实现生成32位UUID工具类
- 小米台灯突然自己亮了_米家台灯Pro,工作读书随我选
- DCDC中电感的计算
- 流程图设计(泳道图 | 任务流程图 | 页面流程图)
- 友盟分享 qq没有存储权限 分享失败
- java生气_Java来抢饭碗,C++可别生气
- 2018年高教社杯全国大学生数学建模竞赛题目
- source insight的使用
- PaddleX---MobileNetV3_ssld图像分类
热门文章
- java 二叉树迭代器_C,为二叉树实现自定义迭代器(长)
- api数据加密的定义_API 设计基础规范
- python 概率分布类型检验_统计学:假设检验Python案例实现+概率论基础知识回顾...
- c++输入错误重新输入_C程序-根据时长和时薪计算工资1.3(解决输入非数字选项退出的bug)...
- Xcode11 后Appdelegate自定义UIWindow对象失败详解。
- Java注释:类、方法和字段注释
- 主成分分析(PCA)及其可视化——matlab
- 山东大学 2020级数据库系统 实验四
- mysql如何查看表拥有的键_如何查看表或列的所有外键?
- ibm招mysql_IBM-ETP实训之MySql基本命令总结