本文参考自http://www.cnblogs.com/liuyun1995/p/8144628.html
不知道可以先回顾一下这么使用JDK的动态代理用法
https://blog.csdn.net/qiuwenjie123/article/details/79330255
Proxy类的静态方法newProxyInstance方法去生成一个代理类,这个静态方法接收三个参数,分别是目标类的类加载器,目标类实现的接口集合,InvocationHandler实例,最后返回一个Object类型的代理类。我们先从该方法开始,看看代理类是怎样一步一步造出来的
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException {
//验证传入的InvocationHandler不能为空
Objects.requireNonNull(h);
//复制代理类实现的所有接口
final Class<?>[] intfs = interfaces.clone();
//获取安全管理器
final SecurityManager sm = System.getSecurityManager();
//进行一些权限检验
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
//该方法先从缓存获取代理类, 如果没有再去生成一个代理类
Class<?> cl = getProxyClass0(loader, intfs);
try {
//进行一些权限检验
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取参数类型是InvocationHandler.class的代理类构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//如果代理类是不可访问的, 就使用特权将它的构造器设置为可访问
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//传入InvocationHandler实例去构造一个代理类的实例
//所有代理类都继承自Proxy, 因此这里会调用Proxy的构造器将InvocationHandler引用传入
return cons.newInstance(new Object[]{h});
} catch (Exception e) {
//为了节省篇幅, 笔者统一用Exception捕获了所有异常
throw new InternalError(e.toString(), e);
}
}
可以看到,newProxyInstance方法首先是对参数进行一些权限校验,之后通过调用getProxyClass0方法生成了代理类的类对象,然后获取参数类型是InvocationHandler.class的代理类构造器。检验构造器是否可以访问,最后传入InvocationHandler实例的引用去构造出一个代理类实例,InvocationHandler实例的引用其实是Proxy持有着,因为生成的代理类默认继承自Proxy,所以最后会调用Proxy的构造器将引用传入。在这里我们重点关注getProxyClass0这个方法,看看代理类的Class对象是怎样来的,下面贴上该方法的代码
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//目标类实现的接口不能大于65535
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//获取代理类使用了缓存机制
return proxyClassCache.get(loader, interfaces);
}
先看看proxyClassCache这个对象在Proxy类里的对象,可以看出他是Proxy类的一个成员
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
可以看到getProxyClass0方法内部没有多少内容,首先是检查目标代理类实现的接口不能大于65535这个数,之后是通过类加载器和接口集合去缓存里面获取,如果能找到代理类就直接返回,否则就会调用ProxyClassFactory这个工厂去生成一个代理类。
首先我们先看看这个工厂类是怎样生成代理类的。
//代理类生成工厂
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
//代理类名称前缀
private static final String proxyClassNamePrefix = "$Proxy";
//用原子类来生成代理类的序号, 以此来确定唯一的代理类
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
//这里遍历interfaces数组进行验证, 主要做三件事情
//1.intf是否可以由指定的类加载进行加载
//2.intf是否是一个接口
//3.intf在数组中是否有重复
}
//生成代理类的包名
String proxyPkg = null;
//生成代理类的访问标志, 默认是public final的
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
for (Class<?> intf : interfaces) {
//获取接口的访问标志
int flags = intf.getModifiers();
//如果接口的访问标志不是public, 那么生成代理类的包名和接口包名相同
if (!Modifier.isPublic(flags)) {
//生成的代理类的访问标志设置为final
accessFlags = Modifier.FINAL;
//获取接口全限定名, 例如:java.util.Collection
String name = intf.getName();
int n = name.lastIndexOf('.');
//剪裁后得到包名:java.util
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
//生成的代理类的包名和接口包名是一样的
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
//代理类如果实现不同包的接口, 并且接口都不是public的, 那么就会在这里报错
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
//如果接口访问标志都是public的话, 那生成的代理类都放到默认的包下:com.sun.proxy
if (proxyPkg == null) {
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
//生成代理类的序号
long num = nextUniqueNumber.getAndIncrement();
//生成代理类的全限定名, 包名+前缀+序号, 例如:com.sun.proxy.$Proxy0
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//这里是核心, 用ProxyGenerator来生成字节码, 该类放在sun.misc包下
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,
interfaces, accessFlags);
try {
//根据二进制文件生成相应的Class实例
return defineClass0(loader, proxyName, proxyClassFile,
0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
该工厂的apply方法会被调用用来生成代理类的Class对象,由于代码的注释比较详细,我们只挑关键点进行阐述,其他的就不反复赘述了。
1. 在代码中可以看到JDK生成的代理类的类名是“$Proxy”+序号。
2. 如果接口是public的,代理类默认是public final的,并且生成的代理类默认放到com.sun.proxy这个包下。
3. 如果接口是非public的,那么代理类也是非public的,并且生成的代理类会放在对应接口所在的包下。
4. 如果接口是非public的,并且这些接口不在同一个包下,那么就会报错。
生成具体的字节码是调用了ProxyGenerator这个类的generateProxyClass方法。这个类放在sun.misc包下,后续我们会扒出这个类继续深究其底层源码。到这里我们已经分析了Proxy这个类是怎样生成代理类对象的,通过源码我们更直观的了解了整个的执行过程,包括代理类的类名是怎样生成的,代理类的访问标志是怎样确定的,生成的代理类会放到哪个包下面,以及InvocationHandler实例的引用是怎样传入的。
这时先回到刚才讲过的proxyClassCache对象,他是由WeakCache构造的,传入了KeyFactory()和ProxyClassFactory()这两个对象,而这两个对象都实现了BiFunction接口(伪函数指针),这个接口很简单,只有一个apply()方法,意思是传入两个参数,得到一个结果,运算过程由实现类决定。
public interface BiFunction<T, U, R> {
R apply(T t, U u); //本文关注这个接口就行了
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
所以上文ProxyClassFactory类的主要功能就是通过传入类加载器(ClassLoader)和接口数组(Class<?>[] interfaces)生成代理类实例并返回,不过可能还会有疑问,WeakCache缓存是怎样实现的?为什么proxyClassCache.get(loader, interfaces)最后会调用到ProxyClassFactory工厂的apply方法?
我们看到了Proxy内部用到了缓存机制,如果根据提供的类加载器和接口数组能在缓存中找到代理类就直接返回该代理类,否则会调用ProxyClassFactory工厂去生成代理类。这里用到的缓存是二级缓存,它的一级缓存key是根据类加载器生成的,二级缓存key是根据接口数组生成的。具体的内部机制我们直接贴上代码详细解释。
//Reference引用队列
private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
//缓存的底层实现, key为一级缓存, value为二级缓存。 为了支持null, map的key类型设置为Object
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>>
map = new ConcurrentHashMap<>();
//reverseMap记录了所有代理类生成器是否可用, 这是为了实现缓存的过期机制
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
//生成二级缓存key的工厂, 这里传入的是KeyFactory
private final BiFunction<K, P, ?> subKeyFactory;
//生成二级缓存value的工厂, 这里传入的是ProxyClassFactory
private final BiFunction<K, P, V> valueFactory;
//构造器, 传入生成二级缓存key的工厂和生成二级缓存value的工厂
public WeakCache(BiFunction<K, P, ?> subKeyFactory, BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
(这里小提一下Objects类在本文的作用基本就是判断对象是否为null,不是就返回他本身,否则抛异常)
首先我们看一下WeakCache的成员变量和构造器,WeakCache缓存的内部实现是通过ConcurrentMap来完成的,成员变量map就是二级缓存的底层实现,reverseMap是为了实现缓存的过期机制,subKeyFactory是二级缓存key的生成工厂,通过构造器传入,这里传入的值是Proxy类的KeyFactory,valueFactory是二级缓存value的生成工厂,通过构造器传入,这里传入的是Proxy类的ProxyClassFactory。接下来我们在看之前了解一下ConcurrentMap的putIfAbsent方法:
这个方法是这样的,当map中:
如果不存在(新的entry),那么会向map中添加该键值对,并返回null。
如果已经存在,那么不会覆盖已有的值,直接返回已经存在的值。
WeakCache的get方法
public V get(K key, P parameter) {
//这里要求实现的接口不能为空
Objects.requireNonNull(parameter);
//清除过期的缓存
expungeStaleEntries();
//将ClassLoader包装成CacheKey, 作为一级缓存的key
Object cacheKey = CacheKey.valueOf(key, refQueue);
//获取得到二级缓存
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
//如果根据ClassLoader没有获取到对应的值
if (valuesMap == null) {
//以CAS方式放入, 如果不存在则放入,否则返回原先的值
ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
//如果oldValuesMap有值, 说明放入失败
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//根据代理类实现的接口数组来生成二级缓存key, 分为key0, key1, key2, keyx
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//这里通过subKey获取到二级缓存的值
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
//这个循环提供了轮询机制, 如果条件为假就继续重试直到条件为真为止
while (true) {
//如果通过subKey取出来的值不为空
if (supplier != null) {
//在这里supplier可能是一个Factory也可能会是一个CacheValue
//在这里不作判断, 而是在Supplier实现类的get方法里面进行验证
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
//新建一个Factory实例作为subKey对应的值
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
//到这里表明subKey没有对应的值, 就将factory作为subKey的值放入
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
//到这里表明成功将factory放入缓存
supplier = factory;
}
//否则, 可能期间有其他线程修改了值, 那么就不再继续给subKey赋值, 而是取出来直接用
} else {
//期间可能其他线程修改了值, 那么就将原先的值替换
if (valuesMap.replace(subKey, supplier, factory)) {
//成功将factory替换成新的值
supplier = factory;
} else {
//替换失败, 继续使用原先的值
supplier = valuesMap.get(subKey);
}
}
}
}
WeakCache的get方法并没有用锁进行同步,那它是怎样实现线程安全的呢?因为它的所有会进行修改的成员变量都使用了ConcurrentMap,这个类是线程安全的。因此它将自身的线程安全委托给了ConcurrentMap, get方法尽可能的将同步代码块缩小,这样可以有效提高WeakCache的性能。我们看到ClassLoader作为了一级缓存的key,这样可以首先根据ClassLoader筛选一遍,因为不同ClassLoader加载的类是不同的。然后它用接口数组来生成二级缓存的key,这里它进行了一些优化,因为大部分类都是实现了一个或两个接口,所以二级缓存key分为key0,key1,key2,keyX。key0到key2分别表示实现了0到2个接口,keyX表示实现了3个或以上的接口,事实上大部分都只会用到key1和key2。这些key的生成工厂是在Proxy类中,通过WeakCache的构造器将key工厂传入。这里的二级缓存的值是一个Factory实例,最终代理类的值是通过Factory这个工厂来获得的。
Proxy类中的静态内部类KeyFactory,即上图中的subKeyFactory(Key简单的理解为接口数组,key0是一个Object)
private static final class KeyFactory implements BiFunction<ClassLoader, Class<?>[], Object> { @Override public Object apply(ClassLoader classLoader, Class<?>[] interfaces) { switch (interfaces.length) { case 1: return new Key1(interfaces[0]); // the most frequent case 2: return new Key2(interfaces[0], interfaces[1]); case 0: return key0; default: return new KeyX(interfaces); } } }
Factory工厂
private final class Factory implements Supplier<V> {
//一级缓存key, 根据ClassLoader生成
private final K key;
//代理类实现的接口数组
private final P parameter;
//二级缓存key, 根据接口数组生成
private final Object subKey;
//二级缓存
private final ConcurrentMap<Object, Supplier<V>> valuesMap;
Factory(K key, P parameter, Object subKey,
ConcurrentMap<Object, Supplier<V>> valuesMap) {
this.key = key;
this.parameter = parameter;
this.subKey = subKey;
this.valuesMap = valuesMap;
}
@Override
public synchronized V get() {
//这里再一次去二级缓存里面获取Supplier, 用来验证是否是Factory本身
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
//在这里验证supplier是否是Factory实例本身, 如果不则返回null让调用者继续轮询重试
//期间supplier可能替换成了CacheValue, 或者由于生成代理类失败被从二级缓存中移除了
return null;
}
V value = null;
try {
//委托valueFactory去生成代理类, 这里会通过传入的ProxyClassFactory去生成代理类
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
//如果生成代理类失败, 就将这个二级缓存删除
if (value == null) {
valuesMap.remove(subKey, this);
}
}
//只有value的值不为空才能到达这里
assert value != null;
//使用弱引用包装生成的代理类
CacheValue<V> cacheValue = new CacheValue<>(value);
//将包装后的cacheValue放入二级缓存中, 这个操作必须成功, 否则就报错
if (valuesMap.replace(subKey, this, cacheValue)) {
//将cacheValue成功放入二级缓存后, 再对它进行标记
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
//最后返回没有被弱引用包装的代理类
return value;
}
}
我们再看看Factory这个内部工厂类,可以看到它的get方法是使用synchronized关键字进行了同步。进行get方法后首先会去验证subKey对应的suppiler是否是工厂本身,如果不是就返回null,而WeakCache的get方法会继续进行重试。如果确实是工厂本身,那么就会委托ProxyClassFactory生成代理类,ProxyClassFactory是在构造WeakCache的时候传入的。所以这里解释了为什么最后会调用到Proxy的ProxyClassFactory这个内部工厂来生成代理类。生成代理类后使用弱引用进行包装并放入reverseMap中,最后会返回原装的代理类。
至于Factory的get方法中为什么要判断supplier是否是Factory实例,是因为在代码的后面可以发现这个工厂后来会被替换成CacheValue,至于为什么要用CacheValue,是因为要用到弱引用的原因,至于这个缓存的过期策略,就不在这里探讨了
这里小总结一下,其实动态代理类在这里用到的是两层的缓存结构,第一层以类加载器为Key,然后获得一个map类型的value,然后再以实现的接口数组(1个,两个,多个,这里是这么大划分的)为key,在map中找到代理类对象(class)。缓存没有的话就会去通过字节码构造一个。
最后深入ProxyGenerator这个类,来看看具体的代理类的字节码生成过程。
代理类是通过Proxy类的ProxyClassFactory工厂生成的,这个工厂类会去调用ProxyGenerator类的generateProxyClass()方法来生成代理类的字节码。ProxyGenerator这个类存放在sun.misc包下,我们可以通过OpenJDK源码来找到这个类,该类的generateProxyClass()静态方法的核心内容就是去调用generateClassFile()实例方法来生成Class文件。我们直接来看generateClassFile()这个方法内部做了些什么。
private byte[] generateClassFile() {
//第一步, 将所有的方法组装成ProxyMethod对象
//首先为代理类生成toString, hashCode, equals等代理方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//遍历每一个接口的每一个方法, 并且为其生成ProxyMethod对象
for (int i = 0; i < interfaces.length; i++) {
Method[] methods = interfaces[i].getMethods();
for (int j = 0; j < methods.length; j++) {
addProxyMethod(methods[j], interfaces[i]);
}
}
//对于具有相同签名的代理方法, 检验方法的返回值是否兼容
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
//第二步, 组装要生成的class文件的所有的字段信息和方法信息
try {
//添加构造器方法
methods.add(generateConstructor());
//遍历缓存中的代理方法
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
//添加代理类的静态字段, 例如:private static Method m1;
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
//添加代理类的代理方法
methods.add(pm.generateMethod());
}
}
//添加代理类的静态字段初始化方法
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//验证方法和字段集合不能大于65535
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
//第三步, 写入最终的class文件
//验证常量池中存在代理类的全限定名
cp.getClass(dotToSlash(className));
//验证常量池中存在代理类父类的全限定名, 父类名为:"java/lang/reflect/Proxy"
cp.getClass(superclassName);
//验证常量池存在代理类接口的全限定名
for (int i = 0; i < interfaces.length; i++) {
cp.getClass(dotToSlash(interfaces[i].getName()));
}
//接下来要开始写入文件了,设置常量池只读
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
try {
//1.写入魔数
dout.writeInt(0xCAFEBABE);
//2.写入次版本号
dout.writeShort(CLASSFILE_MINOR_VERSION);
//3.写入主版本号
dout.writeShort(CLASSFILE_MAJOR_VERSION);
//4.写入常量池
cp.write(dout);
//5.写入访问修饰符
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
//6.写入类索引
dout.writeShort(cp.getClass(dotToSlash(className)));
//7.写入父类索引, 生成的代理类都继承自Proxy
dout.writeShort(cp.getClass(superclassName));
//8.写入接口计数值
dout.writeShort(interfaces.length);
//9.写入接口集合
for (int i = 0; i < interfaces.length; i++) {
dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
}
//10.写入字段计数值
dout.writeShort(fields.size());
//11.写入字段集合
for (FieldInfo f : fields) {
f.write(dout);
}
//12.写入方法计数值
dout.writeShort(methods.size());
//13.写入方法集合
for (MethodInfo m : methods) {
m.write(dout);
}
//14.写入属性计数值, 代理类class文件没有属性所以为0
dout.writeShort(0);
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception");
}
//转换成二进制数组输出
return bout.toByteArray();
}
(太复杂,知道是通过反射机制和字节码两方面来生成的就行了)
可以看到,生成Class文件主要分为三步:
第一步:收集所有要生成的代理方法,将其包装成ProxyMethod对象并注册到Map集合中。
第二步:收集所有要为Class文件生成的字段信息和方法信息。
第三步:完成了上面的工作后,开始组装Class文件。
我们知道一个类的核心部分就是它的字段和方法。我们重点聚焦第二步,看看它为代理类生成了哪些字段和方法。在第二步中,按顺序做了下面四件事。
1.为代理类生成一个带参构造器,传入InvocationHandler实例的引用并调用父类的带参构造器。
2.遍历代理方法Map集合,为每个代理方法生成对应的Method类型静态域,并将其添加到fields集合中。
3.遍历代理方法Map集合,为每个代理方法生成对应的MethodInfo对象,并将其添加到methods集合中。
4.为代理类生成静态初始化方法,该静态初始化方法主要是将每个代理方法的引用赋值给对应的静态字段。
通过以上分析,我们可以大致知道JDK动态代理最终会为我们生成如下结构的代理类:
public class Proxy0 extends Proxy implements UserDao {
//这里我假设实现的接口是UserDao,当然可以是其他的接口,甚至多个接口
//第一步, 生成构造器
protected Proxy0(InvocationHandler h) {
super(h);
}
//第二步, 生成静态域
private static Method m1; //hashCode方法
private static Method m2; //equals方法
private static Method m3; //toString方法
private static Method m4; //...
//第三步, 生成代理方法
@Override
public int hashCode() {
try {
return (int) h.invoke(this, m1, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public boolean equals(Object obj) {
try {
Object[] args = new Object[] {obj};
return (boolean) h.invoke(this, m2, args);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public String toString() {
try {
return (String) h.invoke(this, m3, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void save(User user) {
try {
//构造参数数组, 如果有多个参数往后面添加就行了
Object[] args = new Object[] {user};
h.invoke(this, m4, args);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
//第四步, 生成静态初始化方法
static {
try {
Class c1 = Class.forName(Object.class.getName());
Class c2 = Class.forName(UserDao.class.getName());
m1 = c1.getMethod("hashCode", null);
m2 = c1.getMethod("equals", new Class[]{Object.class});
m3 = c1.getMethod("toString", null);
m4 = c2.getMethod("save", new Class[]{User.class});
//...
} catch (Exception e) {
e.printStackTrace();
}
}
}
至此,经过层层分析,深入探究JDK源码,我们还原了动态生成的代理类的本来面目,之前心中存在的一些疑问也随之得到了很好的解释
1.代理类默认继承Porxy类,因为Java中只支持单继承,所以JDK动态代理只能去实现接口。
2.代理方法都会去调用InvocationHandler的invoke()方法,因此我们需要重写InvocationHandler的invoke()方法。
3.调用invoke()方法时会传入代理实例本身,目标方法和目标方法参数。解释了invoke()方法的参数是怎样来的。