AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能。
除此以外,aspectj 提供了两种另外的 AOP 底层实现:
-
第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中
-
第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能
-
作为对比,之前学习的代理是运行时生成新的字节码
简单比较的话:
-
aspectj 在编译和加载时,修改目标字节码,性能较高
-
aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
-
但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行
这里开拓一下视野,在实际开发中,我们还是经常使用代理为主。
一、AOP 实现之 ajc 编译器
需要导入的依赖和插件
注意事项:
- 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
- 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器
-
<dependencies>
-
<dependency>
-
<groupId>org.aspectj
</groupId>
-
<artifactId>aspectjweaver
</artifactId>
-
</dependency>
-
-
<dependency>
-
<groupId>org.aspectj
</groupId>
-
<artifactId>aspectjrt
</artifactId>
-
</dependency>
-
</dependencies>
-
-
<build>
-
<plugins>
-
<plugin>
-
<groupId>org.codehaus.mojo
</groupId>
-
<artifactId>aspectj-maven-plugin
</artifactId>
-
<version>1.14.0
</version>
-
<configuration>
-
<complianceLevel>1.8
</complianceLevel>
-
<source>8
</source>
-
<target>8
</target>
-
<showWeaveInfo>true
</showWeaveInfo>
-
<verbose>true
</verbose>
-
<Xlint>ignore
</Xlint>
-
<encoding>UTF-8
</encoding>
-
</configuration>
-
<executions>
-
<execution>
-
<goals>
-
<!-- use this goal to weave all your main classes -->
-
<goal>compile
</goal>
-
<!-- use this goal to weave all your test classes -->
-
<goal>test-compile
</goal>
-
</goals>
-
</execution>
-
</executions>
-
</plugin>
-
</plugins>
-
</build>
主程序类
-
@SpringBootApplication
-
public
class
A09Application {
-
-
private
static
final
Logger
log
= LoggerFactory.getLogger(A09Application.class);
-
-
public
static
void
main
(String[] args) {
-
ConfigurableApplicationContext
context
= SpringApplication.run(A09Application.class, args);
-
MyService
service
= context.getBean(MyService.class);
-
-
log.debug(
"service class: {}", service.getClass());
-
service.foo();
-
-
context.close();
-
}
-
}
增强类
-
@Aspect
// 注意此切面并未被 Spring 管理
-
public
class
MyAspect {
-
-
private
static
final
Logger
log
= LoggerFactory.getLogger(MyAspect.class);
-
-
@Before("execution(* com.itheima.service.MyService.foo())")
-
public
void
before
() {
-
log.debug(
"before()");
-
}
-
}
被增强类
-
@Service
-
public
class
MyService {
-
-
private
static
final
Logger
log
= LoggerFactory.getLogger(MyService.class);
-
-
public
static
void
foo
() {
-
log.debug(
"foo()");
-
}
-
}
结果:MyService的类型是原始目标,而不是代理
-
[main] com.itheima.A09Application : service class:
class
com.itheima.service.MyService
-
[main] com.itheima.aop.MyAspect : before()
-
[main] com.itheima.service.MyService : foo()
这里的增强并不是spring做的增强,因为切面类并未被管理,是通过ajc编译器实现的。
查看MyService的class文件,发现foo()调用了增强类的方法。
那我们去掉与spring相关的东西,重新再测试
-
public
class
A09Application {
-
-
private
static
final
Logger
log
= LoggerFactory.getLogger(A09Application.class);
-
-
public
static
void
main
(String[] args) {
-
new
MyService().foo();
-
}
-
}
发现还是被增强了,原因:修改的是class文件,自然可以生效
-
[main] DEBUG com.itheima.aop.MyAspect - before()
-
[main] DEBUG com.itheima.service.MyService - foo()
总结
-
编译器也能修改 class 实现增强
-
编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强
二、AOP 实现之 agent 类加载
类加载时可以通过 agent 修改 class 实现增强
需要导入的依赖
-
<dependency>
-
<groupId>org.aspectj
</groupId>
-
<artifactId>aspectjweaver
</artifactId>
-
</dependency>
-
-
<dependency>
-
<groupId>org.aspectj
</groupId>
-
<artifactId>aspectjrt
</artifactId>
-
</dependency>
主程序类
-
@SpringBootApplication
-
public
class
A10Application {
-
-
private
static
final
Logger
log
= LoggerFactory.getLogger(A10Application.class);
-
-
public
static
void
main
(String[] args) {
-
ConfigurableApplicationContext
context
= SpringApplication.run(A10Application.class, args);
-
MyService
service
= context.getBean(MyService.class);
-
-
log.debug(
"service class: {}", service.getClass());
-
service.foo();
-
}
-
}
增强类
-
@Aspect
// 注意此切面并未被 Spring 管理
-
public
class
MyAspect {
-
-
private
static
final
Logger
log
= LoggerFactory.getLogger(MyAspect.class);
-
-
@Before("execution(* com.itheima.service.MyService.foo())")
-
public
void
before
() {
-
log.debug(
"before()");
-
}
-
}
被增强类
-
@Service
-
public
class
MyService {
-
-
private
static
final
Logger
log
= LoggerFactory.getLogger(MyService.class);
-
-
public
static
void
foo
() {
-
log.debug(
"foo()");
-
}
-
}
运行时需要在 VM options里加入
-javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址类加载阶段,在class文件看不到。
结果:MyService的类型是原始目标,而不是代理
-
[main] com.itheima.A09Application : service class:
class
com.itheima.service.MyService
-
[main] com.itheima.aop.MyAspect : before()
-
[main] com.itheima.service.MyService : foo()
由于是在类加载阶段(即运行期间)被增强,在class文件看不到,我们需要借助Arthas工具查看。可以进入官网下载jar包。
具体操作如下:
反编译命令:
jad com.itheima.service.MyService
发现MyService在运行期间被增强
三、AOP 实现之 proxy
代理类与普通类的差别:
- 普通类:java原代码 -> 字节码 -> 类加载 -> 使用
- 代理类:运行期间直接生成代理类的字节码
1、JDK动态代理
-
public
class
JdkProxyDemo {
-
interface
Foo {
-
void
foo
();
-
}
-
-
static
class
Target
implements
Foo {
-
public
void
foo
() {
-
System.out.println(
"target foo");
-
}
-
}
-
-
// jdk 只能针对接口代理
-
public
static
void
main
(String[] param)
throws IOException {
-
//目标对象
-
Target
target
=
new
Target();
-
-
//用来加载在运行期间动态生成的字节码
-
ClassLoader
classLoader
= JdkProxyDemo.class.getClassLoader();
-
//参数二:代理类要实现的接口 参数三:代理类调用代理类方法时执行的行为
-
Foo
proxy
= (Foo) Proxy.newProxyInstance(classLoader,
new
Class[]{Foo.class},
new
InvocationHandler() {
-
@Override
-
public Object
invoke
(Object proxy, Method method, Object[] args)
throws Throwable {
-
System.out.println(
"before...");
-
//反射调用目标方法
-
Object
result
= method.invoke(target);
-
System.out.println(
"after...");
-
return result;
//让代理类返回目标方法执行的结果
-
}
-
});
-
proxy.foo();
-
}
-
}
结果:
-
before...
-
target foo
-
after...
注意:
jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系
2、Cglib代理
-
public
class
CglibProxyDemo {
-
static
class
Target {
-
public
void
foo
() {
-
System.out.println(
"target foo");
-
}
-
}
-
-
// 代理是子类型, 目标是父类型
-
public
static
void
main
(String[] param) {
-
Target
target
=
new
Target();
-
-
Target
proxy
= (Target) Enhancer.create(Target.class,
new
MethodInterceptor() {
-
-
@Override
-
public Object
intercept
(Object p, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
-
System.out.println(
"before...");
-
//用反射调用目标方法
-
Object
result
= method.invoke(target);
-
System.out.println(
"after...");
-
return result;
-
}
-
});
-
-
proxy.foo();
-
}
-
}
结果:
-
before...
-
target foo
-
after...
注意:
cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系
根据上述分析 final 类,final方法无法被 cglib 增强,因为是子父类的关系,相当于代理类对目标类方法的重写。
invoke 与 invokeSuper的区别
与jdk动态代理不同的是,cglib可以避免使用反射调用目标方法。
使用methodProxy的invoke 或 invokeSuper方法
-
public
static
void
main
(String[] param) {
-
Target
target
=
new
Target();
-
-
Target
proxy
= (Target) Enhancer.create(Target.class,
new
MethodInterceptor() {
-
-
@Override
-
public Object
intercept
(Object p, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
-
System.out.println(
"before...");
-
Object
result
= methodProxy.invoke(target, args);
-
System.out.println(
"after...");
-
return result;
-
}
-
});
-
-
proxy.foo();
-
}
两者区别
-
//需要目标对象
-
methodProxy.invoke(target, args)
-
-
//需要代理对象
-
methodProxy.invokeSuper(p, args);
spring使用的是invoke方法
四、JDK动态代理进阶
学习目标:代理类的内部原理
- 方法重写可以增强逻辑
- 通过接口回调将【增强逻辑】置于代理类之外
- 配合接口方法反射(也是多态),就可以再联动调用目标方法
1、模拟JDK动态代理
-
public
class
A12 {
-
-
interface
Foo {
-
void
foo
();
-
int
boo
();
-
}
-
-
//目标类
-
static
class
Target
implements
Foo {
-
-
public
void
foo
() {
-
System.out.println(
"target foo");
-
}
-
-
@Override
-
public
int
boo
() {
-
System.out.println(
"target boo");
-
return
100;
-
}
-
}
-
-
//提供动态执行增强逻辑的方法
-
interface
InvocationHandler {
-
Object
invoke
(Object proxy, Method method, Object[] args)
throws Throwable;
-
}
-
-
}
代理类
-
public
class
$Proxy0
implements
Foo {
-
-
private InvocationHandler invocationHandler;
-
-
public $Proxy0() {
-
}
-
-
public $Proxy0(InvocationHandler invocationHandler) {
-
this.invocationHandler = invocationHandler;
-
}
-
-
static Method foo;
-
static Method boo;
-
-
static {
-
try {
-
//根据方法名获取Method属性
-
foo = Foo.class.getMethod(
"foo");
-
boo = Foo.class.getMethod(
"boo");
-
}
catch (NoSuchMethodException e) {
-
throw
new
NoSuchMethodError(e.getMessage());
-
}
-
}
-
-
@Override
-
public
void
foo
() {
-
try {
-
invocationHandler.invoke(
this, foo,
new
Object[
0]);
-
}
catch (RuntimeException | Error e) {
-
throw e;
-
}
catch (Throwable e){
-
throw
new
UndeclaredThrowableException(e);
-
}
-
}
-
-
@Override
-
public
int
boo
() {
-
try {
-
int
result
= (
int) invocationHandler.invoke(
this, boo,
new
Object[
0]);
-
return result;
-
}
catch (RuntimeException | Error e) {
-
throw e;
-
}
catch (Throwable e){
-
throw
new
UndeclaredThrowableException(e);
-
}
-
}
-
}
-
这里说明一下异常处理:
- 运行时异常:RuntimeException,Error,无需捕获,直接抛出
- 检查异常:Throwable,接口不一定有Throwable,需要将检查异常转换为运行时异常抛出
测试
-
Foo
foo
=
new
$Proxy0(
new
InvocationHandler() {
-
@Override
-
public Object
invoke
(Object proxy, Method method, Object[] args)
throws Throwable {
-
System.out.println(
"before...");
-
Object
result
= method.invoke(target);
-
System.out.println(
"after...");
-
return result;
-
}
-
});
-
foo.foo();
-
foo.boo();
结果:
-
before...
-
target foo
-
after...
-
before...
-
target boo
-
after...
JDK生成代理类时并没有经历源码阶段,编译阶段,而是直接进入字节码阶段,现在看到的java源码是通过Arthas工具对它进行了反编译, 直接生成字节码的底层技术是ASM,被广泛应用于JDK,Spring等框架,它的作用就是在运行期间动态生成字节码。
和我们自己写的代理类进行比较发现比我们多实现了Object中的toString,equals,hashCode方法,并且继承了Proxy类。
-
package com.itheima.a11;
-
-
import com.itheima.a11.JdkProxyDemo;
-
import java.lang.reflect.InvocationHandler;
-
import java.lang.reflect.Method;
-
import java.lang.reflect.Proxy;
-
import java.lang.reflect.UndeclaredThrowableException;
-
-
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.itheima.a11.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);
-
}
-
}
-
}
Proxy类内部已经定义了 InvocationHandler
-
public
class
Proxy
implements
java.io.Serializable {
-
-
protected InvocationHandler h;
-
-
protected
Proxy
(InvocationHandler h) {
-
Objects.requireNonNull(h);
-
this.h = h;
-
}
-
}
注意:代理增强是借助多态来实现,因此成员变量、静态方法、final 方法均不能通过代理实现
2、方法反射优化
由于ASM学习成本比较高,这里直接给出结论,感兴趣的小伙伴可以去看视频。
优化:
-
前 16 次反射性能较低
-
第 17 次调用会生成代理类,优化为非反射调用
五、cglib 代理进阶
和 JDK 动态代理原理查不多
-
回调的接口换了一下,InvocationHandler 改成了 MethodInterceptor
-
调用目标时有所改进,见下面代码片段
-
method.invoke 是反射调用,必须调用到足够次数才会进行优化
-
methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
-
methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象
-
1、模拟cglib代理
目标类
-
public
class
Target {
-
public
void
save
() {
-
System.out.println(
"save()");
-
}
-
-
public
void
save
(int i) {
-
System.out.println(
"save(int)");
-
}
-
-
public
void
save
(long j) {
-
System.out.println(
"save(long)");
-
}
-
}
代理类
-
public
class
Proxy
extends
Target{
-
-
private MethodInterceptor methodInterceptor;
-
-
public
Proxy
() {
-
}
-
-
public
Proxy
(MethodInterceptor methodInterceptor) {
-
this.methodInterceptor = methodInterceptor;
-
}
-
-
static Method save0;
-
static Method save1;
-
static Method save2;
-
-
static {
-
try {
-
save0 = Target.class.getMethod(
"save");
-
save1 = Target.class.getMethod(
"save",
int.class);
-
save2 = Target.class.getMethod(
"save",
long.class);
-
}
catch (NoSuchMethodException e) {
-
throw
new
NoSuchMethodError(e.getMessage());
-
}
-
}
-
-
@Override
-
public
void
save
() {
-
try {
-
methodInterceptor.intercept(
this, save0,
new
Object[
0],
null);
-
}
catch (RuntimeException | Error e){
-
throw e;
-
}
catch (Throwable throwable) {
-
throw
new
UndeclaredThrowableException(throwable);
-
}
-
}
-
-
@Override
-
public
void
save
(int i) {
-
try {
-
methodInterceptor.intercept(
this, save1,
new
Object[]{i},
null);
-
}
catch (RuntimeException | Error e){
-
throw e;
-
}
catch (Throwable throwable) {
-
throw
new
UndeclaredThrowableException(throwable);
-
}
-
}
-
-
@Override
-
public
void
save
(long j) {
-
try {
-
methodInterceptor.intercept(
this, save2,
new
Object[]{j},
null);
-
}
catch (RuntimeException | Error e){
-
throw e;
-
}
catch (Throwable throwable) {
-
throw
new
UndeclaredThrowableException(throwable);
-
}
-
}
-
}
测试
-
public
class
A13 {
-
public
static
void
main
(String[] args) {
-
Target
target
=
new
Target();
-
Proxy
proxy
=
new
Proxy(
new
MethodInterceptor() {
-
@Override
-
public Object
intercept
(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
-
System.out.println(
"before...");
-
return method.invoke(target, args);
-
}
-
});
-
proxy.save();
-
proxy.save(
1);
-
proxy.save(
2L);
-
}
-
}
结果:
-
before...
-
save()
-
before...
-
save(
int)
-
before...
-
save(
long)
上面的代码其实和模拟JDK动态代理的代码差不多,重点是下面的避免反射调用的代码。
2、cglib 避免反射调用
给代理类增加原始功能的方法以及MethodProxy
-
public
class
Proxy
extends
Target{
-
-
private MethodInterceptor methodInterceptor;
-
-
public
Proxy
() {
-
}
-
-
public
Proxy
(MethodInterceptor methodInterceptor) {
-
this.methodInterceptor = methodInterceptor;
-
}
-
-
static Method save0;
-
static Method save1;
-
static Method save2;
-
static MethodProxy save0Proxy;
-
static MethodProxy save1Proxy;
-
static MethodProxy save2Proxy;
-
static {
-
try {
-
save0 = Target.class.getMethod(
"save");
-
save1 = Target.class.getMethod(
"save",
int.class);
-
save2 = Target.class.getMethod(
"save",
long.class);
-
/*
-
参数一:目标类型 参数二:代理类型
-
参数三:方法参数描述符
-
参数四:带增强功能的方法名 参数五:带原始功能的方法名
-
*/
-
save0Proxy = MethodProxy.create(Target.class, Proxy.class,
"()V",
"save",
"saveSuper");
-
save1Proxy = MethodProxy.create(Target.class, Proxy.class,
"(I)V",
"save",
"saveSuper");
-
save2Proxy = MethodProxy.create(Target.class, Proxy.class,
"(J)V",
"save",
"saveSuper");
-
}
catch (NoSuchMethodException e) {
-
throw
new
NoSuchMethodError(e.getMessage());
-
}
-
}
-
-
//>>>>>>>>>>>>>>>>>>>>>>>带原始功能的方法
-
public
void
saveSuper
(){
-
super.save();
-
}
-
public
void
saveSuper
(int i){
-
super.save(i);
-
}
-
public
void
saveSuper
(long j){
-
super.save(j);
-
}
-
-
//>>>>>>>>>>>>>>>>>>>>>>>带增强功能的方法
-
@Override
-
public
void
save
() {
-
try {
-
methodInterceptor.intercept(
this, save0,
new
Object[
0], save0Proxy);
-
}
catch (RuntimeException | Error e){
-
throw e;
-
}
catch (Throwable throwable) {
-
throw
new
UndeclaredThrowableException(throwable);
-
}
-
}
-
-
@Override
-
public
void
save
(int i) {
-
try {
-
methodInterceptor.intercept(
this, save1,
new
Object[]{i}, save1Proxy);
-
}
catch (RuntimeException | Error e){
-
throw e;
-
}
catch (Throwable throwable) {
-
throw
new
UndeclaredThrowableException(throwable);
-
}
-
}
-
-
@Override
-
public
void
save
(long j) {
-
try {
-
methodInterceptor.intercept(
this, save2,
new
Object[]{j}, save2Proxy);
-
}
catch (RuntimeException | Error e){
-
throw e;
-
}
catch (Throwable throwable) {
-
throw
new
UndeclaredThrowableException(throwable);
-
}
-
}
-
}
当调用 MethodProxy 的 invoke 或 invokeSuper 方法时, 会动态生成两个类
- ProxyFastClass 配合代理对象一起使用, 避免反射
- TargetFastClass 配合目标对象一起使用, 避免反射 (Spring 用的这种)
这两个类会继承FastClass抽象类,为了演示简单,下面的getIndex 和 invoke是实现它的两个方法。
1)invoke方法的无反射演示
-
public
class
TargetFastClass {
-
//方法签名
-
static
Signature
s0
=
new
Signature(
"save",
"()V");
-
static
Signature
s1
=
new
Signature(
"save",
"(I)V");
-
static
Signature
s2
=
new
Signature(
"save",
"(J)V");
-
-
// 获取目标方法的编号
-
/*
-
Target
-
save() 0
-
save(int) 1
-
save(long) 2
-
signature 包括方法名字、参数返回值
-
*/
-
public
int
getIndex
(Signature signature) {
-
if (signature.equals(s0)){
-
return
0;
-
}
else
if (signature.equals(s1)){
-
return
1;
-
}
else
if (signature.equals(s2)){
-
return
2;
-
}
-
return -
1;
-
}
-
-
// 根据方法编号, 正常调用目标对象方法
-
public Object
invoke
(int index, Object target, Object[] args) {
-
if (index ==
0){
-
((Target) target).save();
-
return
null;
-
}
else
if (index ==
1){
-
((Target) target).save((
int) args[
0]);
-
return
null;
-
}
else
if (index ==
2){
-
((Target) target).save((
long) args[
0]);
-
return
null;
-
}
else {
-
throw
new
RuntimeException(
"无此方法");
-
}
-
}
-
-
//模拟操作
-
public
static
void
main
(String[] args) {
-
//首次使用MethodProxy方法时被创建
-
TargetFastClass
fastClass
=
new
TargetFastClass();
-
//MethodProxy在创建时由于记录了方法签名,所以能够调用getIndex方法得到方法编号
-
int
i
= fastClass.getIndex(
new
Signature(
"save",
"()V"));
-
//调用MethodProxy的invoke方法,间接会调用到fastClass的invoke方法
-
fastClass.invoke(i,
new
Target(),
new
Object[
0]);
-
}
-
}
2)invokeSuper方法的无反射演示
-
public
class
ProxyFastClass {
-
/*
-
调用的是代理类带原始功能的方法而不是增强方法,不然会陷入死循环
-
*/
-
static
Signature
s0
=
new
Signature(
"saveSuper",
"()V");
-
static
Signature
s1
=
new
Signature(
"saveSuper",
"(I)V");
-
static
Signature
s2
=
new
Signature(
"saveSuper",
"(J)V");
-
-
// 获取代理方法的编号
-
/*
-
Target
-
saveSuper() 0
-
saveSuper(int) 1
-
saveSuper(long) 2
-
signature 包括方法名字、参数返回值
-
*/
-
public
int
getIndex
(Signature signature) {
-
if (signature.equals(s0)){
-
return
0;
-
}
else
if (signature.equals(s1)){
-
return
1;
-
}
else
if (signature.equals(s2)){
-
return
2;
-
}
-
return -
1;
-
}
-
-
// 根据方法编号, 正常调用目标对象方法
-
public Object
invoke
(int index, Object proxy, Object[] args) {
-
if (index ==
0){
-
((Proxy) proxy).saveSuper();
-
return
null;
-
}
else
if (index ==
1){
-
((Proxy) proxy).saveSuper((
int) args[
0]);
-
return
null;
-
}
else
if (index ==
2){
-
((Proxy) proxy).saveSuper((
long) args[
0]);
-
return
null;
-
}
else {
-
throw
new
RuntimeException(
"无此方法");
-
}
-
}
-
-
public
static
void
main
(String[] args) {
-
ProxyFastClass
fastClass
=
new
ProxyFastClass();
-
int
i
= fastClass.getIndex(
new
Signature(
"saveSuper",
"()V"));
-
fastClass.invoke(i,
new
Proxy(),
new
Object[
0]);
-
}
-
}
总结
为什么有这么麻烦的一套东西呢?
-
避免反射,提高性能,代价是一个代理类配两个 FastClass 类,代理类中还得增加仅调用 super 的一堆方法
-
用编号处理方法对应关系比较省内存,另外,最初获得方法顺序是不确定,这个过程没法固定死
与JDK动态代理相比,CGLIB代理类数目相对较少,只有两个类。而JDK动态代理在调用到第十七次后会生成代理类去优化为非反射调用,并且是一个方法对应一个代理类。
转载:https://blog.csdn.net/qq_51409098/article/details/127689455