AOP底层原理与注解配置详解

注解开发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;}
}

案例后续思考与设计:

  • 测量真实性

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

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

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

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

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

  • 测量结果展示

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/572157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信企业号-根据code获取成员信息(过期code)

二次请求获取成员信息时&#xff0c;会报如下错误&#xff1b; {"errcode": "40029","errmsg": "invalid code" }这种情况是code过期了&#xff0c;企业号开发文档明确的写着”通过成员授权获取到的code&#xff0c;每次成员授权带上的…

MacOS Apple M1 安装ARM架构的JDK及动态切换版本

JDK下载安装&#xff1a; 咱就是说&#xff0c;ARM版本的JDK就是一个字&#xff0c;真特么快&#xff0c;想变快吗&#xff0c;赶紧下载叭&#xff01;&#xff01; 1、下载地址&#xff1a;https://www.azul.com/downloads/?packagejdk 筛选一下MacOS下ARM架构的JDK版本&…

梯度下降和EM算法,kmeans的em推导

I. 牛顿迭代法给定一个复杂的非线性函数f(x)&#xff0c;希望求它的最小值&#xff0c;我们一般可以这样做&#xff0c;假定它足够光滑&#xff0c;那么它的最小值也就是它的极小值点&#xff0c;满足f′(x0)0&#xff0c;然后可以转化为求方程f′(x)0的根了。非线性方程的根我…

Spring事务详解与使用

Spring事务核心对象 J2EE开发使用分层设计的思想进行&#xff0c;对于简单的业务层转调数据层的单一操作&#xff0c;事务开启在业务层或者数据层并无太大差别&#xff0c;当业务中包含多个数据层的调用时&#xff0c;需要在业务层开启事务&#xff0c;对数据层中多个操作进行组…

一、SQL语法——4-数据库约束

4-数据库约束 1.概述 &#xff08;1&#xff09;约束是在表上强制执行的数据校验规则&#xff0c;约束主要用于保证数据库里数据的完整性&#xff1b;此外当表中的数据存在相互依赖性时&#xff0c;可以保护相关的数据不被删除&#xff1b; &#xff08;2&#xff09;大部分数据…

黑马程序员博学谷Java就业班课程

1、资料全无加密&#xff0c;可任意试看 2、内容包括课程资料 地址:https://www.boxuegu.com/class/outline-1112.html

设计模式一の设计模式详解

一、设计模式定义 设计模式&#xff08;Design Pattern&#xff09;是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式的目的&#xff1a;为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化&#xff1b;…

Spring模板对象

Spring模块对象: 把共性的方法抽取出来固定为一个模板&#xff0c;后续再操作只需要填充内容即可。 比如&#xff1a;淘宝每次买东西都要填写地址&#xff0c;只是每次买的东西不一样&#xff0c;所以可以做一个默认地址&#xff0c;每次买东西都要去选商品就行了&#xff0c;不…

SpringMVC入门案例

SpringMVC 概述&#xff1a; SpringMVC是一种基于Java实现MVC模型的轻量级Web框架 三层架构 表现层&#xff1a;负责数据展示业务层&#xff1a;负责业务处理数据层&#xff1a;负责数据操作 MVC&#xff08;Model View Controller&#xff09;&#xff1a;一种用于设计创建…

中文转换成字节数组

由中文转换成字节数组时由于编码格式的不同&#xff0c;一个汉字所占的字节会有所不同&#xff0c;在做转换时需特别注意。 例如&#xff0c; String.teststr "中国"; byte [] leng teststr.getBytestf-8"); 此时leng长度为6&#xff0c;即一个汉字占3个字节…

SpringMVC请求中的普通、POJO、数组集合类型传参与类转换器

SpringMVC将传递的参数封装到处理器方法的形参中&#xff0c;达到快速访问参数的目的。 普通类型参数传参 参数名与处理器方法形参名保持一致 访问URL&#xff1a; http://localhost/requestParam1?nameitzhuzhu&age14 RequestMapping("/requestParam1")publi…

【贪心算法】POJ-1017

一、题目 Description A factory produces products packed in square packets of the same height h and of the sizes 11, 22, 33, 44, 55, 66. These products are always delivered to customers in the square parcels of the same height h as the products have and of …

SpringMVC响应使用案例(带数据页面跳转,快捷访问路径,返回json数据)

页面跳转 转发&#xff08;默认&#xff09; RequestMapping("/showPage1") public String showPage1() {System.out.println("user mvc controller is running ...");// return "WEB_INF/pages/forward:page.jsp";这种方式不支持重定向&#…

HDU 2874 Connections between cities(LCA离线算法实现)

http://acm.hdu.edu.cn/showproblem.php?pid2874 题意&#xff1a; 求两个城市之间的距离。 思路&#xff1a; LCA题&#xff0c;注意原图可能不连通。 如果不了解离线算法的话&#xff0c;可以看我之前博客写的解释http://www.cnblogs.com/zyb993963526/p/7295894.html 1 #in…

SpringMVC中的异步请求-跨域访问

发送异步请求&#xff1a; <%page pageEncoding"UTF-8" language"java" contentType"text/html;UTF-8" %><a href"javascript:void(0);" id"testAjax">访问springmvc后台controller</a><br/> <…

P1080 国王游戏

题目描述 恰逢 H 国国庆&#xff0c;国王邀请 n 位大臣来玩一个有奖游戏。首先&#xff0c;他让每个大臣在左、右手上面分别写下一个整数&#xff0c;国王自己也在左、右手上各写一个整数。然后&#xff0c;让这 n 位大臣排成一排&#xff0c;国王站在队伍的最前面。排好队后&a…

SpringMVC配置静态资源加载, 中文乱码处理,注解驱动

常规配置&#xff08;Controller加载控制&#xff09; SpringMVC的处理器对应的bean必须按照规范格式开发&#xff0c;未避免加入无效的bean可通过bean加载过滤器进行包含设定或排除设定&#xff0c;表现层bean标注通常设定为Controller业务层与数据层bean加载由Spring控制&…

Android App性能測试

一、内存 1、查看单个应用App最大内存限制 Command&#xff1a;adb shell “getprop|grep heapgrowthlimit” C:\Users\hujiachun>adb shell "getprop|grep heapgrowthlimit" [dalvik.vm.heapgrowthlimit]: [128m] 128M是单个APP最大内存限制&#xff0c;假设Dalv…

SpringMVC-HandlerInterceptor拦截器的使用与参数详解

拦截器概念&#xff1a; 拦截器&#xff08; Interceptor&#xff09;是一种动态拦截方法调用的机制&#xff0c;请求处理过程解析核心原理&#xff1a; AOP思想拦截器链&#xff1a;多个拦截器按照一定的顺序&#xff0c;对原始被调用功能进行增强 作用&#xff1a; 在指定的…

使用FindBugs-IDEA插件找到代码中潜在的问题

另一篇使用文档&#xff0c;参照&#xff1a;https://www.cnblogs.com/huaxingtianxia/p/6703315.html 我们通常都会在APP上线之后,发现各种错误,尤其是空指针异常,这些错误对于用户体验来说是非常不好的,但其实大部分的问题,我们都能够提前发现. 在编写代码的过程中,可能不会时…