目录

1.术语

2.aop除代理方式外的其他实现方式

2.1ajc编译器

2.2 agent 类加载

3.jdk代理

4.cglib代理

5.区别

6.spring对代理的选择

7.切点匹配

7.1 AspectJExpressionPointcut简介

7.2 StaticMethodMatcherPointcut

8.切面advisor与aspect


1.术语

  • Target:目标类,需要被代理的类,如:UserService
  • Advice:通知,所要增强或增加的功能,定义了切面的“什么”和“何时”,模式有Before、After、After-returning,After-throwing和Around
  • Join Point:连接点,应用执行过程中,能够插入切面的所有“点”(时机)
  • Pointcut:切点,实际运行中,选择插入切面的连接点,即定义了哪些点得到了增强。切点定义了切面的“何处”。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
  • Aspect:切面,把横切关注点模块化为特殊的类,这些类称为切面,切面是通知和切点的结合。通知和切点共同定义了切面的全部内容:它是什么,在何时和何处完成其功能
  • Introduction:引入,允许我们向现有的类添加新方法或属性
  • Weaving:织入,把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:编译期、类加载期、运行期

2.aop除代理方式外的其他实现方式

aop实现方式有三种,分别是ajc编译器,agent类加载,和代理方式。spring使用的是代理方式

2.1ajc编译器

ajc编译器实现aop需要使用maven插件

     <plugin><groupId>org.codehaus.mojo</groupId><artifactId>aspectj-maven-plugin</artifactId><version>1.14.0</version><configuration><complianceLevel>1.8</complianceLevel><source>1.8</source><target>1.8</target><showWeaveInfo>true</showWeaveInfo><Xlint>ignore</Xlint><encoding>UTF-8</encoding><skip>true</skip></configuration><executions><execution><configuration><skip>false</skip></configuration><goals><goal>compile</goal></goals></execution></executions></plugin>

下载完插件后使用maven重新编译一下才可以使用,本地idea点击maven的compile。示例代码

定义切面

@Aspect
public class MyAspet {@Before("execution(* com.example.springtest.aop.aspet.MyService.foo())")public void before(){System.out.println("before...");}
}

定义目标类

public class MyService {public void foo(){System.out.println("foo......");}
}

测试与结果

public static void main(String[] args) {new MyService().foo();
}

ajc编译器使用并不广泛,示例中的切面和目标类并没有交给spring管理。它的一个小优势是可以处理静态方法,无论是静态还是非静态的方法都可以使用ajc编译器达到面向切面编程的效果。原因是在编译阶段更改.class源码。增强后的目标类代码▼

2.2 agent 类加载

agent实现方式是在类加载时进行增强的。使用agent类加载方式实现aop需要在VM参数加上

-javaagent:E:\software\apache-maven-3.5.0\conf\rep\org\aspectj\aspectjweaver\1.9.7\aspectjweaver-1.9.7.jar

代码可以使用上边例子的代码,可以达到一样的效果

3.jdk代理

代码示例

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class JdkProxyDemo {interface  Foo{void foo();}static class Targent implements Foo{@Overridepublic void foo() {System.out.println("foo");}}public static void main(String[] args) {Targent targent = new Targent();ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();Foo o = (Foo) Proxy.newProxyInstance(classLoader, new Class[]{Foo.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before............");Object invoke = method.invoke(targent, args);System.out.println("after............");return invoke;}});o.foo();}
}

newProxyInstance方法参数

 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
  • ClassLoader loader                    类加载器,因为代理类没有源码,所以加载类时需要类加载器
  • Class<?>[] interfaces                 被代理的接口
  • InvocationHandler h                   抽象出来的接口,使得代理方法能在调用方实现

InvocationHandler接口方法参数

    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
  • Object proxy                 代理对象
  • Method method            目标方法
  • Object[] args                  目标参数

注意点

  • 直接生成代理类的字节码(ASM)
  • 只能针对接口
  • 代理对象与目标对象都实现了同一个接口,代理对象和目标对象是兄弟关系
  • 当调用第17次时优化调用方式,不再使用反射调用,而是生成真正的类调用(一个方法生成一个代理)

通过arthas的jad查看生成的代理类如下

final class $Proxy0
extends Proxy
implements JdkProxyDemo.Foo {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m3 = Class.forName("com.example.springtest.aop.jdk.JdkProxyDemo$Foo").getMethod("foo", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final void foo() {try {this.h.invoke(this, m3, null);return;}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}
}

4.cglib代理

代码示例

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;public class CglibProxyDemo {static class Target{public void foo(){System.out.println("foo");}}public static void main(String[] args) {Target target = new Target();Target o = (Target) Enhancer.create(Target.class, new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)throws Throwable {System.out.println("before...");Object invoke = method.invoke(target, args);System.out.println("after...");return invoke;}});o.foo();}
}

MethodInterceptor接口的intercept()方法参数说明

Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4)
throws Throwable;
  • Object var1              -->代理对象自己
  • Method var2             -->当前代理类正在执行的方法
  • Object[] var3             -->方法的实际参数
  • MethodProxy var4    -->当前代理类正在执行的方法,可以避免反射

执行目标方法除了Method.invoke()还可以使用intercept()方法的第4个参数MethodProxy执行目标方法,并且使用MethodProxy不会使用反射,理论上效率会高一些

methodProxy.invoke(target,args);
target是目标视力,args是目标方法参数

methodProxy.invokeSuper(o,args);
o是代理对象自己,args是目标方法参数

MethodProxy的FastClass机制

Cglib动态代理执行代理方法效率之所以比JDK高是因为Cglib采用了FastClass机制,他为代理类和被代理类各生成了一个class(jdk动态代理优化则是一个方法生成一个代理类),这个class会为代理类与被代理类的方法分类index。这个index作为方法参数,FastClass可以直接定位到要调用的方法进行调用,这样省去了反射调用,所以效率比JDK动态代理快。

注意点

  • 代理对象与目标类是父子关系,目标类是父类。因此目标类不能是final的,被代理的方法也不能是final,因为要重写方法。

5.区别

  • JDK 动态代理只能对接口进行代理,不能对普通的类进行代理,这是因为 JDK 动态代理生成的代理类,其父类是 Proxy,且 Java 不支持类的多继承。
  • CGLIB 能够代理接口和普通的类,但是被代理的类不能被 final 修饰,且接口中的方法不能使用 final 修饰。
  • JDK 动态代理使用 Java 反射技术进行操作,在生成类上更高效。
  • CGLIB 使用 ASM 框架直接对字节码进行修改,使用了 FastClass 的特性。在某些情况下,类的方法执行会比较高效。

6.spring对代理的选择

spring使用代理是用ProxyFactory factory = new ProxyFactory();工厂生成的代理类,ProxyFactory工厂类有几个属性

  • TargetSource targetSource              目标类
  • List<Class<?>> interfaces               目标类实现的接口
  • boolean proxyTargetClass               布尔类型目标类标志

当proxyTargetClass是false时,查看目标类有没有实现接口,如果有实现接口则生成jdk动态代理,如果目标类没有实现接口则生成cglib动态代理;当proxyTargetClass时true时则永远生成cglib动态代理

7.切点匹配

7.1 AspectJExpressionPointcut简介

spring通过AspectJExpressionPointcut来定义实现AspectJ中的切点语言,就是根据切点表达式来判断目标方法是否应该增,示例▼

private static void Test() throws NoSuchMethodException {AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();pt1.setExpression("execution(* bar())");System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));}static class T1 {@Transactionalpublic void foo() {}public void bar() {}}

 定义切点的表达式有两种方式

  • 切点表达式execution如例子中的"execution(* bar())"
  • 注解表达式@annotation如例子中的"@annotation(org.springframework.transaction.annotation.Transactional)"

AspectJExpressionPointcut实现切点定义有一些局限性,使用@annotation时只能判别出定义在方法上的注解,而不能判别定义在接口或者类上的注解。典型的spring-tx中的@Transactional注解可以放在方法上,接口上,类上。使用AspectJExpressionPointcut实现切点定义就无法判断接口和类上的@Transactional了。

此时可以使用StaticMethodMatcherPointcut,事实上spring对@Transactional注解也是这样做的。

7.2 StaticMethodMatcherPointcut

针对上边的示例重新造如下▼

private static void Test1() throws NoSuchMethodException {StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {@Overridepublic boolean matches(Method method, Class<?> targetClass) {//检测方法上的注解MergedAnnotations from = MergedAnnotations.from(method);if (from.isPresent(Transactional.class)) {return true;}//检测类和父类上的注解MergedAnnotations from1 = MergedAnnotations.from(targetClass,MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);if (from1.isPresent(Transactional.class)) {return true;}return false;}};System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));System.out.println(pt3.matches(T2.class.getMethod("foo"), T3.class));}static class T1 {@Transactionalpublic void foo() {}public void bar() {}}@Transactionalstatic class T2 {public void foo() {}}@Transactionalstatic interface I3 {public void foo();}static class T3 implements I3 {@Overridepublic void foo() {}}

上边例子就可以检测到了类上的注解了。

MergedAnnotations是spring的一个工具,MergedAnnotations.from()方法就是检测目标元素上的所有注解, 常用的参数,载方法▼

  • MergedAnnotations.from(method),检测method上的所有注解
  • MergedAnnotations.from(targetClass)检测类上所有的注解,注意只检测当前类上的注解
  • MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);当使用重载方法,加上SearchStrategy.TYPE_HIERARCHY参数时,表示检测当前类及父类,接口上所有的注解

8.切面advisor与aspect

  • aspect是常用的,即标记了@Aspect注解,开发较为方便的切面。内部可以包含一组或多组通知和切点,spring在使用aspect时,最终还是会将aspect转换为基本的advisor切面。
  • advisor是功能基本,适合在框架内部使用的切面。advisor里只能有一个通知和一个切点。

aop的jdk动态代理和cglib动态代理相关推荐

  1. Spring AOP之---基于JDK动态代理和CGLib动态代理的AOP实现

    AOP(面向切面编程)是OOP的有益补充,它只适合那些具有横切逻辑的应用场合,如性能监测,访问控制,事物管理,日志记录等.至于怎么理解横切逻辑,敲完实例代码也就明白了. 为什么要使用AOP,举个栗子: ...

  2. Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理

    Java动态代理的两种实现方法:JDK动态代理和CGLIB动态代理 代理模式 JDK动态代理 CGLIB动态代理 代理模式 代理模式是23种设计模式的一种,指一个对象A通过持有另一个对象B,可以具有B ...

  3. JAVA 进阶篇 动态代理 JDK动态代理和CGlib动态代理

    JDK动态代理和CGlib动态代理 JDK动态代理: 利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. CGlib动态代理: 利用ASM(开源的Java ...

  4. Java两种动态代理JDK动态代理和CGLIB动态代理

    目录 代理模式 JDK动态代理 cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式.为了对外开放协议,B往往实现了一个 ...

  5. Java中动态代理的两种方式JDK动态代理和cglib动态代理以及区别

    视频功能审核通过了,可以看视频啦!记得点关注啊~ 注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了 记得点关注啊,视频里的wx二维码失效了,wx搜索:"聊5毛钱的java&q ...

  6. 【Spring6】| GoF之代理模式(JDK动态代理和CGLIB动态代理)

    目录 一:GoF之代理模式 1. 对代理模式的理解 2. 静态代理 3. 动态代理 3.1 JDK动态代理 3.2 CGLIB动态代理 一:GoF之代理模式 1. 对代理模式的理解 生活场景1:牛村的 ...

  7. 什么是代理模式?代理模式有什么用?通过一个小程序分析静态代理和动态代理。自己简单实现动态代理。JDK动态代理和CGLIB动态代理的区别。

    1. 代理模式有什么用 ①功能增强,在实现目标功能的基础上,又增加了额外功能.就像生活中的中介一样,他跟两边客户会有私下的交流. ②控制访问,代理不让用户直接和目标接触.就像中间商一样,他们不会让我们 ...

  8. 利用代码分别实现jdk动态代理和cglib动态代理_面试之动态代理

    大家好!我是CSRobot,从今天开始,我将会发布一些技术文章,内容就是结合春招以来的面试所遇到的问题进行分享,首先会对知识点进行一个探讨和整理,在最后会给出一些面试题并作出解答,希望可以帮助到大家! ...

  9. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  10. jdk动态代理和cglib动态代理实现及区别

    代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能. 代理模式又分为:静态代理.jdk动态代 ...

最新文章

  1. 通知 | 2017年清华-青岛数据科学研究院“RONG”奖学金即将开放申请
  2. Oracle 查询今天、昨日、本周、本月和本季度的所有记录
  3. cs224n上完后会会获得证书吗_斯坦福NLP组-CS224n: NLP与深度学习-2019春全套资料分享...
  4. RFID采用率迟迟不涨,原因何在?
  5. Swift 5将强制执行内存独占访问
  6. spring-aop相关概念
  7. mysql支持表情输入_让MySQL支持Emoji表情 mysql 5.6
  8. python数据挖掘笔记】二十.KNN最近邻分类算法分析详解及平衡秤TXT数据集读取
  9. VMware 中的操作系统切换模式后总是连接不上互联网可能的问题之一
  10. Cygwin用户改名、机器改名
  11. idea创建jsp项目与JDBC连接数据库
  12. android+rom+bootloader+flash,【极光ROM】-【三星NOTE10/NOTE10+/5G N97XX-9825】-【V9.0 Android-R-UA1】...
  13. IEC 60664-1-2020【现行】低压供电系统内设备的绝缘配合第1部分:原则、要求和试验
  14. 我就问你1MB和1Mb能一样吗?
  15. 用最简单的方法解决:linux系统重启网络delaying initialization错误
  16. 招聘后台投递设置联动按钮迭代开发总结
  17. 第八课: FTP Server设置与加载
  18. Unity打开.exe文件
  19. 中国男人到底配不配得上中国…
  20. matlab画一元函数图

热门文章

  1. python or语句使用_Python语句序列: “a = (1, 2, 3, None, ( ), [ ]); print(len(a))的运行结果是( )。_学小易找答案...
  2. android开发皮肤检测,android – 检测皮肤和捕获图像
  3. Python爬虫,微信公众号话题标签内容采集打印PDF输出
  4. dSPACE Mid-Size Simulator Introduction
  5. windows应急响应入侵排查思路
  6. 无人机/无人车仿真软件学习与实践---CoppeliaSim教程1---Win10/Win11下的安装
  7. [部分学校JAVAmooc答题解答] 16版.字符串算法
  8. 老牌装机神器“驱动精灵”的没落:随机挑选一名幸运儿进行限速、广告、全家桶……
  9. 【金融统计】R语言获取股票数据,计算资产组合等权重收益率,与上证380指数收益进行比较
  10. 基于Spring MVC + Spring + MyBatis的【密室逃脱游戏主题排行榜】