reference:https://blog.csdn.net/weixin_41427129/article/details/113561980
一、概述
本文主要讲解的是 CGLIB 的常用 API 及其使用方式。使用的 CGLIB 依赖如下所示:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
首先创建接口 CglibSampleInterface
和类 CglibSampleClass
,如下所示:
1 public interface CglibSampleInterface { 2 String show(String name); 3 } 4 5 public class CglibSampleClass { 6 public String show(String name) { 7 return String.format("%s show love to you!", name); 8 } 9 }
二、API 详解
2.1 Enhancer
Enhancer
即字节码增强器,它和 JDK 动态代理中的 Proxy
类似,是 CGLIB 中最常用的一个类,既能代理普通的 Java 类,也能代理接口。Enhancer
通过创建一个被代理类的子类来拦截所有的方法调用(包括 Object#toString()
、Object#hashCode()
),但是它不能拦截 final
修饰的方法(如 Object#getClass()
),也不能代理 final
修饰的类。示例如下所示:
1 public class EnhancerClass { 2 public static void main(String[] args) throws Exception { 3 // 字节码增强器 4 Enhancer enhancer = new Enhancer(); 5 // 设置代理类的父类 6 enhancer.setSuperclass(CglibSampleClass.class); 7 // 使用 FixedValue,拦截返回值,每次返回固定值 "Robin walk to you!" 8 enhancer.setCallback((FixedValue) () -> "Robin walk to you!"); 9 10 // 创建代理对象 11 CglibSampleClass sampleClass = (CglibSampleClass) enhancer.create(); 12 13 System.out.println(sampleClass.show("Robin")); 14 System.out.println(sampleClass.show("Nami")); 15 System.out.println(sampleClass.toString()); 16 17 // 无法对 final 修饰的 getClass() 方法进行拦截 18 System.out.println(sampleClass.getClass()); 19 // 因为 hashCode() 需要返回的是 Number 类型,但是 FixedValue 返回值是 String 类型,无法实现类型转换,故会抛出异常 20 System.out.println(sampleClass.hashCode()); 21 } 22 }
上述示例使用 FixedValue()
拦截所有的方法调用(包括非 final
修饰的 show()
、toString()
、hashCode()
方法)并返回相同的值,但是,由于 hashCode()
方法的返回值类型是 int
型,而我们返回的是一个 String
,所以才会抛出 ClassCastException
异常。
此外,create(Class[] argumentTypes, Object[] arguments)
也可创建代理对象,用来匹配被增强类的不同构造方法,第一参数表示构造方法的参数类型,第二个参数表示构造方法的参数值,这两个参数都是数组类型。也可以使用 Enhancer#createClass()
来创建类的字节码,然后使用字节码加载完成后的类动态生成代理对象。
结果如下所示:
public class EnhancerClass { public static void main(String[] args) throws Exception { // 字节码增强器 Enhancer enhancer = new Enhancer(); // 设置代理类的父类 enhancer.setSuperclass(CglibSampleClass.class); // 使用 FixedValue,拦截返回值,每次返回固定值 "Robin walk to you!" enhancer.setCallback((FixedValue) () -> "Robin walk to you!"); // 创建代理对象 CglibSampleClass sampleClass = (CglibSampleClass) enhancer.create(); System.out.println(sampleClass.show("Robin")); System.out.println(sampleClass.show("Nami")); System.out.println(sampleClass.toString()); // 无法对 final 修饰的 getClass() 方法进行拦截 System.out.println(sampleClass.getClass()); // 因为 hashCode() 需要返回的是 Number 类型,但是 FixedValue 返回值是 String 类型,无法实现类型转换,故会抛出异常 System.out.println(sampleClass.hashCode()); } }
代理接口的示例如下所示,结果与上面的相同。
1 public class EnhancerInterface { 2 public static void main(String[] args) throws Exception { 3 Enhancer enhancer = new Enhancer(); 4 5 // 设置被代理的接口 6 enhancer.setInterfaces(new Class[]{CglibSampleInterface.class}); 7 8 enhancer.setCallback((FixedValue) () -> "Robin walk to you!"); 9 10 CglibSampleInterface cglibSampleInterface = (CglibSampleInterface) enhancer.create(); 11 12 System.out.println(cglibSampleInterface.show("Robin")); 13 System.out.println(cglibSampleInterface.show("Nami")); 14 System.out.println(cglibSampleInterface.toString()); 15 System.out.println(cglibSampleInterface.getClass()); 16 System.out.println(cglibSampleInterface.hashCode()); 17 } 18 }
2.2 Callback
Callback
即回调,其接口如下所示,是一个标识接口(不含任何方法)。它的回调时机是被代理类的方法被调用的时候,即被代理类的方法被调用时,Callback
的实现逻辑就会被调用。此外,可通过 Enhancer#setCallback()
和 Enhancer#setCallbacks()
设置 Callback
,若设置了多个 Callback
,则会按照设置的顺序进行回调。CGLIB 提供了以下几种 Callback
的子类:
1 package net.sf.cglib.proxy; 2 3 public interface Callback { 4 }
NoOp
FixedValue
InvocationHandler
MethodInterceptor
Dispatcher
LazyLoader
2.2.1 NoOp
NoOp
即 No Operation,不做任何操作,该回调实现只是简单地将方法调用委托给被代理类的原始方法,即不加任何操作地调用原始类的原始方法,因此,该回调实现也不能做接口代理。实例如下所示:
public class NoOpDemo {
public static void main(String[] args) throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CglibSampleClass.class);
// 设置 Callback 回调为 NoOp
enhancer.setCallback(NoOp.INSTANCE);
CglibSampleClass sampleClass = (CglibSampleClass) enhancer.create();
System.out.println(sampleClass.show("Robin"));
}
}
// 结果如下所示:
Robin show love to you!
2.2.2 FixedValue
FixedValue
即固定值。它提供了一个 loadObject()
方法并返回一个原方法调用想要的固定结果。此外,该 Callback
中看不到任何原方法的信息,也就没有调用原方法的逻辑。需要注意的是,若 loadObject()
方法的返回值并不能转换成原方法的返回值类型,则会抛出类型转换异常 (ClassCastException
)。示例即前面两个 Enhancer
的 Demo。
2.2.3 InvocationHandler
InvocationHandler
即 net.sf.cglib.proxy.InvocationHandler
,它和 JDK 动态代理中 java.lang.reflect.InvocationHandler
的功能类似,同样也提供了如下的一个方法:
Object invoke(Object proxy, Method method, Object[] objects)
- 1
不过需要注意的是,所有对 proxy
对象的方法调用都会被委托给同一个 InvocationHandler
,所以可能会导致无限循环 (因为 invoke
中调用的任何被代理类的方法,均会重新代理到 invoke()
中)
public class InvocationHandlerDeadLoopDemo {
public static void main(String[] args) throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CglibSampleClass.class);
// 设置 Callback 的子类 InvocationHandler
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
// 错误做法,会重新调用 InvocationHandler 的 invoke()
// return method.invoke(proxy, objects);
if (!Objects.equals(method.getDeclaringClass(), Object.class) && Objects.equals(String.class, method.getReturnType())) {
return "Nami fall in love!";
}
return "No one fall in love with you!";
}
});
CglibSampleClass sampleClass = (CglibSampleClass) enhancer.create();
System.out.println(sampleClass.show("Robin"));
}
}
// 结果如下所示:
Nami fall in love!
2.2.4 MethodInterceptor
MethodInterceptor
,即方法拦截器,它可以实现类似于 AOP 编程中的环绕增强(Around Advice)。它只有一个方法:
public Object intercept(Object proxy, // 代理对象
java.lang.reflect.Method method, // 方法
Object[] args, // 方法参数
MethodProxy methodProxy // 方法代理
) throws Throwable
设置了 MethodInterceptor
后,代理类的所有方法调用都会转而执行这个接口中的 intercept
方法而不是原方法。若需要在 intercept
方法中执行原方法,有以下两种方式:
- 使用参数
method
基于代理对象proxy
进行反射调用,但是使用方法代理methodProxy
效率会更高(methodProxy
基于整数数字的索引来直接调用方法)。 - 使用
MethodProxy
调用invokeSuper()
执行原方法,这种方式效率更好,推荐使用这种方式。
需要注意的是,使用 MethodProxy#invokeSuper()
相当于通过方法代理直接调用原类的对应方法,若调用 MethodProxy#invoke()
会进入死循环导致爆栈,原因跟 InvocationHandler
差不多。
2.2.5 Dispatcher
Dispatcher
即分发器,提供了一个 Object loadObject() throws Exception
方法,每次对增强对象进行方法调用都会回调 Dispatcher#loadObject()
方法并返回一个被代理类的对象来调用原方法。Dispatcher
可以类比为 Spring 中的 Prototype
类型。示例如下所示:
public class DispatcherDemo {
private static final AtomicInteger COUNTER = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
// 被代理接口的对象
CglibSampleInterfaceImpl impl = new CglibSampleInterfaceImpl();
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(new Class[]{CglibSampleInterface.class});
enhancer.setCallback(new Dispatcher() {
@Override
public Object loadObject() throws Exception {
COUNTER.incrementAndGet();
// 返回的被代理接口的对象
return impl;
}
});
CglibSampleInterface cglibSampleInterface = (CglibSampleInterface) enhancer.create();
System.out.println(cglibSampleInterface.show("Robin"));
System.out.println(cglibSampleInterface.show("Nami"));
System.out.println(COUNTER.get());
}
private static class CglibSampleInterfaceImpl implements CglibSampleInterface {
public CglibSampleInterfaceImpl() {
System.out.println("CglibSampleInterfaceImpl init...");
}
@Override
public String show(String name) {
return String.format("%s show love to you!", name);
}
}
}
// 结果如下所示:
CglibSampleInterfaceImpl init...
Robin show love to you!
Nami show love to you!
2
如输出结果所示,计数器的结果为 2,可以验证该结论:每次调用方法都会回调 Dispatcher
中的实例进行调用。
2.2.6 LazyLoader
LazyLoader
即懒加载器,它只提供了一个方法 Object loadObject() throws Exception
,loadObject()
方法会在第一次被代理类的方法调用时触发,它返回一个被代理类的对象,这个对象会被存储起来然后负责所有被代理类方法的调用。
适用于被代理类或者代理类的对象的创建比较麻烦,且不确定它是否会被使用。LazyLoader
可以类比为 Spring 中 Lazy 模式的 Singleton
。示例如下所示:
public class LazyLoaderDemo {
private static final AtomicInteger COUNTER = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
// 被代理接口的对象
CglibSampleInterfaceImpl impl = new CglibSampleInterfaceImpl();
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(new Class[]{CglibSampleInterface.class});
enhancer.setCallback(new LazyLoader() {
@Override
public Object loadObject() throws Exception {
COUNTER.incrementAndGet();
// 返回被代理接口的对象
return impl;
}
});
CglibSampleInterface cglibSampleInterface = (CglibSampleInterface) enhancer.create();
System.out.println(cglibSampleInterface.show("Robin"));
System.out.println(cglibSampleInterface.show("Nami"));
System.out.println(COUNTER.get());
}
private static class CglibSampleInterfaceImpl implements CglibSampleInterface {
public CglibSampleInterfaceImpl() {
System.out.println("CglibSampleInterfaceImpl init...");
}
@Override
public String show(String name) {
return String.format("%s show love to you!", name);
}
}
}
// 结果如下所示:
CglibSampleInterfaceImpl init...
Robin show love to you!
Nami show love to you!
1
如输出结果所示,计数器的结果为 1,可以验证该结论:LazyLoader
中的实例只回调了1次。
2.3 BeanCopier
BeanCopier
即 JavaBean 属性拷贝器,提供从一个 JavaBean 实例中拷贝属性到另一个 JavaBean 实例中的功能,类型必须完全匹配,属性才能拷贝成功(基本数据类型和其包装类不属于相同类型)。它还提供了一个 net.sf.cglib.core.Converter
转换器回调接口让使用者控制拷贝的过程。
此外,BeanCopier
内部使用了缓存和基于 ASM 动态生成 BeanCopier
的子类(该子类实现的转换方法中直接使用实例的 Getter
和 Setter
方法),拷贝速度极快(BeanCopier
属性拷贝比直接的 Setter
、Getter
稍慢,原因在于首次需要动态生成 BeanCopier
的子类,一旦子类生成完成之后就和直接调用 Setter
、Getter
效率一致,但是效率远远高于其他使用反射的工具类库)。示例如下所示:
public class BeanCopierDemo {
// 缓存 BeanCopier 实例,BeanCopier 生成是一个耗时的操作
private static final Map<String, BeanCopier> CACHE = new ConcurrentHashMap<>();
public static void main(String[] args) throws Exception {
//这里 useConverter 设置为 false,调用 copy 方法的时候不能传入转换器实例
BeanCopier beanCopier;
String key = generateCacheKey(Person.class, Person.class);
if (CACHE.containsKey(key)) {
beanCopier = CACHE.get(key);
} else {
beanCopier = BeanCopier.create(Person.class, Person.class, false);
CACHE.put(key, beanCopier);
}
Person person = new Person();
person.setId(10086L);
person.setName("Robin");
person.setAge(25);
Person newPerson = new Person();
beanCopier.copy(person, newPerson, null); //这里转换器实例要传 null
System.out.println(newPerson);
}
private static String generateCacheKey(Class<?> source, Class<?> target) {
return String.format("%s-%s", source.getName(), target.getName());
}
@ToString
@Data
private static class Person {
private Long id;
private String name;
private Integer age;
}
}
// 结果如下所示:
BeanCopierDemo.Person(id=10086, name=throwable, age=25)
2.4 ImmutableBean
ImmutableBean 即不可变的 Bean,它可以创建一个对象的包装类,但这个包装类是不可变的,否则会抛出 IllegalStateException
异常,但是可以通过操作底层对象来改变包装类的对象。示例如下所示:
public class ImmutableBeanDemo {
public static void main(String[] args) throws Exception {
Person person = new Person();
person.setName("波雅汉考克");
Person immutablePerson = (Person) ImmutableBean.create(person);
System.out.println(immutablePerson.getName());
// 通过修改底层对象来改变包装类的对象
person.setName("白星公主");
System.out.println(immutablePerson.getName());
// 此处修改了包装类的对象,会抛出异常
immutablePerson.setName("蕾贝卡");
System.out.println(immutablePerson.getName());
}
@Data
private static class Person {
private String name;
}
}
// 结果如下所示:
波雅汉考克
白星公主
Exception in thread "main" java.lang.IllegalStateException: Bean is immutable
2.5 BeanGenerator
BeanGenerator
即 Bean 生成器,它能够在运行时动态的创建一个JavaBean。可以直接设置父类,生成的 JavaBean 就是父类类型的实例。示例如下所示:
public class BeanGeneratorDemo {
public static void main(String[] args) throws Exception {
BeanGenerator beanGenerator = new BeanGenerator();
// 添加 JavaBean 的属性及其类型
beanGenerator.addProperty("name", String.class);
Object target = beanGenerator.create();
Method setter = target.getClass().getDeclaredMethod("setName", String.class);
Method getter = target.getClass().getDeclaredMethod("getName");
// 设置属性的值
setter.invoke(target, "千鹤");
System.out.println(getter.invoke(target));
}
}
// 结果如下所示:
千鹤
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
2.6 BeanMap
BeanMap
类实现了 JDK 的 java.util.Map
接口,它可以将一个 JavaBean 对象中的所有属性转换为一个 <String, Object>
的Map实例。示例如下所示:
public class BeanMapDemo {
public static void main(String[] args) throws Exception {
Person person = new Person();
person.setName("Nami");
BeanMap beanMap = BeanMap.create(person);
System.out.println(beanMap);
System.out.println(beanMap.get("name"));
}
@Data
private static class Person {
private String name;
}
}
// 结果如下所示:
{name=Nami}
Nami
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
2.7 Mixin
Mixin
能够将多个接口的多个实现合并到同一个接口的单个实现中。示例如下所示:
public class MixinDemo {
interface InterfaceFirst {
String first();
}
interface InterfaceSecond {
String second();
}
static class ImplFirst implements InterfaceFirst {
@Override
public String first() {
return "First one";
}
}
static class ImplSecond implements InterfaceSecond {
@Override
public String second() {
return "Second one";
}
}
interface MixinImpl extends InterfaceFirst, InterfaceSecond {
}
public static void main(String[] args) throws Exception {
Mixin mixin = Mixin.create(
new Class[]{InterfaceFirst.class, InterfaceSecond.class, MixinImpl.class}, // 接口数组
new Object[]{new ImplFirst(), new ImplSecond()} // 代理对象数组
);
MixinImpl mixinImpl = (MixinImpl) mixin;
System.out.println(mixinImpl.first());
System.out.println(mixinImpl.second());
}
}
// 结果如下所示:
First one
Second one
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
2.8 FastClass
FastClass
就是对 Class
对象进行特定的处理,可以理解为索引类,比如通过数组保存 method
引用,因此 FastClass
引出了一个 index 下标的新概念。通过数组存储 method
,constructor
等 class
信息,从而将原先的反射调用,转化为 class.index
的直接调用以提高效率,从而体现所谓的 FastClass
。此外, 在接口或者代理类的方法比较少的时候,使用 FastClass
进行方法调用有可能比原生反射方法调用 Method#invoke()
的效率高。示例如下所示:
public class FastClassDemo {
public static void main(String[] args) throws Exception {
FastClass fastClass = FastClass.create(CglibSampleClass.class);
FastMethod fastMethod = fastClass.getMethod("show", new Class[]{String.class});
CglibSampleClass cglibSampleClass = new CglibSampleClass();
// 使用 FastMethod 进行调用
System.out.println(fastMethod.invoke(cglibSampleClass, new Object[]{"Robin"}));
// 获得方法的下标索引 index
System.out.println(fastMethod.getIndex());
}
}
// 结果如下所示:
Robin show love to you!
0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
三、扩展
3.1 LazyLoader 实现延迟加载
public class LazyLoaderExt {
// 计数器
private static final AtomicInteger COUNTER = new AtomicInteger(0);
public static class PictureAlbum {
private String topic;
private List<PictureContent> pictureContentList;
public PictureAlbum() {
this.topic = "海贼王图片集";
this.pictureContentList = getPictureContentList();
}
private List<PictureContent> getPictureContentList() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(List.class);
enhancer.setCallback(new LazyLoader() {
@Override
public Object loadObject() throws Exception {
List<PictureContent> list = new ArrayList<>();
list.add(new PictureContent("Nami"));
list.add(new PictureContent("Lufei"));
list.add(new PictureContent("波雅汉考克"));
COUNTER.getAndIncrement();
return list;
}
});
return (List<PictureContent>) enhancer.create();
}
}
// 图片实体
public static class PictureContent {
private String name;
public PictureContent(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
// 实例化 PictureAlbum
PictureAlbum pictureAlbum = new PictureAlbum();
System.out.println(pictureAlbum.topic);
System.out.println("COUNTER ==> " + COUNTER.get());
System.out.println("=====图片名=====");
for (PictureContent pictureContent : pictureAlbum.pictureContentList) {
System.out.println(pictureContent.name);
}
System.out.println("COUNTER ==> " + COUNTER.get());
}
}
// 结果如下所示:
海贼王图片集
COUNTER ==> 0
=====图片名=====
Nami
Lufei
波雅汉考克
COUNTER ==> 1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
从计数器的输出结果可以看到:即使实例化了 PictureAlbum
,pictureContentList
的赋值只有在调用它的时候,才会通过 LazyLoader#loadObject
方法去赋值。
3.2 Dispathcer 扩展类的接口
该示例中有 UserService
类、IMethodInfo
接口以及该接口的实现类 DefaultMethodInfo
,在这里,我们通过 CGLIB 创建一个代理类,该代理类的父类是 UserService
,且实现了 IMethodInfo
接口,将接口 IMethodInfo
中所有的方法都转发给 DefaultMethodInfo
处理,代理类中的其他方法转发给父类 UserService
处理。
简而言之,就是对 UserService
进行了增强,使其具有 IMethodInfo
接口中的功能。
public class DispatcherExt {
public static class UserService {
public void add() {
System.out.println("新增用户");
}
public void update() {
System.out.println("更新用户信息");
}
}
// 用来获取方法信息的接口
public interface IMethodInfo {
// 获取方法数量
int methodCount();
// 获取被代理的对象中方法名称列表
List<String> methodNames();
}
// IMethodInfo 的默认实现
public static class DefaultMethodInfo implements IMethodInfo {
private Class<?> targetClass;
public DefaultMethodInfo(Class<?> targetClass) {
this.targetClass = targetClass;
}
@Override
public int methodCount() {
return targetClass.getDeclaredMethods().length;
}
@Override
public List<String> methodNames() {
return Arrays.stream(targetClass.getDeclaredMethods()).
map(Method::getName).
collect(Collectors.toList());
}
}
public static void main(String[] args) {
Class<?> targetClass = UserService.class;
Enhancer enhancer = new Enhancer();
// 设置代理的父类
enhancer.setSuperclass(targetClass);
// 设置代理需要实现的接口列表
enhancer.setInterfaces(new Class[]{IMethodInfo.class});
// 创建一个方法统计器,即 IMethodInfo 的默认实现类
IMethodInfo methodInfo = new DefaultMethodInfo(targetClass);
// 创建回调用器列表
Callback[] callbacks = {
// 处理 UserService 中所有的方法
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o, objects);
}
},
// 处理 IMethodInfo 接口中的方法
new Dispatcher() {
@Override
public Object loadObject() throws Exception {
/**
* 用来处理代理对象中 IMethodInfo 接口中的所有方法
* 所以此处返回的为 IMethodInfo 类型的对象,
* 将由这个对象来处理代理对象中 IMethodInfo 接口中的所有方法
*/
return methodInfo;
}
}
};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
// 当方法在 IMethodInfo 中定义的时候,返回 callbacks 中的第二个元素
return method.getDeclaringClass() == IMethodInfo.class ? 1 : 0;
}
});
Object proxy = enhancer.create();
//代理的父类是UserService
UserService userService = (UserService) proxy;
userService.add();
//代理实现了IMethodInfo接口
IMethodInfo mf = (IMethodInfo) proxy;
System.out.println(mf.methodCount());
System.out.println(mf.methodNames());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100